Skip to Content
Core FeaturesSecret Shielding

Secret Shielding

Secret Shielding keeps your local, project-specific secrets — API keys, passwords, private keys, connection strings — from ever reaching the third-party LLM provider, while letting the agent keep working with the real values.

When an agent reads a .env file, prints ~/.aws/credentials, or includes a *.pem in a diff, those values would normally travel to the model provider, where they can be logged or captured into a future training run. With Secret Shielding enabled, Agent Harbor’s proxy reversibly tokenizes those values: it swaps each real secret for a neutral placeholder on the way to the model, and swaps it back on the way back to the agent.

agent ──request: prompt contains REAL secret──▶ PROXY ──secret → placeholder──▶ LLM provider agent ◀──restored: placeholder → REAL secret── PROXY ◀──response w/ placeholder── LLM provider

The provider only ever sees placeholders like [AH_TKN_a8k2c3d4]. The agent stays fully functional, because the placeholder is restored to the real value before the agent acts on it — so when the model says “write this key to config.yaml”, the agent writes the real key, not a redaction marker.

This is not redaction. Tools that replace secrets with an irreversible [REDACTED] marker break the agent — it can never use the secret afterwards. Secret Shielding is reversible, so the agent keeps working.

What it protects (and what it doesn’t)

Protects againstInadvertent disclosure of your local secrets to the LLM provider and its logs/training.
Does not replaceProvider-auth handling (the credential the agent uses to call the model) — that is managed separately. Secret Shielding is for unrelated local secrets that happen to land in the agent’s context.
RequiresMonitoring enabled (the proxy must be in the request path). It is unavailable under --no-monitoring.

Enabling it

Secret Shielding is configured from the [secret-shielding] table in your Agent Harbor config (layered across user / project / org), and can be overridden per run with CLI flags. The active LLM provider credential is already handled by credential management — here you declare the other secrets your project uses.

[secret-shielding] mode = "strict" # off | on | strict patterns = true # also catch undeclared secret-shaped values # Known-value sources (primary). The proxy loads the exact secret strings # from these at session start and tokenizes any occurrence in LLM traffic. [[secret-shielding.secrets]] from-files = [".env", "~/.aws/credentials", "**/*.pem", "**/id_rsa"] from-env = ["DATABASE_URL", "STRIPE_SECRET_KEY"] values = [] # explicit literals or secret-store refs (inline literals discouraged) min-length = 8 # ignore trivially short values to avoid over-matching

A user who declares secrets in [secret-shielding] gets shielding automatically — no per-run flags needed.

Modes

ModeBehavior
offNo shielding.
onBest-effort: tokenize known + detected secrets; if a request body can’t be fully scanned, forward it and record a degraded event.
strictFail-closed: if an outbound request body can’t be fully scanned, block it rather than risk leaking a secret. Recommended for sensitive projects.

CLI flags

These work on the direct launchers (ah claude, ah codex, …), ah agent start, ah agent record, and ah review, and override config for that run:

FlagPurpose
--secret-shield <off|on|strict>Set the mode for this run.
--shield-secret <VALUE|@FILE>Add a known secret value (or read it from @FILE). Repeatable.
--shield-from <PATH|GLOB>Add secret-bearing files to scan (e.g. .env, *.pem). Repeatable.
--shield-patterns <on|off>Toggle the detect-secrets-style backstop for undeclared secrets.
ah claude --secret-shield strict --shield-from .env --shield-secret @~/.config/app/token

How detection works

  • Known values (primary). The exact secret strings loaded from your manifest are matched precisely, so they round-trip reliably. Matching is encoding-aware: a known value is still caught when it appears base64- / URL- / JSON-escaped, line-wrapped, hex-encoded, or inside a gzip/deflate-compressed request body.
  • Pattern backstop (optional). With patterns = true, a detector also flags undeclared secret-shaped values (AWS keys, GitHub tokens, OpenAI/Anthropic/Google keys, Slack tokens, PEM blocks, high-entropy strings). When it catches one, that occurrence is tokenized too and surfaced so you can add it to your manifest.

Observability

Each session reports its shielding posture and emits events you can see in the session timeline and /sessions/{id}/events — the secret value is never logged, only its name and counts:

  • SecretShielded — a value was tokenized on a request (or restored on a response).
  • SecretLeakBlocked — in strict mode, a request was blocked because it couldn’t be safely scanned.
  • SecretShieldDegraded — on mode forwarded something it couldn’t fully scan.

The session’s monitoring summary includes a secret_shielding field: full, degraded, or off. Recorded sessions (AHR) store only the shielded form, so replaying a session never reveals a real secret.

Limitations

  • Truly arbitrary / nested / custom encodings can’t be exhaustively decoded; the pattern backstop is the only line of defense there.
  • Model mangling. If the model reformats or splits a placeholder instead of echoing it verbatim, that instance can’t be restored (it is forwarded as-is and counted for observability). The placeholder format is chosen to minimize this.
  • Monitoring required. Shielding only works when the proxy is in path; --secret-shield strict together with --no-monitoring is rejected rather than running with real secrets exposed.
  • Sandboxing — isolates the agent’s filesystem, process, and network; complementary to Secret Shielding.
  • Configuration — the full configuration hierarchy and precedence.