Skip to content

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

MethodPathOperation IDAuth
POST/v1/projects/{project_id}/sessionsIssueSessionoperator bearer
GET/v1/projects/{project_id}/sessionsListSessionsoperator bearer
GET/v1/projects/{project_id}/sessions/{session_id}GetSessionoperator bearer
GET/v1/projects/{project_id}/sessions/{session_id}/attachAttachSessionoperator bearer
POST/v1/projects/{project_id}/sessions/{session_id}/revokeRevokeSessionoperator bearer
POST/v1/nodes/{id}/sessions/{session_id}PostNodeSessionCallbackNSK bearer
  • IssueSession runs 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 the 201 response. That response carries no listener_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.
  • ListSessions returns an issuance-ordered, metadata-only page of Session lifecycle projections, optionally narrowed by aggregate status, kind, or identity_id. The session-read ReBAC check — the owning Project's read permission, not act — 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 no act authority) may list its Sessions without being able to issue or revoke one. Every entry carries the persisted last_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 as 400 on the next call.
  • GetSession returns the metadata-only projection of one Session under the same Project read gate and the same never-the-token contract. Both read paths serve the listener_endpoint the target Node reported for the Session. The key is absent until a tcpsession_started callback settles the coordinate, so a client that needs it polls the read until the key appears. Both read paths also serve the persisted last_active_at: a freshly issued Session reports its issuance time, so the 201 body and the first re-read agree exactly.
  • AttachSession upgrades 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 is active and ssh-kind, then dials the listener_endpoint the 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 refused 409 session_listener_pending before the upgrade, so a client that gets it retries after the Node's next reconcile. This operation does not speak JSON — a successful upgrade answers 101 Switching Protocols with an opaque bidirectional byte stream; a non-upgrade request surfaces as 426. The gateway's dial bound is a composition-root knob documented in the context reference.
  • RevokeSession runs 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).
  • PostNodeSessionCallback accepts 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. A tcp session_started row may also carry the listener_endpoint the Node bound, which the platform parses and settles onto the session row as its dial target; a repeated session_started overwrites 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, is 400 invalid_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 the resource#act issuance gate authorises against.
  • ./index.md — platform-wide /v1 HTTP surface map.
  • ../../../api/openapi/plexsphere-v1.yaml — authoritative OpenAPI contract; this doc is a map, not a duplicate.