Self Checks
1. Is this request related to a challenge you're experiencing? Tell me about your story.
Environment Hardening: Secret Injection Without Exposure
Problem
Secret environment variables in shell outputs are currently redacted via a configurable pattern list, but leakage remains possible:
- Encoding bypass — apply a transformation (e.g. base64) to the sensitive envvar, then decode it externally.
- Exfiltration via network — transmit the secret directly to untrusted sites (e.g. as an HTTP header value).
Pattern-based redaction is inherently an arms race; the root cause is that secrets live in the agent's process address space at all.
Proposed Solution
Remove secrets from the agent's process entirely. Instead of injecting real values as environment variables, maintain a mapping outside the agent's reachable filesystem and resolve placeholders at the network boundary.
Provider Profiles
Each provider is defined by a complete injection profile — not just a secret value, but the full mapping of which env var is injected into which HTTP header for which target domain(s). The proxy uses these profiles as an allowlist; secrets are never injected into requests that don't match.
# /run/secrets/<session-id>/providers.yaml
providers:
dify:
secrets:
- env: INTERNAL_ACCESS_TOKEN # placeholder exposed to agent
value_file: dify/internal_token # actual secret (file ref)
inject:
header: Authorization # HTTP header to populate
prefix: "Bearer " # optional prefix before value
domains: # allowlisted target domains
- "*.dify.internal"
- "api.dify.ai"
github:
secrets:
- env: GITHUB_TOKEN
value_file: github/token
inject:
header: Authorization
prefix: "Bearer "
domains:
- "api.github.com"
- "uploads.github.com"
- env: GITHUB_WEBHOOK_SECRET
value_file: github/webhook_secret
inject:
header: X-Hub-Signature-256
prefix: "sha256="
domains:
- "*.github.com"
aws:
secrets:
- env: AWS_ACCESS_KEY_ID
value_file: aws/access_key_id
inject:
header: X-Amz-Access-Key # resolved by SigV4 signing
domains:
- "*.amazonaws.com"
- "*.aws.amazon.com"
- env: AWS_SECRET_ACCESS_KEY
value_file: aws/secret_access_key
inject:
header: X-Amz-Secret-Key # consumed by proxy's SigV4 signer
domains:
- "*.amazonaws.com"
- "*.aws.amazon.com"
- env: AWS_SESSION_TOKEN
value_file: aws/session_token
inject:
header: X-Amz-Security-Token
domains:
- "*.amazonaws.com"
openai:
secrets:
- env: OPENAI_API_KEY
value_file: openai/api_key
inject:
header: Authorization
prefix: "Bearer "
domains:
- "api.openai.com"
gcloud:
secrets:
- env: GCLOUD_ACCESS_TOKEN
value_file: gcloud/access_token
inject:
header: Authorization
prefix: "Bearer "
domains:
- "*.googleapis.com"
- "*.google.com"
Secret Store Layout
Provider profiles and secret value files live outside the agent's Landlock-restricted workspace:
/run/secrets/
└── <session-id>/
├── providers.yaml ← injection profiles (read by proxy)
└── <provider>/
└── <secret-name> ← file contains the real secret value
Example:
/run/secrets/session-1/providers.yaml
/run/secrets/session-1/dify/internal_token
/run/secrets/session-1/github/token
/run/secrets/session-1/aws/access_key_id
Injection & Resolution
- The agent process receives only a placeholder in its environment, e.g.
GITHUB_TOKEN=__secret:github:GITHUB_TOKEN__.
- The outbound proxy intercepts every request, matches the target domain against provider profiles, and only injects the secret into the declared header if the domain matches.
- If no profile matches the target domain, the placeholder is stripped (never forwarded raw).
- Each injection event is logged for audit (provider, env key, target domain, timestamp).
Third-Party CLI Tools
For tools that require authenticated credentials (e.g. gh, aws, gcloud, kubectl), the flow is:
- Authenticate with the 3rd-party provider (OAuth, API key exchange, etc.) — this happens outside the agent runtime.
- Convert the resulting credential into a mapped secret file in the secret store.
- Deploy the mapped file into the agent runtime's secret mount so the proxy can inject it on outbound calls.
┌──────────────────────────────────────────────────────────────────────────┐
│ OUTSIDE AGENT RUNTIME │
│ │
│ ┌──────────┐ ┌───────────────────┐ ┌────────────────────┐ │
│ │ 3rd-Party│ │ Credential Mgr │ │ Secret Store │ │
│ │ App │◄─────►│ (auth + convert) │──────►│ /run/secrets/ │ │
│ │(GitHub, │ OAuth │ │ write │ session-1/ │ │
│ │ AWS …) │ / key │ │ │ ├─ providers.yaml │ │
│ └──────────┘ └───────────────────┘ │ ├─ github/token │ │
│ │ ├─ aws/access_key │ │
│ │ └─ openai/api_key │ │
│ └─────────┬──────────┘ │
│ │ mount │
├───────────────────────────────────────────────────────────┼─────────────┤
│ AGENT RUNTIME │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────────────────┐ ┌────────────┐ │
│ │ Agent │──────►│ Outbound Proxy │─►│ Internet │ │
│ │ Process │ req │ │ │ │ │
│ │ │ │ 1. match target domain │ │ github.com │ │
│ │ env: │ │ against profiles │ │ aws.com │ │
│ │ GITHUB_TOKEN │ │ 2. lookup value_file │ │ openai.com │ │
│ │ = __secret: │ │ 3. inject into header │ │ │ │
│ │ github: │ │ 4. strip all remaining │ │ ✗ evil.com│ │
│ │ GITHUB_TOKEN │ │ placeholders │ │ (blocked) │ │
│ │ __ │ │ 5. log audit event │ │ │ │
│ └──────────────┘ └──────────────────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
Flow Summary
authenticate convert deploy resolve
─────────────► ─────────────────► ──────────────────► ────────────►
3rd-party credential → mount secret proxy injects
OAuth/key secret file into runtime real value
Design Decisions
| Decision |
Resolution |
| Injection scope |
Per-domain. Each secret is only injected into requests matching the profile's domains allowlist. |
| Credential lifecycle |
Session-scoped by default. Profiles are created at session start and destroyed at session end. |
| Secret rotation |
The credential manager can overwrite value_file contents without restarting the session; the proxy reads on each request. |
| Audit trail |
Every injection event is logged: (timestamp, session_id, provider, env, target_domain, header). |
| Fallback behavior |
If a placeholder appears in a request body or non-declared header, the proxy blocks the request and logs a warning. |
| Profile validation |
Profiles are validated at session start — invalid YAML, missing value files, or overlapping domain rules are rejected. |
Security Properties
- No secret in agent address space — the agent only sees opaque placeholders.
- Domain-pinned injection — even if the agent crafts a request to
evil.com with a placeholder header, the proxy refuses injection.
- No global resolution — there is no wildcard "inject everywhere" mode; every injection requires an explicit domain match.
- Placeholder stripping — unresolved placeholders are never forwarded to any upstream.
2. Additional context or comments
No response
3. Can you help us with this feature?
Self Checks
1. Is this request related to a challenge you're experiencing? Tell me about your story.
Environment Hardening: Secret Injection Without Exposure
Problem
Secret environment variables in shell outputs are currently redacted via a configurable pattern list, but leakage remains possible:
Pattern-based redaction is inherently an arms race; the root cause is that secrets live in the agent's process address space at all.
Proposed Solution
Remove secrets from the agent's process entirely. Instead of injecting real values as environment variables, maintain a mapping outside the agent's reachable filesystem and resolve placeholders at the network boundary.
Provider Profiles
Each provider is defined by a complete injection profile — not just a secret value, but the full mapping of which env var is injected into which HTTP header for which target domain(s). The proxy uses these profiles as an allowlist; secrets are never injected into requests that don't match.
Secret Store Layout
Provider profiles and secret value files live outside the agent's Landlock-restricted workspace:
Example:
Injection & Resolution
GITHUB_TOKEN=__secret:github:GITHUB_TOKEN__.Third-Party CLI Tools
For tools that require authenticated credentials (e.g.
gh,aws,gcloud,kubectl), the flow is:Flow Summary
Design Decisions
domainsallowlist.value_filecontents without restarting the session; the proxy reads on each request.(timestamp, session_id, provider, env, target_domain, header).Security Properties
evil.comwith a placeholder header, the proxy refuses injection.2. Additional context or comments
No response
3. Can you help us with this feature?