Skip to content

Machine Fingerprints and Bindings

Forge licenses are not floating. Each activation binds the license to a specific machine using a hardware fingerprint. This page explains why, what gets hashed, how the limits work per tier, and how stale bindings get cleaned up over time.

License keys are strings. A string can be copied and shared. Machine bindings make a license key useful on a limited number of machines, matching the seat count you purchased.

The binding model is server-authoritative: the license server tracks which fingerprints are active for each license. The signed key you store locally is a credential; the server decides whether that credential is accepted on the current machine.

When you run forge activate, the binary collects three values from the current machine:

InputSource
Machine ID/etc/machine-id (Linux), IOPlatformUUID (macOS), Registry MachineGuid (Windows)
Hostnamegethostname()
Primary network interface MAC addressFirst non-loopback adapter

All three are combined and passed through SHA-256. The resulting 64-character hex string is the fingerprint. We never store the raw inputs — only the hash reaches our servers.

The hash is deterministic for a given machine. If you reinstall the OS without wiping the machine ID, the fingerprint is unchanged and your activation survives. If the machine ID is regenerated (some cloud images do this on first boot), a new fingerprint is produced and the old binding becomes stale.

TierSeatsNotes
Community0No activation required. No MCP tools. CLI-only in limited mode.
Solo1One machine at a time.
Pro3Three machines simultaneously (laptop, desktop, home server, etc.).
TeamPer-memberEach team member gets one seat. Admin manages the roster.

“Seats” are concurrent bindings, not total activations. You can deactivate a machine and activate a new one as many times as you need — the seat limit is how many machines can be active at once.

CI runners, GitHub Codespaces, GitLab CI, Docker containers, and Devcontainers produce a fresh machine ID on every boot. Activating normally in these environments would consume a seat on each run and exhaust your binding slots within hours.

Forge handles this automatically: when it detects an ephemeral environment, it skips fingerprint registration entirely. The license is validated and the session is authorized, but no binding is recorded and no seat is consumed.

Ephemeral detection checks, in priority order:

  1. FORGE_EPHEMERAL=1 environment variable (explicit override — always wins)
  2. CODESPACES=true (GitHub Codespaces)
  3. GITPOD_WORKSPACE_ID set (Gitpod)
  4. CI=true with a known CI vendor variable (GITHUB_ACTIONS, GITLAB_CI, CIRCLECI, BUILDKITE, JENKINS_URL)
  5. /proc/1/cgroup contains docker, containerd, libpod, or kubepods
  6. /.dockerenv exists
  7. DEVCONTAINER=true or REMOTE_CONTAINERS=true

If you are running on a bare-metal machine that accidentally matches one of the file-based signals (some VMs leave docker artifacts in cgroups), set FORGE_EPHEMERAL=0 to force persistent mode.

Ephemeral sessions cache the validation result in /tmp/forge-session-cache.json for one hour, so a multi-step CI job does not hit the license server on every invocation.

See Use Forge in CI, Use Forge in Codespaces, and Use Forge in Docker for setup guides.

A machine that has not sent a heartbeat in 90 days is considered stale. When you run forge activate on a new machine and your seat limit is full, Forge checks whether any current binding is stale. If one is found, it offers to evict the inactive binding and take the seat in its place:

License seat limit reached for solo tier.
One of your bindings has been inactive:
Platform: macos-aarch64
Last heartbeat: 2025-11-18 (152 days ago)
Evict this inactive binding and activate on this machine? [y/N]:

Confirm with y and activation completes. The old binding is removed, the new machine is registered, and a re-issued license key is written to ~/.forge/license.json.

A few constraints on eviction:

  • Maximum 2 evictions per license per rolling 30-day window. This prevents a compromised key from cycling through machines. If you hit this limit, contact support@forge.ironpinelabs.com.
  • Eviction requires the old binding to be genuinely 90 days inactive. A machine that comes back online resets its staleness timer.
  • If the evicted machine comes back online, its next heartbeat returns an unknown_binding response. Forge degrades to community mode and prompts the user to re-activate. The re-activation will trigger another seat-limit check.

You receive an email notification when a binding is evicted, with a 7-day window to contact support and reverse it if you did not expect it.

If a machine dies before 90 days have passed, you can revoke its binding manually from the portal at https://forge.ironpinelabs.com/portal/login. Sign in with the email on your license, find the dead machine in the table, and revoke it. See Recover from a dead machine.

DataRetention
Fingerprint hashRetained while the binding is active. Removed when the binding is revoked or evicted.
Heartbeat timestampsRetained per binding for eviction eligibility. Removed with the binding.
Ephemeral usage recordsAggregated weekly; rows older than 13 months are purged.
Binding event audit log12 months.

Raw hardware identifiers (machine ID, hostname, MAC address) are never stored. Only the SHA-256 hash is transmitted and retained.

For the full data handling policy, see the Forge Privacy Policy.

The short version: fingerprint hashes are used only for seat enforcement. They are not correlated with your code, your repo contents, or any other license holder’s data. Ephemeral usage records track only counts, source tags (e.g., ci:github_actions), platform, and client version — never file paths or code.