feat(supervisor): enforce supervisor session token expiry with proactive reconnect
@pimlock is already working on this.
Since Jun 17, 2026.
- Dominant language
- Rust
- Stars
- 8.7k
- Forks
- 1.3k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 253
Description
Problem Statement
ConnectSupervisor is authenticated when the bidirectional stream is created. The supervisor process already refreshes its gateway sandbox JWT in the background, and new outbound RPCs pick up the refreshed token through the shared AuthInterceptor token slot. An already-open ConnectSupervisor stream does not get re-authenticated when that token slot changes.
For the initial session-control migration in #1731, this is acceptable: the accepted stream remains valid until disconnect, supersede, sandbox deletion, or gateway restart. This follow-up tracks making session lifetime explicitly align with token lifetime.
Proposed Design
Add expiry-enforced supervisor sessions with proactive reconnect:
- Record the accepted token/session expiry when the gateway accepts
ConnectSupervisor. - Extend
SessionAcceptedwith both:session_expires_at_unix_msreconnect_before_unix_ms
- Have the supervisor open a fresh
ConnectSupervisorbeforereconnect_before_unix_ms, using the already-refreshed process-wide token. - Let the gateway supersede the old session through the existing
SupervisorSessionRegistryreconnect behavior. - Keep bearer tokens out of supervisor session payloads.
- Add a short gateway grace period so clock skew or scheduler delay does not cause avoidable sandbox disconnects.
Alternatives Considered
- Keep accepted stream as a lease forever: simpler, and acceptable for #1731 v1, but token expiry no longer bounds live session authorization.
- Send fresh bearer tokens inside stream messages: rejected because it introduces credential material into the long-lived control payloads and duplicates the existing gRPC auth path.
Agent Investigation
Relevant current behavior:
crates/openshell-core/src/grpc_client.rsstores the bearer token in a process-wide slot.refresh_token_looprenews the gateway sandbox JWT around 80 percent of remaining lifetime.AuthInterceptorinjects the current token into new outbound gRPC requests.crates/openshell-server/src/supervisor_session.rsvalidates theConnectSupervisorrequest once at stream creation and does not re-check token expiry in the session loop.
Definition of Done
-
SessionAcceptedcarries explicit expiry and reconnect deadline fields. - Gateway computes and records session expiry for accepted supervisor sessions.
- Supervisor reconnects before the deadline using the refreshed token slot.
- Gateway supersedes the old session without interrupting normal relay/control behavior.
- Expired sessions are closed or rejected after a documented grace period.
- Tests cover reconnect-before-expiry, expired-session handling, and supersede behavior.
- Architecture docs describe the session auth lifetime.
Related
- Follow-up to #1731
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.