You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: architecture/gateway.md
+81-2Lines changed: 81 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -354,6 +354,70 @@ authenticated sandbox ID with any sandbox ID or name resolved from the request.
354
354
Supervisor control and relay streams require a matching sandbox principal before
355
355
the gateway registers the session or bridges relay bytes.
356
356
357
+
## HA Supervisor Ownership
358
+
359
+
In multi-replica Kubernetes deployments, every gateway pod can accept client
360
+
RPCs, but a sandbox supervisor maintains one active stream to one gateway
361
+
replica at a time. The connected replica publishes a short-lived supervisor
362
+
owner record in the shared Postgres object store with its replica id, peer DNS
363
+
endpoint, supervisor instance id, and connection epoch. Ownership does not move
364
+
because another gateway receives a client request. It changes only when the
365
+
supervisor opens a new control stream, usually after the previous owner pod is
366
+
terminated or the stream breaks. A reconnect from the same supervisor instance
367
+
with a newer epoch can supersede the previous owner before the TTL expires, and
368
+
heartbeats from the active connection renew that current owner record.
369
+
Cleanup from an older connection checks the shared owner record before and
370
+
after changing sandbox readiness. It cannot demote a sandbox after a newer
371
+
replica has published replacement ownership.
372
+
373
+
Session-bound operations such as exec, TCP forwarding, file sync, and sandbox
374
+
service routing first check the local session registry. If the supervisor is
375
+
owned by another gateway replica, the serving gateway opens an internal
376
+
`PeerRelay` stream to that owner and asks it to open the supervisor relay. This
377
+
keeps client traffic working when a Kubernetes Service routes the client to a
378
+
non-owner gateway pod. If a peer owner is stale or unreachable during a rollout,
379
+
the serving gateway retries ownership lookup until the normal relay wait
380
+
deadline. Each retry re-reads the owner record, so a supervisor reconnect or
381
+
heartbeat can surface a new owner; if no fresh reachable owner appears before
382
+
the deadline, the client operation fails rather than electing an owner itself.
383
+
Provider-readiness reports, endpoint-status reports, and provider-status reads
384
+
also follow the durable owner record through unary peer RPCs. The owning replica
385
+
validates the current supervisor session and keeps the in-memory evidence; a
386
+
non-owner never accepts evidence from a stale local session or projects a
387
+
remote session as disconnected.
388
+
389
+
Nothing redistributes established sessions, so after a rolling restart the last
390
+
surviving replica holds most sessions and a new replica serves none until
391
+
sandboxes reconnect. That skew decays only as sandboxes churn. Client traffic
392
+
stays correct throughout because a non-owner relays to the owner.
393
+
394
+
File upload and download use tar-over-SSH through the same relay path. A gateway
395
+
pod termination drops the active SSH proxy byte stream, so the CLI retries the
396
+
whole sync operation with a fresh SSH session instead of attempting mid-stream
397
+
resume.
398
+
399
+
Gateway peer RPCs authenticate with Kubernetes ServiceAccount identity rather
400
+
than a shared secret. Helm mounts a projected, pod-bound token with audience
401
+
`openshell-gateway-peer`; the receiving gateway validates it through
402
+
TokenReview, checks the live pod UID and chart selector labels, and authorizes
403
+
only the internal peer RPC methods. When gateway TLS is enabled, peer clients
404
+
also trust the chart CA, present the chart-generated client certificate for
405
+
mTLS, and verify the stable gateway Service DNS name even when connecting to a
406
+
Deployment pod IP.
407
+
408
+
`WatchSandbox` uses the local update bus for same-replica writes. On
409
+
multi-replica backends one shared poller per gateway observes resource-version
410
+
changes made by other replicas and feeds that bus for all local watchers,
411
+
avoiding a database poll per client stream. SQLite deployments do not run the
412
+
poller because they are single-replica and the local bus already sees every
413
+
write.
414
+
415
+
Mutations whose invariants span sandbox, provider-profile, policy, or provider
416
+
records take a process-local mutex and a shared PostgreSQL advisory lock. The
417
+
database session remains dedicated to the request and closes when the guard is
418
+
dropped, which releases the lock on normal completion, cancellation, or error.
419
+
SQLite deployments use only the local mutex because they are single-replica.
420
+
357
421
## API Surface
358
422
359
423
The gateway API is organized around platform objects and operational streams:
@@ -753,7 +817,7 @@ migrations backfill existing rows with version 1.
753
817
Provider profile imports, updates, and deletes hold the sandbox synchronization
754
818
guard while checking attached-sandbox dynamic token grant ambiguity or in-use
755
819
state and writing the profile record. Sandbox creation with initial providers and
756
-
sandbox provider attach/detach use the same guard, so one gateway process cannot
820
+
sandbox provider attach/detach use the same guard, so gateway replicas cannot
757
821
interleave a profile mutation with a sandbox provider-set mutation that would
758
822
leave an ambiguous final dynamic-token state or a deleted custom profile that is
759
823
still referenced by a sandbox.
@@ -782,6 +846,15 @@ resolution and again by the sandbox placeholder resolver. This keeps expired
782
846
credentials from resolving even when a running sandbox still has retained
783
847
placeholder generations from an earlier provider credential snapshot.
784
848
849
+
All gateway-owned extension registries negotiate the same peer metadata envelope
850
+
before accepting work. Compute drivers, credential drivers, gateway interceptors,
851
+
and supervisor middleware retain their typed family manifests. Both the gateway
852
+
and extension run the shared validator against the startup exchange, enforcing
853
+
protocol-major compatibility and mutual required-capability sets before either
854
+
peer accepts the other. The gateway aggregates immutable, non-secret startup
855
+
snapshots for the protected gateway-info API; it does not publish transport,
856
+
authentication, or backend configuration.
857
+
785
858
Static credential delivery is capability-negotiated and endpoint-bound. The
786
859
gateway classifies each returned environment entry as either a credential or
787
860
non-secret provider configuration and associates every credential key with the
@@ -810,7 +883,13 @@ Provider receipts, installation status, and common operations represent absolute
810
883
811
884
Provider installation reports belong to the existing `ConnectSupervisor` session. Each report names that session, has an increasing sequence, and expires unless the supervisor reports again. Reconnection or disconnect invalidates prior observations; stored change records survive a gateway restart, but runtime evidence does not. Replaying an identical report cannot extend its lifetime.
812
885
813
-
Reports and status also compare the supervisor instance with the sandbox's persisted current instance. A different supervisor becoming current invalidates an older connection, including one retained by another gateway replica. Observations stay local to the gateway holding the supervisor session; a status request reaching a replica without that session returns pending. Multi-replica deployments therefore retain the existing supervisor-session routing requirement.
886
+
Reports and status also compare the supervisor instance with the sandbox's
887
+
persisted current instance. A different supervisor becoming current invalidates
888
+
an older connection, including one retained by another gateway replica.
889
+
Observations stay local to the gateway holding the supervisor session. A status
890
+
request or report reaching another replica follows the shared owner record to
891
+
that gateway, which remains the sole authority for accepting and projecting the
892
+
session's evidence.
814
893
815
894
The supervisor reports success only after it installs the matching credentials, activates the effective policy, and receives an acknowledgment from the authenticated workload boundary that it installed the environment for future processes. Environment synchronization shares the process-launch lock, and its acknowledgment identifies the exact publication, including retries at the same provider revision. Failed policy installation cannot reuse evidence for a different installed policy. Ready and revoked statuses also recheck the requested sandbox, provider, attachment and configuration identities; revision fingerprints are compared only for equality. Revocation applies to future credential resolution and future processes. Requests already forwarded upstream can still finish.
0 commit comments