~bigbes/sr-ht-spec

ref: faa9163e9645bd737244305c48a2b13788dbca10 sr-ht-spec/cmd/specsrht/token_test.go -rw-r--r-- 2.3 KiB
8219ede1 — Eugene Blikh 13 days ago
feat(web,service): the owner mints and revokes agent tokens in a browser

Issuing a credential required SSH to the host, which made the remote
agent write plane unusable from anywhere else: to hand an agent a token
the owner had to be at the machine. /tokens is that page — list, mint,
revoke — behind the same owner-only gate and same-origin guard as
approve/reject.

The mint is owner-only, and that rule is what revocation depends on: an
agent allowed to mint would survive having its own credential revoked by
issuing itself another, and "revoke the token" is the entire incident
response this design has. An agent asking for the page gets 403 rather
than the read plane's login redirect — it is authenticated already, so
bouncing it to meta would answer a question it did not ask.

The plaintext is rendered in the response to the POST rather than after a
redirect. A redirect would either drop the secret or carry it in a URL,
where it lands in history and in every proxy log on the way; the cost is
that a reload re-submits and mints a second token, which is one click to
revoke on that same page, whereas a lost token is not recoverable.

service.IssueAgentToken/ListAgentTokens/RevokeAgentToken hold the ACL and
the mint, and `specsrht token` now goes through them too, so the CLI and
the page cannot drift into two ideas of what issuing a token is.

spec-ejq.3
90eb06ec — Eugene Blikh 13 days ago
feat(cmd): agent tokens and host-side proposals get admin commands

Two entry points were missing, and both left a deployment unable to do
the thing it exists for.

`token create|list|revoke` — db/ has had the whole agent-token lifecycle
since Phase 1, but nothing called it: no CLI, no page. A fresh instance
therefore had no credential for the agent write plane, which refuses an
anonymous caller by design, and the only way to mint one was an operator
hand-writing an INSERT with a sha256 hash. The plaintext is printed once
and never logged; only its hash is stored, and the listing deliberately
omits the hash so nobody mistakes it for the credential.

`doc propose ~owner/space <file>...` — the two agent write surfaces are
remote and so need a bearer token. When the operator and the documents
are already on the host, that token is ceremony: the process can open
Postgres and the bare repositories directly, so it constructs the agent
principal itself rather than resolving one from an agent_token row.
Provenance is not waived — --agent and --session are recorded exactly as
a remote agent's are, so `git log` cannot tell the two apart, and neither
can a reviewer. It calls service.Propose, so If-Match, the branch cut,
the trailers and the auto-merge gate stay spelled once.

Flags parse before, after and between the positionals: Go's flag package
stops at the first non-flag, which would make `doc propose ~bigbes/rfcs
spec.md --title x` drop --title and fail one layer down complaining
about a missing title rather than the flag it ignored.

spec-rsb, spec-ovo