Skip to content

fix(agentgit): register the member's SSH key on every BBS login, not just at signup - #131

Merged
ralyodio merged 1 commit into
mainfrom
worktree-agentgit-key-sync
Sep 24, 2026
Merged

ralyodio merged 1 commit into
mainfrom
worktree-agentgit-key-sync

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

What

When a member joins the BBS, their SSH key is supposed to be registered on their
git.profullstack.com account — "your BBS SSH key is your git key", as the pod
banner says. It only ever happened once, at first provisioning.

provisionGit bailed out before reaching EnsureKey:

created, password, err := a.forgejo.EnsureUser(u.Name, u.Email)
if !created { return }   // <- every later login stops here
... a.forgejo.EnsureKey(...)  // unreachable after the first time

So a member who deleted their key on AgentGit never got it back, and members who
joined before AgentGit captured keys were never backfilled — even though the
login path at main.go:519 calls provisionGit on every visit specifically to
do that, and its comment says so. The web verify path's comment ("key is added
on next BBS login", main.go:1217) described behaviour that could not happen.

Change

Key registration now runs on every provisionGit call that carries a session
key. EnsureKey was already idempotent — it GETs the account's keys and
compares key material ignoring the trailing comment — so re-running it costs one
GET when nothing changed, and it re-adds a removed key, picks up a rotated one,
and backfills old accounts. The welcome email stays gated on created, since
the one-time password is empty for an account that already existed.

Two things that would have made the every-login path unreliable:

  • Key title was the constant "agentbbs". Forgejo 422s a duplicate title,
    so a member who rotated their BBS key would have had the new key silently
    dropped. The title now carries a short fingerprint (agentbbs <fp>), so
    distinct keys coexist and the same key stays stable across logins.
  • EnsureKey mapped every 422 to "already exists" and returned nil. That
    hid genuine rejections forever, which matters much more now the call is on the
    hot path. Only "has been used" bodies are swallowed; anything else surfaces
    with Forgejo's own reason so it reaches the logs.

Tests

  • TestProvisionGitRegistersKeyForExistingAccount — the regression guard: with
    an account that already exists, the key must still be POSTed. Fails against
    the old early-return.
  • TestGitKeyTitleIsPerKey — distinct keys get distinct titles, the same key is
    stable, unparseable input degrades to "agentbbs".
  • TestEnsureKeyTreatsAlreadyUsed422AsBenign / TestEnsureKeySurfacesRejecting422
    — the two sides of the 422 split.

gofmt, go vet and the full go test ./... suite are clean.

Not covered here

Pushing over SSH to AgentGit is still broken for an unrelated reason: Forgejo
advertises ssh://git@git.profullstack.com:2222/…, but 2222 is not reachable —
it times out, while 587 on the same host answers refused, so the live ufw
ruleset has no ACCEPT for it despite setup.sh carrying
ufw allow "${FORGEJO_SSH_PORT}/tcp" since f210b42. Port 22 there is agentbbs
itself, so the pod banner's git push → git@git.profullstack.com fails with
fatal: protocol error: bad line length character: Requ (the BBS's "Requires an
active PTY" parsed as git protocol). Needs a look on the box; HTTPS remotes work
today.

🤖 Generated with Claude Code

…just at signup

provisionGit returned early when the Forgejo account already existed, so
EnsureKey only ever ran during first provisioning. A member who deleted their
key on git.profullstack.com never got it back: every later BBS login hit the
`if !created { return }` and skipped key registration entirely. The comment on
the web verify path ("key is added on next BBS login") was describing behaviour
that could not happen.

Key registration now runs on every provisionGit call that carries a session
key. EnsureKey was already idempotent — it GETs the account's keys and compares
key material ignoring the comment — so re-running it is free when nothing
changed, and it re-adds a removed key, picks up a rotated one, and backfills
members who joined before AgentGit captured keys. The welcome email stays gated
on `created`, since the one-time password is only meaningful for a new account.

Two things that would have made this unreliable in the new every-login path:

- The key title was the constant "agentbbs". Forgejo rejects a duplicate title
  with 422, so a member who rotated their BBS key would have had the new one
  silently dropped. The title now carries a short fingerprint, so distinct keys
  coexist and the same key stays stable across logins.

- EnsureKey mapped *every* 422 to "already exists" and returned nil. That hid
  genuine rejections forever, which matters far more now the call is on the hot
  path. Only "has been used" bodies are swallowed; a rejected key surfaces with
  Forgejo's own reason so it lands in the logs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

10 finding(s)

HIGH/CRITICAL: 7 | LOW: 3

Severity Rule Location
HIGH secret-generic-credential deploy/ergo/ircd.yaml:162
HIGH secret-generic-credential deploy/ergo/ircd.yaml:221
HIGH secret-generic-credential deploy/ergo/ircd.yaml:638
HIGH secret-generic-credential deploy/ergo/ircd.yaml:787
HIGH secret-database-url deploy/ergo/ircd.yaml:893
HIGH secret-generic-credential deploy/ergo/ircd.yaml:1025
HIGH secret-generic-credential deploy/ergo/ircd.yaml:1033
LOW secret-generic-credential deploy/ergo/ircd.yaml:772
LOW tls-verification-disabled internal/mail/mail.go:109
LOW secret-generic-credential internal/mailu/mailu_test.go:44

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 0789371 into main Sep 24, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant