lume

06 — Software protection and anti-reverse-engineering (REA)

Status: implemented (Bricks W1–W5 in compiler/stage1/protect.src, compiler/stage1/lprotect.src, lumec, lcaas.src, lmod.src, and lume build --protect <none|standard|hardened|max> [--lsym <file.lsym>]). This document specifies protection of the programs a user compiles with Lume. It is a first-class goal of the toolchain, not an afterthought, because Lume targets commercial and proprietary software (ERP, trading, games, licensed products) where the shipped artifact is intellectual property its author is entitled to protect.

Scope. These features protect the developer’s own compiled program against inspection, tampering and cloning by third parties who receive the binary. They are the same class of capability offered by LLVM’s obfuscation forks, ProGuard/R8 (Java/Android), VMProtect/Themida, and the hardening flags of every production toolchain. They are controlled entirely by the author of the code being compiled, apply only to that author’s own artifact, and never hide anything from that author. Lume does not ship malware tooling: there is no capability here for attacking other people’s systems, defeating someone else’s protection, or evading security software.

P-REA — the governing principle (added to 02-principles.md)

P33 — The author owns the artifact. The Lume toolchain gives the author of a program practical control over how much its shipped binary reveals, down to a configurable level, without ever weakening the safety guarantees of the source (P9) and without obstructing the author’s own debugging, auditing or reproducibility. Protection is opt-in, per build profile, and composable.

1. Threat model

The protected party is the author/vendor shipping a binary to an end user or deploying it to a host they do not fully control. The adversary is anyone with the binary who wants to:

  1. Read the logic — recover algorithms, formulas, business rules, prompts, model weights.
  2. Extract secrets — license keys, API endpoints, embedded credentials-by-mistake (Lume also warns against embedding these; see §6).
  3. Tamper — patch out a license check, a payment step, an integrity test.
  4. Clone / lift — copy a proprietary component into another program.

Non-goals: Lume does not claim perfect protection (no software protection is unbreakable against a determined analyst with the hardware). The goal is to raise the cost of each attack by orders of magnitude, which is what commercial protection does in practice, and to make tampering detectable.

2. Build profiles

Protection is selected per profile in lume.toml:

profile.release
    protect = .standard          # none | standard | hardened | max

profile.ship
    protect = .max
    protect.vm       = [sensitive]        # run marked functions on an embedded bytecode VM
    protect.seal     = true               # tamper-evident integrity seal
    protect.license  = "std.license.ed25519"

protect never changes program meaning (P9/P25); it changes only the shape of the artifact. A debug build always has protect = .none so the author can always inspect their own code.

3. The protection passes (what each level turns on)

.standard (default for release)

.hardened

Adds, as opt-in MLIR/lume-core passes:

.max

Adds:

4. Protecting models and prompts (the AI-era case)

Lume ships models and LLM prompts inside programs (§14 of the spec), which are themselves IP:

5. What protection must never do (hard constraints)

  1. Never change observable behaviour of a correct program (P9, P25). Protection is a shape transform; a max-protected build and a debug build compute the same results.
  2. Never hide code from its own author. The .lsym sidecar, the source, and a debug build always let the author see everything. Lume keeps no author-opaque blob.
  3. Never obstruct safety. Memory safety, contracts and effect checks run at compile time on the unobfuscated IR; obfuscation happens after checking, so a protected binary is exactly as safe as its source.
  4. Never bundle attack tooling. Lume has no feature to deobfuscate, unpack, or defeat the protection of a binary someone else produced, and no feature to evade antivirus/EDR. std.protect hardens your program; it is not an offensive toolkit.
  5. Reproducibility is preserved: given the same source, profile and a recorded per-build randomisation seed, the artifact rebuilds bit-for-bit (needed for the triple self-build, P27, and for audits). The seed is an input the author controls.

6. Secrets hygiene (the complement to obfuscation)

Obfuscation is the last resort, not the first. Lume’s effect and capability system (§8) does the primary work:

7. Roadmap placement

Stage Protection deliverable
1 Symbol stripping, .lsym sidecar, deterministic/path-independent builds, string minimisation, std.secret, W-SEC-001
2 profile.*.protect in lume.toml; constant/string encryption; @protect attribute plumbing
3 std.protect.guard self-checks; import/call obfuscation
4 @protect model, prompt encryption, encrypted-weights loader
5 Control-flow flattening, MBA, selective VM (protect.vm), tamper seals, std.protect.enclave, white-box crypto

Every pass is a lume-core/MLIR transformation we own, so protection travels to every target (native, WASM, freestanding) and composes with the rest of the pipeline.