Skip to content

feat(supervisor): add independent application listener and endpoints - #4936

Open
TaylorMutch wants to merge 1 commit into
mainfrom
feat/4666-supervisor-app-endpoints/TaylorMutch
Open

TaylorMutch wants to merge 1 commit into
mainfrom
feat/4666-supervisor-app-endpoints/TaylorMutch

Conversation

@TaylorMutch

@TaylorMutch TaylorMutch commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Add an opt-in supervisor TLS listener for declared HTTP and WebSocket services. Application traffic connects directly to the owning supervisor and sandbox loopback connector, bypassing the gateway.

Related Issue

Closes #4666. Part of RFC 0017.

Changes

  • Add listener enablement, domain, app-port, and Kubernetes TLS configuration; default to disabled.
  • Provide optional dedicated supervisor Services. Shared application traffic uses the owning member Pod IP and app port without a shared headless Service.
  • Route exact declared hosts through the sandbox connector, isolate per-sandbox teardown, and document DNS, TLS, and ingress authentication responsibilities.

Release Notes

The application listener remains opt-in and disabled by default.

Application listener request flow

This PR adds an opt-in TLS listener on the supervisor. The gateway supplies
configuration, while application traffic reaches declared sandbox services
through the selected sandbox instance's connector.

flowchart TB
    subgraph Configuration["Configuration and deployment"]
        SETTINGS["Operator enables app listener<br/>Domain, port, TLS Secret"]
        KUBE["Kubernetes driver / Helm<br/>Configure supervisor port and mount TLS material"]
        DECLARE["Sandbox service declaration<br/>Example: web → port 8080"]
        GW["OpenShell gateway<br/>Include service allowlist in sandbox configuration"]

        SETTINGS --> KUBE
        DECLARE --> GW
    end

    subgraph HOST["Supervisor process — dedicated or shared"]
        TLS["One process-level TLS listener<br/>Example: Pod IP:8443"]
        ROUTER["SNI dispatcher<br/>Select sandbox instance"]

        subgraph INSTANCE["Selected sandbox instance"]
            CONFIG["Apply accepted configuration<br/>Install routes and renew freshness"]
            CHECK["Validate installed route and freshness<br/>Require HTTP Host to match TLS SNI"]
            PROXY["HTTP/1.1 or WebSocket proxy<br/>Apply limits and sanitize headers"]
            CONNECTOR["This instance's<br/>BoundaryLoopbackConnector"]

            CONFIG -.->|"Route table and validity"| CHECK
            CHECK -->|"Accepted"| PROXY
            PROXY --> CONNECTOR
        end

        TLS --> ROUTER
        ROUTER --> CHECK

        OTHER["Other sandbox instances<br/>Separate routes, connectors and lifetimes"]
        ROUTER -->|"Other SNI hostname"| OTHER

        REVOKE["Route removed or changed,<br/>configuration rejected, freshness expires,<br/>or instance stops"]
        CONFIG -.-> REVOKE
        REVOKE -.->|"Reject affected traffic<br/>Close affected active streams"| PROXY
    end

    subgraph SANDBOX["Selected sandbox"]
        APP["Application<br/>127.0.0.1:8080"]
    end

    CLIENT["Client<br/>team--demo--web.apps.example.com"]
    EDGE["Externally configured ingress / proxy<br/>or direct connection"]

    KUBE -.->|"Startup configuration"| TLS
    GW -->|"Existing configuration push / poll"| CONFIG
    CLIENT --> EDGE
    EDGE -->|"TLS with application SNI"| TLS
    CONNECTOR -->|"Connect to declared loopback port"| APP
Loading
  • Shared supervisor support: one listener dispatches to separate sandbox
    instances. Each retains its own connector, limits, and lifetime.
  • Configuration freshness: routes have a 30-second freshness window, renewed
    through successful configuration delivery or an accepted live push session.
    Withdrawal or expiry also closes affected streams.
  • Kubernetes reachability: this PR configures ports and TLS material. Shared
    supervisors expose individual Pod endpoints without a shared application
    Service; callers must target the owning member.
  • Authentication: this direct path relies on the application or trusted
    ingress for authentication. Gateway-mediated authentication is separate.

Advertisements, discovery, authoritative public URLs, the sandbox proxy, and
HTTPRoute reconciliation are outside this PR. The ingress box represents
infrastructure configured externally at this stage. Addresses and ports are
illustrative.

Testing

  • Validated against main fdce6ef26: 2,557 server tests passed (34 ignored), 279 supervisor tests, 310 Kubernetes driver tests, 8 proxy tests, full Go SDK CI, and 431 gateway + 6 workspace + 6 proxy Helm tests. All eight PR layers independently passed locked Rust compilation with test targets. Repository pre-commit passed with SETUPTOOLS_SCM_PRETEND_VERSION_FOR_OPENSHELL=0.0.0; plain invocation hits an existing main-branch setuptools-scm failure parsing tag sdk/go/v0.1.6. The final advance to main 9f1cdb4ef changes only tests/suites/features/Cargo.lock in each validated layer. Live HA qualification was not rerun for this rebase.
  • This layer independently passed locked Rust compilation with test targets.
  • Combined stack: 3,136 runtime library tests passed (34 ignored), 48 Rust SDK mock tests, 8 proxy tests, full Go SDK CI, and 412 gateway + 6 workspace + 6 proxy Helm tests passed.
  • Combined stack: full repository pre-commit (including strict Clippy), strict docs, and navigation checks passed.
  • Live Kubernetes stack: two gateways, shared supervisors, and proxy replicas; direct/proxy TLS and HTTP/WebSockets, abrupt owner-process loss/recovery, populated 2→1→2→1 scaling, route renewal/withdrawal, and discovery grant narrowing/regrant passed. Workload Pod identity, PID, and in-memory state survived owner handoff.

Qualification limits

Live HA validation exercised supervisor process/Pod loss on a local k3d cluster. Whole-node failure, real network partitions, metric-driven HPA, sustained production load, and published release artifacts remain unqualified. Grant-file projection contributes to revocation propagation time. Main now includes #4842 for gateway-mediated hashed hostnames. Direct application-listener/discovery hostnames remain readable; extending hashed naming to that path and installation-status presentation (#4670) remain separate work.

The example Kubernetes HTTPRoute controller is a deferred compatibility POC outside this stack.

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Relevant implementation, configuration, and API documentation updated

Draft stack: #4936 → #4940 → #4941 → #4956 → #4963 → #4964 → #4957 → #4958.

@copy-pr-bot

copy-pr-bot Bot commented Oct 26, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-actions

Copy link
Copy Markdown

Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
@TaylorMutch
TaylorMutch force-pushed the feat/4666-supervisor-app-endpoints/TaylorMutch branch from dfb34da to d94fb6a Compare October 28, 2026 17:05
@TaylorMutch
TaylorMutch marked this pull request as ready for review October 28, 2026 17:40
@github-actions

Copy link
Copy Markdown

Compatibility review (advisory)

New or updated breaking changes; see the full report for all findings.

1. rust-sdk — No explicit risk acceptance found

The SDK-reachable raw::proto::SandboxConfigSnapshot and GetSandboxConfigResponse structs gain a required service_routes field, invalidating previously exhaustive literals and patterns.

  • Impact: Consumers constructing or exhaustively matching these messages, including custom servers and mocks, stop compiling; ordinary RPC callers and server-trait signatures are unaffected.
  • Action: Keep existing raw message shapes unchanged on this no-break train; any later permitted break needs migration guidance for service_routes initialization and non-exhaustive matching.
  • PR: feat(supervisor): add independent application listener and endpoints #4936

2. typescript-sdk — No explicit risk acceptance found

Regenerated SandboxConfigSnapshot and GetSandboxConfigResponse types gain a required serviceRoutes array in the public raw entry point.

  • Impact: Complete object literals typed as either raw message fail type-checking for missing serviceRoutes, while create() and MessageInitShape-based initialization remain compatible.
  • Action: Preserve existing raw message shapes on this no-break train; any later permitted break needs guidance to add serviceRoutes: [] or use schema-based initialization.
  • PR: feat(supervisor): add independent application listener and endpoints #4936
Review details
  • Execution: complete
  • Candidate: PR #4936 (623b1575bf49333608f856d0930c9aa6e862269b)
  • Baseline: 9f1cdb4ef6ed6be9fd86507580f9656b61b9c754 (9f1cdb4ef6ed6be9fd86507580f9656b61b9c754)
  • Reviewed: rust-sdk, python-sdk, go-sdk, typescript-sdk, api-behavior, cli, configuration, policy, helm, state

Advisory review; publication gates are unchanged.

Branch check run

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(supervisor): add an independent sandbox application listener

1 participant