Skip to content

Commit 11c50c0

Browse files
committed
feat: New response codes
1 parent a2b6628 commit 11c50c0

4 files changed

Lines changed: 10 additions & 9 deletions

File tree

AGENTS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ AuthForge is a license key validation service. Your app sends a license key + ha
1111

1212
- **1 `login()` or `validate_license()` = 1 credit** (one `/auth/validate` debit each).
1313
- **10 heartbeats = 1 credit** (billed on every 10th successful heartbeat per license).
14-
- Any `heartbeat_interval` is safe — from `1` (server apps) to `900` (15 min, desktop apps). Revocations always take effect on the **next** heartbeat regardless of interval.
14+
- Keep `heartbeat_interval` at `>= 10` seconds (`900` / 15 min is the typical desktop default). `/auth/heartbeat` is limited to 6 requests/minute per license key, and revocations still take effect on the **next** heartbeat.
1515

1616
## Installation
1717

@@ -61,7 +61,7 @@ if __name__ == "__main__":
6161
| `app_id` | `str` | yes || Application ID |
6262
| `app_secret` | `str` | yes || Application secret |
6363
| `heartbeat_mode` | `str` | yes || `"SERVER"` or `"LOCAL"` (case-insensitive) |
64-
| `heartbeat_interval` | `int` | no | `900` | Seconds between heartbeats (any value ≥ 1) |
64+
| `heartbeat_interval` | `int` | no | `900` | Seconds between heartbeats (minimum `10`) |
6565
| `api_base_url` | `str` | no | `https://auth.authforge.cc` | API base URL |
6666
| `on_failure` | `Callable[[str, Optional[Exception]], None] \| None` | no | `None` | Called on login/heartbeat/network failure; if omitted, process exits via `os._exit(1)` (not used by `validate_license`) |
6767
| `request_timeout` | `int` | no | `15` | HTTP timeout (seconds) |
@@ -87,7 +87,7 @@ For Telegram/Discord bot flows, prefer immutable IDs (`tg:<user_id>`, `discord:<
8787
invalid_app, invalid_key, expired, revoked, hwid_mismatch, no_credits, blocked, rate_limited, replay_detected, session_expired, app_disabled, bad_request
8888

8989
Notes:
90-
- `rate_limited` and `replay_detected` can only be returned from `/auth/validate`. Heartbeats are not IP rate-limited and do not enforce nonce replay.
90+
- `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).
9191

9292
## Common patterns
9393

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ else:
4646
| `app_secret` | str | required | Your application secret from the AuthForge dashboard |
4747
| `public_key` | str | required | App Ed25519 public key (base64) from dashboard |
4848
| `heartbeat_mode` | str | required | `"SERVER"` or `"LOCAL"` (see below) |
49-
| `heartbeat_interval` | int | `900` | Seconds between heartbeat checks (any value ≥ 1; default 15 min) |
49+
| `heartbeat_interval` | int | `900` | Seconds between heartbeat checks (minimum `10`; default 15 min) |
5050
| `api_base_url` | str | `https://auth.authforge.cc` | API endpoint |
5151
| `on_failure` | callable | `None` | Callback `(reason: str, exc: Exception | None)` on auth failure |
5252
| `request_timeout` | int | `15` | HTTP request timeout in seconds |
@@ -70,7 +70,7 @@ client = AuthForgeClient(
7070
- **1 `login()` or `validate_license()` call = 1 credit** (one `/auth/validate` debit each).
7171
- **10 heartbeats on the same license = 1 credit** (billed every 10th successful heartbeat).
7272

73-
A desktop app running 6h/day at a 15-minute interval burns ~3–4 credits/day. A server app running 24/7 at a 1-minute interval burns ~145 credits/day — pick the interval based on how fast you need revocations to propagate (they always land on the **next** heartbeat).
73+
A desktop app running 6h/day at a 15-minute interval burns ~3–4 credits/day. The server enforces `/auth/heartbeat` at 6 requests/minute per license key, so keep intervals at 10 seconds or higher and pick the interval based on how fast you need revocations to propagate (they always land on the **next** heartbeat).
7474

7575
## Methods
7676

@@ -98,7 +98,7 @@ If authentication fails (login rejected, heartbeat fails, signature mismatch, et
9898
**`validate_license()`** does not trigger `on_failure` or `os._exit` — check `result["valid"]` and `result["code"]`.
9999

100100
Recognized server errors:
101-
`invalid_app`, `invalid_key`, `expired`, `revoked`, `hwid_mismatch`, `no_credits`, `blocked`, `rate_limited`, `replay_detected`, `app_disabled`, `session_expired`, `revoke_requires_session`, `bad_request`
101+
`invalid_app`, `invalid_key`, `expired`, `revoked`, `hwid_mismatch`, `no_credits`, `blocked`, `rate_limited`, `replay_detected`, `app_disabled`, `session_expired`, `revoke_requires_session`, `bad_request`, `malformed_request`, `system_error`
102102

103103
Request retries are automatic inside the internal HTTP layer:
104104
- `rate_limited`: retry after 2s, then 5s (max 3 attempts total)

authforge.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class ValidateLicenseFailure(TypedDict):
6363
"session_expired",
6464
"revoke_requires_session",
6565
"bad_request",
66+
"malformed_request",
6667
"system_error",
6768
}
6869

@@ -93,8 +94,8 @@ def __init__(
9394
mode = (heartbeat_mode or "").upper()
9495
if mode not in {"LOCAL", "SERVER"}:
9596
raise ValueError("heartbeat_mode must be LOCAL or SERVER")
96-
if heartbeat_interval <= 0:
97-
raise ValueError("heartbeat_interval must be > 0")
97+
if heartbeat_interval < 10:
98+
raise ValueError("heartbeat_interval must be >= 10")
9899

99100
self.app_id = app_id
100101
self.app_secret = app_secret

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "authforge-sdk"
7-
version = "1.0.7"
7+
version = "1.0.8"
88
description = "Official Python SDK for AuthForge — credit-based license key authentication with Ed25519-verified responses."
99
readme = "README.md"
1010
requires-python = ">=3.9"

0 commit comments

Comments
 (0)