1# Delegated endpoint access23Delegated access lets a sandboxed worker obtain narrowly bounded service access without4giving that worker a person's browser session, a machine credential, or the granting5machine's endpoint key.67## Actors and authority89- The **requester** owns a fresh, temporary Ed25519 endpoint key and creates a signed service10 request. Its public request is safe to hand to the granting party; its private key is not.11- The **granting party** is a human or automated process operating an ordinary enrolled12 machine whose single-use enrollment ticket carried the `service_grants:issue` scope. The13 server stores that scope on the resulting revocable machine credential.14- The **control plane** verifies both signatures, enforces the grant scope and account15 permissions, rejects replays, and issues an endpoint-bound tunnel session.16- The **service daemon** still verifies the resulting one-use transport capability before17 connecting to its local loopback target.1819Grant scope is delegated at enrollment instead of inferred from possession of any machine20credential. The dashboard must explicitly include it when minting the registration ticket.21Revoking the resulting machine credential removes grant sessions issued by it.2223## Signed handoff2425The requester runs:2627```sh28devsite --json access request postgres \29 --request /private/request.json \30 --key /private/requester.key31```3233The server separately requires schema version 1. The request signature covers a domain34separator, request id, service keyword, requester endpoint id, and expiry. The command35creates new files rather than overwriting paths and keeps both private by default. Only the36request JSON crosses the sandbox boundary.3738The granting party first resolves and plans:3940```sh41devsite --json access resolve postgres42devsite --json access grant --request /inbox/request.json --plan43```4445Resolution searches only owned services and accepted service shares visible to the granting46account.47An exact unique name is selected automatically. Ambiguous matches require an explicit48`--resource res_…`. The plan verifies the requester signature and reports the exact49resource, owner, requester endpoint, request id, and expiry without issuing a grant.5051After user or policy approval, the granting party applies the same intent:5253```sh54devsite --json access grant --request /inbox/request.json \55 --approved-plan dsp_…56```5758The granting machine's persistent `devsite-endpoint.key` signs the request id, selected59resource, requester endpoint, and grant expiry. The server also verifies that the signed request's60keyword still resolves to that resource. It rejects expired requests, grants longer than 1561minutes, duplicate request ids, credentials without grant scope, and resources outside the62granting account's current access. The `dsp_…` token is signed by the control plane over the63granting credential, request id and keyword, resource, endpoint, request expiry, and grant64expiry. Apply requires it, preventing a request file or selected target from being swapped65after approval.6667The granting party returns only `result.grant` and `result.server`. The requester then runs:6869```sh70devsite --server SERVER --json access connect GRANT \71 --key /private/requester.key72```7374The connector recreates the requested endpoint from that private key, validates that the75grant is delegated and bound to the same endpoint, and emits resident NDJSON lifecycle76events. It refuses non-loopback listeners.7778## Expiry and revocation7980Requests expire within 10 minutes and request ids are single-use at issuance. Grant81sessions expire within 15 minutes and may mint one capability per TCP connection during82that window. A granting credential has a bounded number of simultaneous active grant83sessions. Credential revocation deletes those sessions and prevents further capabilities84from being minted. A one-use transport capability already minted immediately before85revocation remains limited by its own short expiry and nonce.8687## Harness-neutral integration8889The canonical interface is the CLI JSON contract and the portable90[`devsite-cli` skill](../skills/devsite-cli/SKILL.md). A harness can execute those commands91directly, expose them through its own tool API, or use the bundled local MCP adapter.9293The adapter follows MCP `2026-07-28`:9495- clients use `server/discover`, not `initialize`;96- every request carries97 `_meta.io.modelcontextprotocol/protocolVersion` and98 `_meta.io.modelcontextprotocol/clientCapabilities`;99- each response is complete and self-describing; and100- the adapter stores no negotiated client, authorization, initialization, or call-order101 state.102103The long-lived stdio process is only a transport. Killing and restarting it does not lose a104logical session because there is no logical session to lose. The MCP plugin manifest is a105convenience package for clients that understand that format, not a requirement on the model106or harness.107108See the current official MCP [discovery](https://modelcontextprotocol.io/specification/draft/server/discover),109[tools](https://modelcontextprotocol.io/specification/draft/server/tools), and110[stdio transport](https://modelcontextprotocol.io/specification/draft/basic/transports/stdio)111specifications.