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 (Homebrew / cargo / direct, once released)
brew install veilcc/tap/veil

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

# 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

Protect one compiled object (sealing) or one LLVM IR module (obfuscation) and write the artifact. Your link step is unchanged.

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

# seal a C/C++ object (functions auto-discovered from veil:seal)
veil protect --level seal app.o -o app.sealed.o

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

# obfuscate IR with a fixed, reproducible seed
veil protect --level obfuscate --seed 0x9f3ac1… module.ll -o module.o

# device-bound: the key is unwrapped at runtime, never in the file
veil protect --level device-bound --key-provider veil_key \
  attest.o -o attest.sealed.o --receipt build-receipt.json

Flags

FlagApplies toDescription
--levelallobfuscate · seal · device-bound. Default seal.
--proto repeatablesealThe sealed function's C signature, e.g. "uint32_t check_license(const char*)". Omit for a C source object where veil:seal functions are auto-discovered.
--symbolsallComma-separated symbol names to protect when the source carries no annotation (Rust, IL2CPP, Swift).
--targetallOutput 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…obfuscateFix the moving-target seed for a reproducible build. Blank rotates it every build.
-o, --outallPath for the protected artifact (a drop-in object).
--receiptallWrite the signed 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.

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

Every protected build carries a signed receipt: what was sealed, the seed, and a cost-to-break estimate. Verify one offline, or in CI, against the artifact it describes.

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) to seal, or the IR (.ll) to obfuscate. Max 24 MB.
levelobfuscate · 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 "level=seal" \
  -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": {
    "sealed_symbol": "check_license",
    "level": "seal",
    "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).
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 quota reached for the workspace's plan.
502Protection runner unreachable.