Placeholder-injection mode (Kloak-inspired transparency)

Status: ROADMAP — captured 2026-04-25 from a discussion of Kloak’s eBPF kernel-level interception approach.

Background

Kloak (Kubernetes eBPF HTTPS interceptor) achieves a strong property: the application never holds real secrets. The app holds opaque placeholder tokens; an eBPF program in the kernel TLS path swaps placeholders for real credentials at the moment of network send. If the app process is compromised and its memory is dumped, the attacker gets placeholders, not credentials.

We can’t trivially port their mechanism — eBPF is Linux-only, requires root + recent kernels, and TLS interception specifically requires uprobes into userspace TLS libraries. Calciforge runs on macOS as a first-class target; we’d be cutting that off.

Proposed approach: HTTP-proxy-level placeholder injection

The same property — “agent never holds real secret” — can be achieved with our existing HTTP-proxy architecture by inverting the current flow:

Current flow (`` substitution)

  1. Agent author writes Authorization: Bearer
  2. Agent process emits that literal string in its request
  3. security-proxy substitutes `` → real value
  4. Real value goes to upstream

Property: agent never sees the real value, BUT must know about the substitution syntax. Off-the-shelf agents that don’t know about Calciforge can’t use this — they hardcode env-var reads.

Proposed flow (placeholder injection)

  1. Calciforge spawns the agent process with env: OPENAI_API_KEY=cfg_OPENAI_KEY_a1b2c3d4e5f6... (per-agent random)
  2. Agent reads env, thinks it holds the real key, and emits an Authorization: Bearer … header carrying the placeholder value
  3. security-proxy recognizes the placeholder pattern, looks up real value in its per-agent placeholder→secret map, substitutes, forwards to upstream
  4. Real value goes to upstream

Property: agent never sees the real value AND doesn’t need to know about Calciforge. Works with any off-the-shelf agent that reads credentials from env vars.

Current staged implementation

Status as of 2026-05-12: the security-proxy-local primitives are merged, but live placeholder substitution is intentionally not enabled yet.

Implemented pieces:

Not yet wired:

The next safe implementation slice is Calciforge-side lifecycle wiring: decide which supervised agent runtime owns placeholder creation, where generated values are injected, and where single-token or whole-agent retirement is called. That delivery surface may be an env var for CLI agents, a wrapper-generated config file, or a managed credentials folder for agents that already expect plaintext files. Live substitution should remain disabled until that owner can deterministically register and retire tokens.

That boundary is explicit: calciforge currently owns agent config and adapter construction, while security-proxy owns the in-memory placeholder registry. AgentConfig.env is cloned into concrete subprocess adapters at adapter construction time, and installer-managed wrappers separately export CALCIFORGE_AGENT_ID for central secret helper calls. Do not hide placeholder generation inside an adapter constructor by making adapters reach into SecurityProxy; that would make lifecycle ownership and retirement ambiguous. The next implementation should either move the placeholder lifecycle API into a shared crate used by both sides, or add an explicit Calciforge-owned registration channel/client before adapter env maps are rewritten.

What we’d build

Per-agent state in security-proxy:

pub struct PlaceholderMap {
    /// agent_id → placeholder token → real secret name
    /// e.g. "claude-research" → "cfg_OPENAI_KEY_a1b2..." → "OPENAI_API_KEY"
    by_agent: HashMap<String, HashMap<String, String>>,
}

When Calciforge starts an agent (today: the channel router does this indirectly via the openclaw adapter; tomorrow: explicit “spawn under supervision” entry point):

  1. Look up which secrets the agent’s config references
  2. Generate a per-agent random placeholder for each
  3. Set agent’s env to use placeholders
  4. Register placeholders in security-proxy’s PlaceholderMap

When security-proxy sees an outbound request, it scans body + headers for placeholder shapes (regex on the cfg_*_* prefix) and swaps through PlaceholderMap before forwarding. Same code path as `` substitution — just a different recognizer.

Important invariant: the placeholder path must never trust the embedded <NAME> hint in cfg_<NAME>_<random>. It must resolve the full opaque token through the per-agent map, apply the same per-agent/user/channel secret access policy and destination allowlist as explicit `` substitution, and only then load the real secret value.

Comparison vs. true eBPF interception

Property True eBPF Placeholder injection
Agent never sees real secret
Works without agent’s awareness of Calciforge
Kernel-enforced (agent can’t bypass) ❌ (agent can bypass cooperative proxy env unless paired with host/container egress controls)
Linux only yes no — cross-platform
Requires root yes (CAP_BPF) no
Requires recent kernel yes (5.x+) no
Engineering cost months ~1 week
Debuggability brutal normal HTTP-proxy logs
Compatible with our existing substitution engine rewrite direct extension

Threat model deltas

Things both approaches catch:

Things only true eBPF catches:

Things neither catches:

Implementation notes

Out of scope for first cut

Rough effort estimate

~1 week for a working prototype:

Compared to the months that a true eBPF implementation would take (plus the ongoing Linux-only constraint), the cost-benefit strongly favors placeholder injection unless someone shows up needing kernel- enforced isolation specifically.