Appearance
Access HTTP API
This is the reference for the Access HTTP surface. It maps each operation to its OpenAPI schema, its authentication seam, and the contracts the handlers enforce. The wire-contract origin is api/openapi/plexsphere-v1.yaml; this doc is a map, not a duplicate contract. For the domain model — the Session aggregate and its issuance invariants, the per-Kind SessionTarget value objects, the canonical-JSON JWT claim set, the per-Domain SessionPolicy, the resource#act ReBAC gate, the step-up enforcement contract, and the audit relations — see the bounded-context reference ../../contexts/access.md.
A Session is a short-lived, session-scoped grant for one mediated target (ssh, k8s, or tcp) inside the owning Project, carried as an EdDSA JWT whose JTI is the session's own identifier. The surface adds six operations: issuance, a cursor-paginated list, a single read, the browser-SSH WebSocket attach, the revoke verb, and the Node-side activity callback. The carrying OpenAPI tag is access.
Operations
| Method | Path | Operation ID | Auth |
|---|---|---|---|
| POST | /v1/projects/{project_id}/sessions | IssueSession | operator bearer |
| GET | /v1/projects/{project_id}/sessions | ListSessions | operator bearer |
| GET | /v1/projects/{project_id}/sessions/{session_id} | GetSession | operator bearer |
| GET | /v1/projects/{project_id}/sessions/{session_id}/attach | AttachSession | operator bearer |
| POST | /v1/projects/{project_id}/sessions/{session_id}/revoke | RevokeSession | operator bearer |
| POST | /v1/nodes/{id}/sessions/{session_id} | PostNodeSessionCallback | NSK bearer |
IssueSessionruns the issuance ReBAC check on the targeted Resource before any persistence write, enforces the per-Domain step-up policy, clamps the requested TTL to the per-Domain ceiling, admits the session against the per-Domain concurrency and rate budgets, then mints one Session and signs the canonical claim bytes through the per-Domain Signing Service key. The signed token is delivered exactly once, in the201response. That response carries nolistener_endpoint: the target Node picks the port when it binds the mediated listener, so the coordinate settles on the read projection afterwards rather than at issuance.ListSessionsreturns an issuance-ordered, metadata-only page of Session lifecycle projections, optionally narrowed by aggregatestatus,kind, oridentity_id. The session-read ReBAC check — the owning Project'sreadpermission, notact— runs before the persistence read; the projection never carries the signed token. Because the page is metadata-only, a Project read principal (an operator who observes the Project but holds noactauthority) may list its Sessions without being able to issue or revoke one. Every entry carries the persistedlast_active_at: a freshly issued Session reports its issuance instant, and each accepted activity callback advances it. The pagination cursor is opaque and HMAC-signed by the server, so a tampered cursor surfaces as400on the next call.GetSessionreturns the metadata-only projection of one Session under the same Projectreadgate and the same never-the-token contract. Both read paths serve thelistener_endpointthe target Node reported for the Session. The key is absent until atcpsession_startedcallback settles the coordinate, so a client that needs it polls the read until the key appears. Both read paths also serve the persistedlast_active_at: a freshly issued Session reports its issuance time, so the201body and the first re-read agree exactly.AttachSessionupgrades the request to a WebSocket and bridges the browser to the mediated SSH session: it re-runs the issuance-equivalent ReBAC check on the session's Resource before dialing anything, validates the Session isactiveandssh-kind, then dials thelistener_endpointthe target Node reported for the Session over the mesh and copies bytes bidirectionally. That reported coordinate is the only dial target: a live Session the Node has not reported for is refused409session_listener_pendingbefore the upgrade, so a client that gets it retries after the Node's next reconcile. This operation does not speak JSON — a successful upgrade answers101 Switching Protocolswith an opaque bidirectional byte stream; a non-upgrade request surfaces as426. The gateway's dial bound is a composition-root knob documented in the context reference.RevokeSessionruns the revoke ReBAC check before any write, then writes the revocation, appends the session identifier to the revocation denylist, and fans the revocation event out over the Signed Event Bus so the target plexd tears the listener down — all in one transaction. Re-revoking is idempotent (204, no additional rows).PostNodeSessionCallbackaccepts the per-kind activity callback from the target plexd and appends it to the session's append-only activity log, touching the last-active timestamp the idle-timeout sweeper reads. Atcpsession_startedrow may also carry thelistener_endpointthe Node bound, which the platform parses and settles onto the session row as its dial target; a repeatedsession_startedoverwrites the stored value, so the last report wins. The reported host must be the reporting Node's own mesh address — the platform dials that coordinate, so a target may not name an address it does not own. A coordinate on any other kind or activity, one whose host is not the reporter's mesh address, or one the parser otherwise refuses, is400invalid_session_activity.
Authentication
The five Project-scoped operations use the platform operator bearer scheme. The callback does not: like the heartbeat and endpoint-observation surfaces, it authenticates the Node against the per-Node Node Secret Key (NSK) plaintext supplied in the Authorization: Bearer header, refusing a revoked credential with 401 and cross-Node use of a leaked NSK with 403 (nsk_node_mismatch).
Error envelope
All error responses use the shared Problem envelope (application/problem+json). Every operation carries the 501 arm for a build whose Access surface is not provisioned. A step-up-gated issuance surfaces with code step_up_required so a client can branch without parsing prose; the per-state and per-gate code taxonomy is catalogued in the context reference.
Cross-references
../../contexts/access.md— the Session aggregate, the JWT claim set and verifier, the SessionPolicy snapshot, the issuance state machine, the audit contract, and the step-up enforcement contract../node-events.md— the mesh surfaces that share the per-Node NSK authentication seam with the callback../resources.md— the Resource lifecycle theresource#actissuance gate authorises against../index.md— platform-wide/v1HTTP surface map.../../../api/openapi/plexsphere-v1.yaml— authoritative OpenAPI contract; this doc is a map, not a duplicate.