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.

post/ssh-keys

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

authorizationstring nullable

Request body

public_keystring required
labelstring nullable
api_loginboolean

Response

Successful Response

idstring uuid required
labelstring nullable required
algostring required
fingerprintstring required
created_atstring date-time required
last_used_atstring date-time nullable required
api_loginboolean required

Changes

No recorded changes to this operation across all 1 revision of this API.