Heartbeat Protocol
Forge performs a weekly license health check (heartbeat) to verify that your license is still active — not revoked due to a refund, payment failure, or subscription cancellation. This page documents exactly what is sent, when, and how to inspect or control the behavior.
Air-gapped binaries (compiled with the air_gapped feature) never perform any heartbeat. All network code is compiled out entirely in those builds.
What Is Transmitted
Section titled “What Is Transmitted”The heartbeat sends a single JSON object via HTTP POST:
{ "license_hash": "a3f1b2c4d5e6...", "client_version": "1.3.0", "platform": "linux-x86_64", "team_id": null}| Field | Value | Notes |
|---|---|---|
license_hash | SHA-256 hex of the raw license key string | The license key itself is never sent — only its hash. This is how the server identifies the license without seeing the plaintext. |
client_version | Forge version string (e.g., "1.3.0") | Used by the server for telemetry on version adoption. |
platform | "linux-x86_64", "macos-aarch64", "windows-x86_64", etc. | Operating system and CPU architecture. |
team_id | Team subscription ID, or null | Included for Team licenses so the admin portal can show aggregate team activity (“4 of 5 seats active this week”). null for Solo and Pro. |
What is never transmitted: source code, file paths, queries, repository names, symbol names, email addresses, or any personal data. Privacy policy: https://forge.ironpinelabs.com/privacy#heartbeat
Endpoint
Section titled “Endpoint”POST https://forge-license-webhook.ironpinelabs.workers.dev/heartbeatThe endpoint is a Cloudflare Worker. It logs the hash, version, and platform in a D1 database for support and analytics. It does not store your source code or queries. The endpoint can be overridden for testing via the FORGE_HEARTBEAT_URL environment variable (internal use only).
Frequency and Scheduling
Section titled “Frequency and Scheduling”The heartbeat fires from the background task spawned by forge serve:
- Base interval: 7 days
- Jitter: up to ±12 hours (uniform random, applied at each scheduling point)
- First run: immediately on
forge servestartup if no heartbeat has been attempted yet, or if the next scheduled attempt is in the past
The jitter prevents all instances updating simultaneously (“thundering herd”). The exact next attempt time is stored in ~/.forge/heartbeat.json.
Cache Semantics
Section titled “Cache Semantics”The server responds with a cached_until timestamp, typically last_heartbeat_at + 14 days. Forge uses this to avoid unnecessary network calls:
| Cache age | Forge behavior |
|---|---|
Within cached_until | Use cached result; do not attempt a new heartbeat |
cached_until elapsed but < 30 days since last heartbeat | Attempt a new heartbeat on next forge serve startup |
| > 30 days since last heartbeat | Warn on startup: “License check overdue — please ensure network access” |
| > 60 days since last heartbeat | Degrade to Community Mode regardless of cached license status |
The 60-day grace window ensures legitimate customers are never permanently locked out by a prolonged network outage.
What Triggers Degradation
Section titled “What Triggers Degradation”The heartbeat response includes a status field:
| Status | Forge behavior |
|---|---|
active | Continue normally |
revoked | Degrade to Community Mode on next forge serve startup; show revocation message |
expired | Degrade to Community Mode; show renewal prompt |
unknown | Log a warning; continue with cached license (the server may not have the hash if the key was issued before v1.3.0) |
Degradation is never immediate — it takes effect at the next forge serve startup after the heartbeat response is received and cached.
Transparency Commands
Section titled “Transparency Commands”You can inspect and control the heartbeat at any time:
View current heartbeat status
Section titled “View current heartbeat status”forge config heartbeat showPrints:
- Last heartbeat timestamp
- Cached result (status +
cached_until) - Next scheduled attempt
- Exact payload that would be transmitted
- Link to the privacy policy
Nothing is sent by running this command.
Force an immediate heartbeat
Section titled “Force an immediate heartbeat”forge config heartbeat nowBypasses the weekly schedule and sends a heartbeat immediately. Use this after re-subscribing or after a network outage to restore the cache. Exits non-zero if the heartbeat fails.
Heartbeat State File
Section titled “Heartbeat State File”Heartbeat state is persisted to ~/.forge/heartbeat.json:
{ "last_heartbeat_at": "2026-04-15T10:00:00Z", "last_status": "active", "cached_until": "2026-04-29T10:00:00Z", "last_error": null, "next_attempt_at": "2026-04-22T09:17:00Z"}| Field | Description |
|---|---|
last_heartbeat_at | UTC timestamp of the last successful heartbeat. |
last_status | Status returned by the last successful heartbeat: active, revoked, expired, or unknown. |
cached_until | The server’s suggested cache expiry. |
last_error | Error message from the most recent failed attempt, or null. |
next_attempt_at | Jittered timestamp of the next scheduled attempt. |
All fields are optional. If the file is missing or corrupt, Forge resets to a clean default state (which triggers an immediate heartbeat attempt on the next forge serve startup).
Licenses Activated Before v1.3.0
Section titled “Licenses Activated Before v1.3.0”If your ~/.forge/license.json has key_hash: "<unknown>" (activated before Forge v1.3.0), the heartbeat is skipped until you re-activate:
forge activate <your-license-key>Re-activation writes the correct key_hash and enables heartbeat going forward. Your license remains valid in the meantime; this only affects the server-side activity tracking.
Air-Gapped Builds
Section titled “Air-Gapped Builds”Air-gapped binaries are compiled with --features air_gapped. In these builds:
- The
reqwestHTTP client is never called (though it remains linked in the binary in v1.3.0; true network-code removal is planned for v1.4.0) forge config heartbeat showdisplays an air-gapped mode notice instead of the standard payload displayforge config heartbeat nowis a no-op- License validation is performed entirely against the embedded Ed25519 signature and the compile-time expiration year
No configuration is needed for air-gapped builds; heartbeat is disabled at compile time.