All posts
Patrick Kelly licensing ci developer-experience

How Canopy Handles Licensing Without Getting in Your Way

Five-year signed keys, automatic CI detection, portal self-service when a laptop dies, and 90-day auto-eviction. Here is how Canopy thinks about licensing friction.


Developer tools that force you to re-enter your license key every month are annoying. Here is how Canopy handles it differently.


I have had the experience of sitting down to work, running a tool I pay for, and getting a wall of licensing error text because a key expired or a seat count was wrong. Every minute spent on that is a minute not spent on the actual work. When I built Canopy’s licensing system, I spent as much time thinking about how not to interrupt developers as I did thinking about how to protect the software.

Here is what that looks like in practice.


Keys That Last Five Years

When you activate Canopy on a Solo, Pro, or Team tier, the license server issues you an Ed25519-signed key. That key is valid for five years.

The key embeds your tier, your seat count, and an expiry timestamp. The Canopy binary validates the cryptographic signature locally on every invocation — no network call required to verify the key itself. The heartbeat that Canopy sends periodically is about seat enforcement and feature authorization, not about keeping a key alive.

Five years is long enough that key expiry is genuinely not something you will think about. When a key does expire, Canopy degrades gracefully to community mode rather than hard-failing — you see a message and a prompt to renew, not an error that breaks your workflow.

Air-gapped installs use 1-year keys because offline environments benefit from periodic security key rotation. That is a deliberate trade-off, not an oversight.


Heartbeats Are the Source of Truth

Every running Canopy instance sends a periodic heartbeat to the license server. That heartbeat is the mechanism for seat enforcement — the server tracks which machine fingerprints are actively using a given license.

The key insight here is that the heartbeat is the source of truth, not the key. Your signed key is a credential that proves you are authorized. The server-side state tracks which machines are currently using that credential. This separation is what makes the recovery flows work cleanly when a machine dies.

The heartbeat payload contains a SHA-256 hash of your machine’s hardware identifiers (machine ID, hostname, primary network interface MAC address). Raw hardware values never leave your machine — only the hash is transmitted. The server stores only the hash.


Ephemeral Mode: CI and Containers Work Without Burning Seats

The first version of Canopy’s activation flow had a problem with CI. Every GitHub Actions runner boots a fresh VM. That VM produces a fresh machine fingerprint. Activate Canopy in CI and you burn a seat on each run, which would exhaust a Solo tier license in an afternoon.

The fix is ephemeral mode. When Canopy runs in a CI environment — GitHub Actions, GitLab CI, CircleCI, Buildkite, Docker containers, GitHub Codespaces, Gitpod, or Devcontainers — it detects this automatically and skips fingerprint registration. The license is validated and the session is authorized, but no binding is recorded and no seat is consumed.

Detection is automatic. Canopy checks environment variables and filesystem signals (like the presence of /.dockerenv or CODESPACES=true) in priority order. You do not need to configure anything for standard CI environments.

If you want explicit control, CANOPY_EPHEMERAL=1 forces ephemeral mode on any machine. CANOPY_EPHEMERAL=0 disables detection if your bare-metal setup triggers a false positive.

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


When a Laptop Dies: Portal Self-Service

The most common licensing friction I heard from developers before v1.5.0 was the dead laptop problem. Your machine dies. Your binding is still registered against it. You cannot run canopy deactivate on a machine that does not exist. You are blocked from activating on your new machine until you contact support.

Canopy now has a portal at https://canopy.ironpinelabs.com/portal/login. Sign in with the email address on your license — no password, just a 15-minute single-use link sent to your inbox. You see a table of every machine currently bound to your license, showing platform, Canopy version, and the last heartbeat timestamp. Find the dead machine, click Revoke, confirm. The seat is free immediately.

You do not need to re-enter your license key. Your key is unchanged. You are just removing a server-side binding entry. Activate on your new machine with the same key you have always used.


90-Day Auto-Eviction as a Safety Net

The portal covers the case where you know your machine is dead and want to act immediately. The auto-eviction feature covers the case where you forgot — where a machine has just been sitting in a drawer for three months and is no longer something you think about.

When you run canopy activate on a new machine and your seat limit is full, Canopy now checks whether any current binding has been silent for 90 days or more. If it finds one, it presents an interactive prompt:

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]:

Type y and activation completes. The old binding is removed, your new machine is registered, and a fresh key is written to disk. You receive an email notification with a 7-day window to contact support and reverse the eviction if something unexpected happened.

The eviction rate is capped at 2 per license per 30 days to prevent a compromised key from cycling through machines.


This Is How I Would Want My Own Tools to Work

When I think about what makes licensing not painful, the answer is basically: do not ask me to do maintenance work. Do not expire my key every year. Do not require a support ticket when my laptop breaks. Do not charge my CI builds against my seat count. Handle the obvious cases automatically and give me a self-service path for the rest.

That is what this system does. Five-year keys with cryptographic validation eliminate the renewal cycle. Ephemeral detection eliminates the CI seat problem. The portal eliminates the support ticket for a dead machine. Auto-eviction eliminates the forgotten binding.

The only thing left to do when you get a new machine is run canopy activate and get back to work.


Canopy v1.5.0 is available now. If you are already on a paid tier, update with canopy self-update — no action needed on your license. If you have been holding off on the CI integration because of seat concerns, ephemeral mode removes that blocker.

Questions at [email protected] or GitHub Discussions.