CLI & API reference.

The veil CLI and the GitHub Action are open source and are thin clients of one Protection API. The protection itself runs server-side, so the algorithm never ships in your binary. Everything the CLI does, you can do over HTTP.

Install & auth

terminal
# install (macOS / Linux via Homebrew, or Go)
brew install almata-ai/tap/veil
# or:  go install github.com/almata-ai/veil-cli@latest

# sign in from the browser (people), stores a token locally
veil login
veil whoami

# exposure scan — free, unmetered (what a decompiler + AI read first)
veil scan app.o

# or use an API key (CI)
export VEILCC_KEY="veil_sk_live_…"        # or pass --api-key

veil login opens the browser, you authorize the CLI, and a token is saved to ~/.config/veilcc. For CI, create a key in the console → API keys (shown once). Every command resolves auth in order: --api-key → VEILCC_KEY → the stored login.

veil protect

Protection is composable: obfuscate source or LLVM IR, seal a selected function, then optionally strip debug information and discardable local symbols. Sealing accepts a crown-only object or source file: keep other exported functions and global data in separate host objects. Stacking obfuscation and sealing needs source/IR. Link the returned object into your build.

synopsis
veil protect [FLAGS] <input.o | module.ll> -o <output>

# seal a crown-only C/C++ object
veil protect --seal crown.o --proto "uint32_t check_license(const char*)" -o crown.sealed.o

# seal by symbol + signature (Rust / any C-ABI object)
veil protect --seal --symbols check_license \
  --proto "uint32_t check_license(const unsigned char*, size_t)" \
  liblicense.o -o license.sealed.o

# obfuscate, seal, then harden source with one veil:seal annotation
veil protect crown.c --obfuscate --seal --post-obfuscate -o crown.o

# strip an existing object without sealing
veil protect --strip app.o -o app.stripped.o

# seal with a device-bound key, unwrapped at runtime, never in the file
veil protect --seal --seal-key device-bound --key-provider veil_key \
  attest.o --proto "uint32_t attest(const char*)" -o attest.sealed.o --receipt build-receipt.json

Flags

FlagApplies toDescription
--obfuscateobfuscateObfuscate the whole module on source / IR. Default set: mixed-boolean arithmetic + opaque-predicate decoys (lighter), reseeded every build.
--obfuscate-maxobfuscateAlso apply control-flow flattening to every function. Strongest, but it bloats the binary and adds runtime cost. Implies --obfuscate.
--sealsealSeal the annotated functions — ships as ciphertext, decrypts per call. Works on a compiled object.
--post-obfuscateallStrip metadata and randomize retained private symbol names after compilation. Verify code/data contents, exports/imports and post-strip relocations; reject incompatible results. Does not rewrite instructions or pack executables.
--scope sealedsealRestrict optional IR obfuscation to the exact C symbol selected for sealing. The web app uses this scope; CLI defaults to whole-module IR protection.
--stripallRemove debug information and discardable local symbols after obfuscation/sealing, preserving exports and relocation-required symbols. Also works alone on compiled objects. Removes analysis metadata; does not encrypt code or measure resistance to reverse engineering.
--seal-keysealembedded (default) · device-bound (Enterprise only; key from the enclave/TPM, never in the file).
--levellegacyBack-compat alias: obfuscate · seal · device-bound → maps to the flags above.
--protosealThe sealed function's C signature, e.g. "uint32_t check_license(const char*)". Optional for source with exactly one veil:seal annotation; required for objects and IR. Use one function per request.
--symbolsbothComma-separated symbol names to protect when the source carries no annotation (Rust, IL2CPP, Swift).
--targetbothOutput triple. Default auto-detect from the object. See Targets.
--persistentsealDecrypt once and cache, for very-hot sealed functions. Default is ephemeral: decrypt per call, then scrub and unmap, so plaintext never persists.
--anti-debugsealFold a debugger-present check into the key, so a traced process decrypts to garbage. A cost layer, patchable; off by default (can false-trigger under some sandboxes).
--key-provider SYMdevice-boundRoot the key outside the binary: the loader calls int SYM(unsigned char key[32]) at runtime. Back it with a secure-enclave/TPM unwrap. Returns a one-time provision key in the receipt.
--seed 0x…obfuscate/post-compileFix the seed for IR transformations and private-name randomization. Blank rotates it; sealing still uses fresh encryption randomness.
-o, --outallPath for the protected artifact (a drop-in object).
--receiptallWrite the build receipt JSON here.
--api-keyallOverrides VEILCC_KEY.
--api-urlallPoint at your own Protection API for on-prem / air-gapped builds. Default https://veilcc.com.

Post-compile hardening runs before final linking/signing. Supported objects: 64-bit little-endian Mach-O and ELF on arm64/x86-64, and standard COFF x86-64. Linked binaries, archives, embedded LTO and extended COFF relocations are rejected. Public names, section names and arbitrary strings remain; builds that inspect private symbol names should leave this option off. Receipts report actual renamed-local counts, including zero when stripping removed every eligible local. Metadata hardening is an additional layer, not evidence of immunity to AI-assisted analysis.

Targets

TriplePlatform
auto-detectInfer from the object (default)
arm64-macosmacOS, Apple silicon
x86_64-macosmacOS, Intel
x86_64-linuxLinux, x86-64
aarch64-linuxLinux, arm64
x86_64-windowsWindows, x86-64

veil verify

The receipt records the protection stages, seed, and a shortened SHA-256 hash of the final artifact after stripping. Verify the artifact hash offline or in CI. The current verifier checks this hash; it does not authenticate a cryptographic signature or measure cost to break.

terminal
veil verify build-receipt.json --artifact app.sealed.o
# → receipt hash matches · sealed: check_license · level seal · ok

Protection API

The CLI and Action are clients of this one endpoint. Authenticate with a Bearer key; upload the object and options as multipart form data; receive the protected artifact (base64) and a receipt. Quota is per workspace per month.

POSThttps://veilcc.com/api/protect

Headers: Authorization: Bearer veil_sk_live_…

Form fieldValue
binary requiredThe object (.o) or source to seal, or source / IR (.c/.cpp/.ll) to obfuscate. Max 24 MB.
obfuscate1 to obfuscate the whole module (source or IR).
seal1 to seal the annotated functions.
strip1 to remove debug information and discardable local symbols from the final object. Can be combined with obfuscation/sealing or used alone on an object.
seal_keyembedded (default) · device-bound.
levelLegacy alias for the above: obfuscate · seal · device-bound.
protoSeal: the C signature of the sealed function.
symbolsComma-separated symbol names (unannotated sources).
targetOne of the target triples.
persistent1 to cache the decrypted function (seal).
anti_debug1 to fold a debugger check into the key (seal).
key_providerRuntime key-unwrap symbol (device-bound).
seed0x… fixed build seed (obfuscate).

Request

curl
curl https://veilcc.com/api/protect \
  -H "Authorization: Bearer $VEILCC_KEY" \
  -F "binary=@license.o" \
  -F "seal=1" \
  -F "proto=uint32_t check_license(const char*)" \
  -F "anti_debug=1" \
  -o response.json

Response

200 application/json
{
  "ok": true,
  "artifact_b64": "f0VMRgIBAQ…",          // the protected object, base64
  "receipt": {
    "protect": "seal",
    "obfuscate": "off",
    "seal": "on",
    "sealed_symbol": "check_license",
    "target": "arm64-macos",
    "plaintext_in_file": "none",
    "hot_paths": "left native",
    "key_cache": "ephemeral (scrubbed per call)",
    "anti_debug": "on (traced process decrypts to garbage)",
    "seed": "0x9f3ac1…",
    "receipt_hash": "b7a4…e39c"
  },
  "quota": { "used": 42, "limit": 500 }
}

Decode artifact_b64 to the output path and link it as normal. On-prem customers run the identical API inside their network and set --api-url / the Action's api-url accordingly; the object never leaves.

Errors

StatusMeaning
400Bad request, e.g. sealing without a proto, or an unknown target.
401Missing or invalid API key (or a revoked key).
403The selected feature needs another tier. Device-bound sealing requires Enterprise. The response includes code=plan_required, required_plan, upgrade_url, and action_label.
413Object exceeds the 24 MB cap.
422The toolchain rejected the input, e.g. the named symbol isn't in the object. The error carries the runner's message.
429Monthly allowance is used or reserved by running jobs. Includes code=quota_exceeded, usage, resets_at, an upgrade/sales action, and a Retry-After header. Free: 1 build/month; Pro: 500; Enterprise: 1,000,000.
502Protection runner unreachable.
503Runner configuration or build accounting is unavailable. No protected artifact is delivered.