Skip to main content

Keys and licensing

Obfy has two independent mechanisms that are easy to confuse:

  • The build key — the AES-256-GCM key that encrypts your code. Required to run a protected dist.
  • The license file — optional terms (expiry, machine binding) baked into the dist and enforced by the runtime at import time.

Per-developer seat licensing (who may run obfy build) is separate again — see Seats and devices.

The build key

Each build uses one AES-256-GCM key. By default it's written to <out>/code.key and read from there at run time.

obfy build --src ./app --out ./dist --key ./secrets/build.key
  • Never commit code.key. Obfy's defaults keep it out of your source tree; keep it out of version control on your side too.

  • To keep the key out of the shipped dist entirely, delete code.key after building and provide it out-of-band at run time via the OBFY_KEY environment variable (hex-encoded):

    OBFY_KEY=<hex-key> python ./dist/run.py

The license file

Pass --license with a JSON file to bind a build to specific conditions. The terms are encrypted into the dist; the verdict runs in the native runtime and is enforced by the loader at import time, with no network calls — everything is measured locally.

obfy build --src ./app --out ./dist --license ./license.json
{
"expires": "2026-12-31",
"machines": ["00:11:22:33:44:55"],
"hostnames": ["build-server-01"],
"disks": ["WD-1234567890"],
"anti_rollback": true
}
FieldMeaning
expiresDate (YYYY-MM-DD) after which the dist refuses to run.
machinesAllowed MAC addresses.
hostnamesAllowed hostnames.
disksAllowed disk serial numbers.
anti_rollbackSeals a clock-rollback floor so the expiry can't be bypassed by setting the system clock back.

Every binding field you include must match for the dist to run. Omit a field to leave that dimension unconstrained.

Hardening flags

FlagEffect
--anti-debugThe loader refuses to decrypt under an attached tracer (Linux TracerPid, macOS P_TRACED). Off by default so normal builds still run under your own pdb/coverage.
--anti-tamperSeals the plaintext loader and manifest.json under the build key; the runtime refuses to decrypt unless the on-disk copies still match.

Both are deterrence, not prevention — a patched native runtime can still defeat them.