Repository navigation
bug(supervisor): routine provider token refresh bumps the policy generation and closes in-flight tunnels and streams #4945
Description
User Story
I run OpenShell 0.1.4 on a home server to sandbox a fleet of always-on agents, using the Podman compute driver and provider-credentialed model endpoints.
One of those providers is Google Vertex AI with a gateway-refreshed OAuth access token, refreshed hourly. I directly encountered long-lived connections through the proxy being closed at each refresh, even though no network rule changed.
This affects every agent with a streaming response or open tunnel at the moment of the refresh.
Problem Statement
When a provider token refreshes, the settings poll sees a new provider_env_revision. provider_env_changed feeds policy_runtime_changed (lib.rs#L4390-L4412 @ v0.1.4; on main it moved to config_runtime.rs#L277-L302), so the supervisor reloads the policy runtime and moves to a new generation. Every relay holding the old generation then closes with "policy generation is stale" (proxy/relay.rs#L354 @ v0.1.4, #L349 @ main); L7 tunnels log "L7 tunnel policy generation is stale" (l7/relay.rs#L3412 @ v0.1.4).
With v0.1.5's push delivery (#4320, config_delivery_mode = "push"), any delivered provider bundle counts as provider_env_changed.
#3809 and #3994 fixed the spurious first-poll version of this in v0.1.3 (#3819). The real refresh case is still there on main.
Related: #3809, #3994, #3819, #4734 (open: freeze effective policy; credential refresh must keep working, but it does not cover dropped connections).
Impact / Why This Matters
Long-lived CONNECT tunnels, WebSockets and streaming responses are cut at every refresh, and clients must reconnect. In our tests that added about 17 s each time, and with an hourly token it happens every hour for every sandbox using the provider. RFC 0009's reload decision says supervisors should "reuse connections for policy-only changes"; a credential rotation that changes no network rule is an even weaker reason to cut connections.
Acceptance Criteria
- When a provider refresh only rotates credential values (same identity, same endpoints, same policy), established tunnels, WebSockets and streaming responses stay open.
- Requests made after the refresh use the new token.
- No
policy generation is staledeny follows a refresh that changed only credential values. - A real change to policy or endpoint binding still moves to a new generation, as today.
Reproduction Steps
- Attach a provider with a short-lived, refreshing token.
- Hold a long-lived connection open through the proxy (a WebSocket, an SSE stream, or a large download).
- Wait for the refresh. A
CONFIG:DETECTED ... provider_env_changed:trueevent appears, thenNET:OPEN [MED] DENIED ... policy generation is stale, and the connection closes.
Suggested UX (if applicable)
No configuration change. Existing tunnels either keep the generation they started with or resolve placeholders from the latest snapshot; only a real change to policy or endpoint binding forces a new generation.
Environment
- OpenShell: 0.1.4; code unchanged on main 1d880c9
- OS: Rocky Linux 10.2, kernel 6.12.0-211.49.1.el10_2.x86_64
- Deployment: Podman compute driver (rootless, podman 5.8.2); Google Vertex provider with a gateway-refreshed OAuth access token, refreshed hourly
Logs
CONFIG:DETECTED ... provider_env_changed:true
NET:OPEN [MED] DENIED ... policy generation is staleNote: this content was drafted by Claude, an AI assistant, working on behalf of Brian Bouterse and in collaboration with him. It is AI generated and human reviewed.
Activity