Skip to content

feat(auth): sign in with OrangeCat — recognition, never authority - #73

Merged
catomean merged 1 commit into
mainfrom
feat/solon-oc-login
Aug 13, 2026
Merged

feat(auth): sign in with OrangeCat — recognition, never authority#73
catomean merged 1 commit into
mainfrom
feat/solon-oc-login

Conversation

@catomean

Copy link
Copy Markdown
Collaborator

What

Solon's first and only login: "Sign in with OrangeCat" (NextAuth v5, single orangecat OIDC provider, identity scopes only). Completes the deferred OC1 Solon-side item — the OC client has been registered and the secrets staged on the box since orangecat#661.

Doctrine (what is deliberately absent)

  • No passwords, no registration. Membership is granted by a governance vote (or the documented operator bootstrap) — never by a signup form. A password would be a weaker credential than the Bitcoin signature that carries all actual authority.
  • No adapter, no auth tables. The session JWT carries only the OrangeCat actor id; membership is resolved fresh from members.oc_actor_id on every request, so a 30-day token can never outlive a roster change.
  • No middleware. Everything stays public — transparency is the product. A session adds recognition only, and the middleware-strangles-HMAC-receivers bug class structurally cannot occur.
  • Anonymous OrangeCat accounts rejected at the signIn gate (a governance identity must be attributable); /auth/error explains it honestly, and the gate is pinned by unit tests.

Surfaces

  • Nav sign-in control (hidden when the OAuth pair is unset; SessionProvider keeps all marketing pages static — verified in build output)
  • /account: OC identity (name/email/actor id) + live membership status, with honest copy for observers
  • scripts/add-member.ts --oc-actor <uuid> links a human member to their OrangeCat identity (humans only; agents are recognized by API key)
  • .env.example created (README referenced it but it never existed)

Verification

  • npm run verify green: lint, typecheck, design:check, 33 tests (4 new pinning the anonymous gate)
  • npm run build green; static pages stayed static (○), /account + /auth/error + auth route dynamic (ƒ)
  • AUTH_SECRET staged in /opt/solon/shared/.env; post-merge I'll verify the live OAuth dance end-to-end (signin → orangecat.ch/oauth/authorize with client_id=solon)

🤖 Generated with Claude Code

Solon gets its first and only login: NextAuth v5 with a single orangecat
OIDC provider (identity scopes only), mirroring FleetCrown's proven
config (client_secret_post, PKCE+state). Deliberately absent, as doctrine:

- no passwords, no registration — membership is granted by vote (or the
  documented operator bootstrap), never by a signup form
- no adapter, no auth tables — the JWT carries the OrangeCat actor id;
  membership is resolved fresh from members.oc_actor_id per request so a
  30-day token can never outlive a roster change
- no middleware — every page and API stays public; a session only adds
  recognition (the middleware-strangles-receivers class cannot exist here)
- anonymous OrangeCat accounts are rejected at the door (governance
  identities must be attributable); /auth/error explains why, and the
  gate is pinned by unit tests

Surfaces: nav sign-in control (hidden when the OAuth pair is unset;
SessionProvider keeps marketing pages static), /account showing OC
identity + live membership status, add-member.ts --oc-actor to link a
human member to their OrangeCat identity.

OC side has been ready since orangecat#661 (client registered, secrets
staged in /opt/solon/shared/.env); AUTH_SECRET staged alongside.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@catomean
catomean merged commit 8b3339f into main Aug 13, 2026
2 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