Create Ssh Key
Register one OpenSSH public key for the caller.
The route answers 400 for a line the parser refuses, and 409 for a fingerprint the table already holds. The unique constraint is global, so the same key cannot name two accounts.
The route writes one ssh_key.added notification and queues one security.ssh_key_added mail in the same transaction as the row. A coding agent can register a key for the user, so the user must learn about the new key. Both carry the same text: a bell row alone reaches nobody on an account no person watches, and the mail leaves the console (design/EMAIL.md). An account that holds no verified address gets the notification alone, because the sink writes no row for an empty recipient.
api_login stores the caller's value. True means the key can also mint a bearer token through POST /auth/ssh-key, and the shared text says so. The console sends a checkbox that starts cleared, so a pasted key stays a git identity unless the person asks for more.
Registering a key is a mint, so the mint rule gates it. A key satisfies repo:* by construction (design/AUTH.md), so the route requires repo:*; api_login additionally requires the whole default set, because the key goes on to mint that set plus email:bind through POST /auth/ssh-key. Either refusal answers 403 scope_denied.
api_login also needs an ssh-ed25519 key, the one type POST /auth/ssh-key accepts. The route answers 400 for any other type, so the stored flag never promises a sign-in the login route refuses. Every other accepted type still registers as a git identity.
api_login then needs one more thing, because that mint carries email:bind and a bound address takes the account's console: the account holds a verified address, or the caller's own token carries email:bind. The second arm is the rule that a key-proved credential can register another key — only the two proof-based mints in :mod:xhostd.routes.registration grant that scope, so a token from POST /credentials or POST /tokens never holds it, and an agent with no address still rotates its own keys. A refusal answers 409 conflict, the POST /me/email-verifications shape: nothing about the caller's authorization is wrong, and the account is not ready.
The two scope checks run first, before any read of the account, so a caller that fails authorization learns nothing about account state.
This route carries NO protected-action gate. A gate would close the one path that lets an agent work without a secret in its context. The scope gate is not that gate: a general credential passes it, and only a narrowed one, which could not reach the repositories itself, is refused.
Request
- Base URL: https://api.xhostd.com (the document declares no server; this is the origin it was published from)
- URL: https://api.xhostd.com/ssh-keys
- Auth: none declared
Headers
Request body
Response
Successful Response
Changes
No recorded changes to this operation across all 1 revision of this API.