dispatch#67 fix#2 needed a read-only CI credential and hit a hard wall:
Gitea's own token-creation and admin-user endpoints reject token auth
outright -- POST /users/{u}/tokens returns 'auth required', POST
/admin/users returns 'token does not have at least one of required
scope(s), required=[write:admin]' -- for ANY existing token, by design. An
existing token cannot mint another token or create an account; the only
way through either endpoint is real basic auth as an actual site admin.
That is exactly what a sandboxed agent session cannot safely hold.
internal/adminbootstrap: the core bootstrap, HTTP-only, fully testable
against a fake transport (5 tests, no network). Given real admin
username/password, it creates the target account (or resets its password
if it already exists -- deliberately unconditional, gated behind a
required ConfirmServiceAccount flag since that reset is only safe for a
dedicated bot account), mints it a scoped token by authenticating AS that
account (Gitea requires this specifically -- an admin cannot mint on
another account's behalf), grants it read-only collaborator access on
named repos, and stores the result via a SecretStore -- never returning
the token or either password to the caller.
internal/localtools: the MCP tool wrapper (mint_service_account_token) and
the real op-CLI-backed SecretStore (token passed via stdin, never argv,
per the estate's own secret-handling rule). Deliberately its OWN package,
not internal/tools -- this capability is far too dangerous to risk it ever
being wired into RegisterAll and reachable from the shared HTTP gitea-mcp
server, even behind OIDC.
internal/mcp/stdio.go: a stdio JSON-RPC transport (one message per line,
standard MCP stdio framing) for the existing registry.Tool interface --
the only transport gitea-mcp had before this was HTTP (server.go), which a
sandboxed session can reach but a real admin password never should be
piped through.
cmd/gitea-admin-mcp: the binary. Meant to be spawned by a LOCAL Claude Code
session (.mcp.json stdio server) or run by hand, via 'op run --env-file
~/.op-env' so admin credentials arrive just-in-time and never touch a
sandboxed session.
11 new tests total, all passing under -race. Nothing in cmd/gitea-mcp (the
shared HTTP server) changed.