Summary
os_env.sandbox.credential_proxy resolves its source (env / file / command) once, at OS-env helper start, bakes the secret into the in-memory CredentialRewriteRule, and never re-reads it for the life of the helper. For sources that yield a short-lived secret — notably gh_basic with a GitHub App installation token (~1 h lifetime) — a session that outlives the token starts getting proxy-injected 401s mid-run, with no recovery short of restarting the helper.
Where (verified against v0.6.0)
inner/os_env.py _start_locked → prepare_credential_proxy_runtime(...) resolves each entry's source once.
inner/credential_proxy.py _resolve_secret(...) reads env/file/command and the result is stored in CredentialRewriteRule; the proxy (inner/egress/proxy.py _rewrite_authorization) swaps in that cached value on every bound-host request. Nothing re-invokes _resolve_secret on an upstream 401 or after a TTL.
Impact
Any credential_proxy whose secret is shorter-lived than the session it serves. The concrete case: a gh_basic source backed by a rotating GitHub App installation token — the intended secretless-GitHub pattern for a sandboxed agent shell. Force-refreshing the token at helper start only widens the window (to ~1 h); it cannot cover a session that runs longer.
Request
Re-resolve the source when a bound-host request comes back 401 (bounded retry: re-resolve once, replay, then surface the 401), and/or support an optional TTL after which the next bound-host request re-resolves the source. Either would let short-lived-token sources work for long-lived sessions without a helper restart. The command source already runs in the trusted parent, so re-invoking it is consistent with the existing trust model.
Workaround we run
Best-effort force-refresh of the token in the command: source at helper start (so it begins ~1 h fresh), plus documenting the residual gap as an accepted limitation for now.
Summary
os_env.sandbox.credential_proxyresolves itssource(env/file/command) once, at OS-env helper start, bakes the secret into the in-memoryCredentialRewriteRule, and never re-reads it for the life of the helper. For sources that yield a short-lived secret — notablygh_basicwith a GitHub App installation token (~1 h lifetime) — a session that outlives the token starts getting proxy-injected 401s mid-run, with no recovery short of restarting the helper.Where (verified against
v0.6.0)inner/os_env.py_start_locked→prepare_credential_proxy_runtime(...)resolves each entry's source once.inner/credential_proxy.py_resolve_secret(...)readsenv/file/commandand the result is stored inCredentialRewriteRule; the proxy (inner/egress/proxy.py_rewrite_authorization) swaps in that cached value on every bound-host request. Nothing re-invokes_resolve_secreton an upstream401or after a TTL.Impact
Any
credential_proxywhose secret is shorter-lived than the session it serves. The concrete case: agh_basicsource backed by a rotating GitHub App installation token — the intended secretless-GitHub pattern for a sandboxed agent shell. Force-refreshing the token at helper start only widens the window (to ~1 h); it cannot cover a session that runs longer.Request
Re-resolve the
sourcewhen a bound-host request comes back 401 (bounded retry: re-resolve once, replay, then surface the 401), and/or support an optional TTL after which the next bound-host request re-resolves the source. Either would let short-lived-token sources work for long-lived sessions without a helper restart. Thecommandsource already runs in the trusted parent, so re-invoking it is consistent with the existing trust model.Workaround we run
Best-effort force-refresh of the token in the
command:source at helper start (so it begins ~1 h fresh), plus documenting the residual gap as an accepted limitation for now.