1# Keys and endpoint identities23dev.site uses Ed25519 keys for three jobs: identifying Iroh endpoints, proving that a4request came from the endpoint it names, and signing control-plane authorization. A5public endpoint id is safe to share. Its matching private key is what proves possession.67## Key inventory89| Key | Lifetime and storage | What it proves |10| --- | --- | --- |11| Machine endpoint key | Persistent; `devsite-endpoint.key` in the dev.site config directory | The enrolled machine and its daemon are the same endpoint |12| Machine endpoint public key | Persistent; `devsite-endpoint.pub` and the endpoint id registered with the server | Where to address the daemon and which key capabilities name as their audience |13| Ordinary connector key | Ephemeral; process memory only | Which client redeemed a `dst_…` ticket and opened the Iroh connection |14| Delegated requester key | Temporary; a caller-selected file such as `requester.key` | Which endpoint signed the request and may use the resulting grant |15| Control-plane signing key | Persistent operator secret; environment secret or `capability_signing.key` | That a plan or stream capability was issued by this control plane |16| Pinned control-plane public key | Persistent; stored in the machine's private `config.json` | Which control-plane signatures the daemon will trust |1718All are 32-byte Ed25519 private keys or their public halves. A displayed Iroh endpoint id19is the encoded public key, not a separate secret identifier.2021## Persistent machine endpoint identity2223The CLI creates the machine endpoint key during first login or daemon startup and keeps it24across restarts:2526```text27dev.site config directory/28├── devsite-endpoint.key private, raw 32 bytes29├── devsite-endpoint.pub public endpoint id, text30└── config.json private machine credential + pinned server public key31```3233Set `DEVSITE_HOME` to choose the config directory explicitly. Otherwise the CLI uses the34operating system's application config location. On Unix, private files are created with35mode `0600`; the public `.pub` file does not need private permissions.3637During enrollment, the CLI signs a domain-separated endpoint-proof message. The server38checks that proof before binding the returned machine credential to the public endpoint id.39Daemon registration repeats a possession proof. A scoped granting party also uses this40same key to sign the chosen resource, requester endpoint, request id, and grant expiry.4142Capabilities sent to this daemon name its public endpoint key as the audience. The daemon43rejects a capability issued for another endpoint even if every other claim is valid.4445### Legacy files in version 0.6.04647Version 0.6.0 does not read or move `identity.key` and `identity.pub`. Start version 0.5.148once before the upgrade if these legacy files remain.4950Version 0.5.1 checks only these exact names in the resolved dev.site config directory.51It does not scan the current directory, home directory, SSH directory, or filesystem for52similar names.5354Version 0.5.1 moves a legacy file only when the target file does not exist:5556```text57identity.key → devsite-endpoint.key58identity.pub → devsite-endpoint.pub59```6061An existing `devsite-endpoint.key` or `devsite-endpoint.pub` takes priority. Version 0.6.062does not read or move `identity.key` or `identity.pub`.6364## Client endpoint keys6566For ordinary `devsite connect dst_…`, the CLI creates a fresh Iroh key in memory. The67public half is supplied when the ticket is redeemed, the resulting `dss_…` session is68bound to it, and every per-stream capability repeats that binding. The daemon compares the69capability's client key with the authenticated remote endpoint from the QUIC connection.70A copied session or capability is therefore insufficient without the private key.7172The key disappears when the connector process exits. A new process cannot resume that73session, even if it has copied the `dss_…` ticket.7475## Delegated requester keys7677`devsite access request` creates a temporary requester key in a caller-selected file:7879```sh80devsite access request postgres \81 --request request.json \82 --key requester.key83```8485Both paths are created without overwriting existing files and use private permissions on86Unix. The request JSON contains the public endpoint id and a signature over the request id,87service keyword, endpoint id, and expiry. Only `request.json` should cross the sandbox or88approval boundary. `requester.key` stays with the requester and is later supplied to89`devsite access connect`.9091The key file itself does not expire, but every request and resulting session does. Delete92it after the request and any granted connection are finished. If the key leaks while a93grant is active, revoke the issuing machine credential or underlying service access and94stop the connector; waiting for the grant's 15-minute maximum lifetime also bounds the95exposure.9697## Control-plane signing key9899The control plane has one Ed25519 signing key shared by two authorization formats:100101- `dsp_…` service-grant approval plans; and102- one-stream service capabilities.103104Production can supply the private key through `DEVSITE_SIGNING_KEY`. A local deployment105without that setting loads or creates `capability_signing.key` in the server state106directory with private permissions. The private key must never be distributed to clients107or daemons.108109At login, the CLI fetches the public half from `/api/pubkey` and pins it in `config.json`.110The daemon refuses to start without a valid pinned key and verifies every capability111against it. `devsite doctor` compares the currently published server key with the pinned112value and reports a change rather than silently trusting it.113114Losing the private signing key prevents the server from minting artifacts that existing115daemons trust. Compromise permits forging plans and capabilities until clients move to a116new trust root. Back it up as an operator secret and treat rotation as an explicit117re-enrollment or re-pinning event, not a routine file replacement.118119## What compromise permits120121| Compromised material | Consequence | Recovery |122| --- | --- | --- |123| Machine endpoint private key only | Endpoint impersonation is possible, but control-plane API calls still require the machine credential | Revoke the named machine credential, remove the key, and enroll a new endpoint |124| Machine credential only | Authenticated control-plane actions within its scopes; it cannot satisfy endpoint proofs without the key | Revoke the named machine credential and log in again |125| Machine key and credential | Full authority of that enrolled machine and its scopes | Revoke immediately and re-enroll with a new key |126| Ephemeral connector key | Useful only with its still-live bound session or capability | Stop the connector/delete its session; ordinary key vanishes with the process |127| Delegated requester key | Use of a still-live grant bound to that requester | Stop/revoke the grant path and delete the key |128| Control-plane signing key | Forged approval plans and service capabilities | Rotate the trust root and re-pin or re-enroll every daemon |129130A `dsm_…` machine ticket is a bearer ticket, not a cryptographic key, even though the CLI131stores it beside key material. See [Tickets](tickets.md) for its lifecycle.