Skip to content

Commit 22cffd7

Browse files
committed
Productize grace period; deprecate heartbeat mode
The grace period (running on the signed session until the TTL expires) is now the default and needs no configuration. Online check-ins are opt-in via authforge::OnlineHeartbeat::On. Legacy heartbeat mode values still work behind a deprecation shim: LOCAL maps to the default, SERVER maps to online check-ins. README and AGENTS rewritten with the new vocabulary and a migration section. Version bumped to 1.1.0.
1 parent 7234910 commit 22cffd7

5 files changed

Lines changed: 271 additions & 63 deletions

File tree

AGENTS.md

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# AuthForge SDK AI Agent Reference
1+
# AuthForge SDK: AI Agent Reference
22

33
> This file is optimized for AI coding agents (Cursor, Copilot, Claude Code, etc.).
44
> It contains everything needed to correctly integrate AuthForge licensing into a project.
55
66
## What AuthForge does
77

8-
AuthForge is a license key validation service. Your app sends a license key + hardware ID to the AuthForge API, gets back a cryptographically signed response, and runs background heartbeats to maintain the session. If the license is revoked or expired, the heartbeat fails and you handle it (typically exit the app).
8+
AuthForge is a license key validation service. Your app activates a license key online: it sends the key plus a hardware ID to `POST /auth/validate`, and the server checks revocation, expiry, HWID binding, and credits, then returns an Ed25519-signed session with a TTL. By default the app then runs through the grace period: it keeps running on that signed session without contacting AuthForge (the SDK re-verifies the signed session locally in the background) until the TTL expires. Optionally, you can enable online check-ins: periodic calls to `POST /auth/heartbeat` for fast revocation and concurrent-use detection. If the license is revoked or the session becomes invalid, the background check fails and you handle it (typically exit the app).
99

1010
## Installation
1111

@@ -20,10 +20,14 @@ Add `authforge_sdk.h` and `authforge_sdk.cpp` to your project, or consume the li
2020
#include <string>
2121

2222
int main() {
23+
// Default policy: activate online once, then run through the grace period
24+
// (no network until the session TTL expires). To enable online check-ins,
25+
// pass authforge::OnlineHeartbeat::On as the 4th argument.
2326
authforge::AuthForgeClient client(
2427
"YOUR_APP_ID",
2528
"YOUR_APP_SECRET",
26-
"SERVER",
29+
"YOUR_PUBLIC_KEY", // required: base64 Ed25519 key from the dashboard
30+
authforge::OnlineHeartbeat::Off,
2731
900,
2832
authforge::AuthForgeClient::kDefaultApiBaseUrl,
2933
[](const std::string &reason, const std::exception *exc) {
@@ -54,43 +58,56 @@ int main() {
5458

5559
| Parameter | Type | Required | Default | Description |
5660
|-----------|------|----------|---------|-------------|
57-
| `appId` | `std::string` | yes || Application ID |
58-
| `appSecret` | `std::string` | yes || Application secret |
59-
| `heartbeatMode` | `std::string` | yes || `"SERVER"` or `"LOCAL"` |
60-
| `heartbeatInterval` | `int` | no | `900` | Seconds between heartbeats (minimum `10`; revocations apply on the next heartbeat) |
61+
| `appId` | `std::string` | yes | none | Application ID |
62+
| `appSecret` | `std::string` | yes | none | Application secret |
63+
| `publicKey` | `std::string` / `std::vector<std::string>` | yes | none | Base64 Ed25519 public key from the dashboard (3rd positional arg). The string overload accepts a comma-separated trust list; a `std::vector<std::string>` overload takes a rotation set. The SDK trusts a signature matching **any** key |
64+
| `onlineHeartbeat` | `authforge::OnlineHeartbeat` | no | `OnlineHeartbeat::Off` | `Off` (default): after activation, run through the grace period on the signed session with no network calls. `On`: enable online check-ins via `/auth/heartbeat` for fast revocation and concurrent-use detection |
65+
| `heartbeatInterval` | `int` | no | `900` | Seconds between background checks (minimum `10`). With online check-ins enabled, revocations apply on the next check-in |
6166
| `apiBaseUrl` | `std::string` | no | `kDefaultApiBaseUrl` (`https://auth.authforge.cc`) | API base URL |
62-
| `onFailure` | `std::function<void(const std::string&, const std::exception*)>` | no | `nullptr` | Failure callback for `Login` / heartbeat; if null, `std::exit(1)` (not used by `ValidateLicense`) |
67+
| `onFailure` | `std::function<void(const std::string&, const std::exception*)>` | no | `nullptr` | Failure callback for `Login` / background checks; if null, `std::exit(1)` (not used by `ValidateLicense`) |
6368
| `requestTimeout` | `int` | no | `15` | HTTP timeout (seconds) |
64-
| `ttlSeconds` | `int` | no | `0` (server default: 86400) | Requested session token lifetime. `0` means "server default". Server clamps to `[3600, 604800]`; preserved across heartbeat refreshes. |
69+
| `ttlSeconds` | `int` | no | `0` (server default: 86400) | Requested grace period duration in seconds (the session token lifetime). `0` means "server default" (24h). Server clamps to `[3600, 604800]` (1h to 7d); preserved across heartbeat refreshes. |
6570
| `hwidOverride` | `std::string` | no | `""` | Optional custom HWID/subject string. When non-empty (for example `tg:123456789`), the SDK sends it instead of generating a machine fingerprint. |
6671

6772
For Telegram/Discord bot flows, prefer immutable IDs (`tg:<user_id>`, `discord:<user_id>`) instead of usernames.
6873

74+
## Migrating from heartbeatMode
75+
76+
Earlier versions took a `std::string heartbeatMode` (`"LOCAL"` or `"SERVER"`) as the 4th constructor parameter:
77+
78+
- `"LOCAL"` maps to the default (grace period behavior): drop the argument entirely.
79+
- `"SERVER"` maps to `authforge::OnlineHeartbeat::On`.
80+
81+
The old string-mode constructors still work and behave exactly as before, but they emit a deprecation warning at compile time. Never describe the grace period as a "LOCAL mode" or "offline mode"; it is the default behavior of every activated session.
82+
6983
## Billing model
7084

7185
- Each `Login()` or `ValidateLicense()` calls `/auth/validate` and costs **1 credit**.
72-
- Heartbeats cost **1 credit per 10 successful calls** (billed on every 10th heartbeat).
73-
- Keep heartbeat interval at or above 10 seconds. `/auth/heartbeat` is limited to 6 requests/minute per license key; cost still scales with how many heartbeats you send.
74-
- Revocations take effect on the **next** heartbeat regardless of interval.
86+
- Online check-ins cost **1 credit per 10 successful calls** (billed on every 10th heartbeat).
87+
- The default grace period policy makes no network calls after activation and costs nothing until the next activation.
88+
- Keep the check-in interval at or above 10 seconds. `/auth/heartbeat` is limited to 6 requests/minute per license key; cost still scales with how many check-ins you send.
89+
- Revocations take effect on the **next** check-in regardless of interval.
7590

7691
## Methods
7792

7893
| Method | Returns | Description |
7994
|--------|---------|-------------|
80-
| `Login(const std::string&)` | `bool` | Validates license and starts heartbeat |
81-
| `ValidateLicense(const std::string&)` | `ValidateLicenseResult` | Same validate + signatures; no session/heartbeat; **never** calls `onFailure` or `std::exit` |
82-
| `Logout()` | `void` | Stops heartbeat and clears state |
95+
| `Login(const std::string&)` | `bool` | Activates the license online and starts the background check loop |
96+
| `ValidateLicense(const std::string&)` | `ValidateLicenseResult` | Same validate + signatures; no session/background checks; **never** calls `onFailure` or `std::exit` |
97+
| `Logout()` | `void` | Stops background checks and clears state |
8398
| `IsAuthenticated()` | `bool` | Whether authenticated |
8499
| `GetSessionDataJson()` | `std::optional<std::string>` | Payload JSON string |
85100
| `GetAppVariablesJson()` | `std::optional<std::string>` | App variables JSON |
86101
| `GetLicenseVariablesJson()` | `std::optional<std::string>` | License variables JSON |
87102

88103
## Error codes the server can return
89104

90-
invalid_app, invalid_key, expired, revoked, hwid_mismatch, no_credits, blocked, rate_limited, replay_detected, session_expired, app_disabled, bad_request
105+
Full set: invalid_app, invalid_key, expired, revoked, hwid_mismatch, no_credits, app_burn_cap_reached, blocked, rate_limited, replay_detected, app_disabled, session_expired, revoke_requires_session, bad_request, malformed_request, system_error
91106

92107
Notes:
93108
- `replay_detected` is validate-only. `rate_limited` can be returned by `/auth/validate` and `/auth/heartbeat` (heartbeat is license-limited at 6/min and has no app-layer IP limit).
109+
- `app_burn_cap_reached` means the app's configured credit burn cap is hit; `revoke_requires_session` means a pre-session self-ban tried to revoke a license (only session-authenticated self-ban can revoke).
110+
- `session_expired` is what the default background check reports when the grace period ends; the app must activate online again.
94111

95112
## Common patterns
96113

@@ -116,7 +133,8 @@ Use the `onFailure` callback; distinguish `reason` (`login_failed`, `heartbeat_f
116133

117134
## Do NOT
118135

119-
- Do not hardcode the app secret as a plain string literal in source — use environment variables or encrypted config
120-
- Do not omit `onFailure` — without it, failures call `std::exit(1)` without your cleanup
121-
- Do not call `Login` on every app action — call once at startup; heartbeats handle the rest
122-
- Do not use `heartbeatMode` `"LOCAL"` unless the app has no internet after initial auth
136+
- Do not hardcode the app secret as a plain string literal in source; use environment variables or encrypted config
137+
- Do not omit `onFailure`; without it, failures call `std::exit(1)` without your cleanup
138+
- Do not call `Login` on every app action; call once at startup, the background checks handle the rest
139+
- Do not pass the deprecated `heartbeatMode` strings (`"LOCAL"` / `"SERVER"`) in new code; use the default for grace period behavior or `authforge::OnlineHeartbeat::On` for online check-ins
140+
- Do not enable online check-ins if the app loses internet access after initial activation; the default grace period behavior covers that case within the session TTL

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.16)
2-
project(AuthForgeCPPSDK VERSION 1.0.8 LANGUAGES CXX)
2+
project(AuthForgeCPPSDK VERSION 1.1.0 LANGUAGES CXX)
33

44
include(GNUInstallDirs)
55
include(CMakePackageConfigHelpers)

0 commit comments

Comments
 (0)