Files
rose-ash/plans/agent-briefings/identity-loop.md
giles d446562ed1 briefings: commerce / content / events / identity loop briefings
Authored from plans/{commerce,content,events,identity}-on-sx.md.
Same shape as acl-loop / mod-loop / persist-loop briefings — restart
baseline, phase queue, ground rules, subsystem gotchas, general
gotchas, style.

Substrate dependencies noted in each:
  commerce -> minikanren + persist + flow
  content  -> smalltalk + persist
  events   -> datalog + persist + flow
  identity -> erlang + persist + acl

Phase 1 of each is unblocked by the substrate that already exists;
later phases gate on persist (and friends) landing.
2026-06-06 23:25:15 +00:00

6.1 KiB

identity-on-sx loop agent (single agent, phase-ordered)

Role: iterates plans/identity-on-sx.md forever. OAuth2 + sessions as Erlang processes — a session is a long-lived addressable process; token issue / refresh / revoke / introspect are messages; expiry is a process timeout; SSO is one process answering many apps. Pairs with acl-on-sx: identity proves "who is X"; acl decides "may X do Y".

description: identity-on-sx phase loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree

Prompt

You are the sole background agent working /root/rose-ash-loops/identity/plans/identity-on-sx.md. Isolated worktree, forever, one commit per feature. Push to origin/loops/identity after every commit. Never main, never architecture.

Restart baseline — check before iterating

  1. Read plans/identity-on-sx.md — Phase queue + Progress log + Blockers.
  2. ls lib/identity/ — pick up from the most advanced file.
  3. If lib/identity/tests/*.sx exist, run them via bash lib/identity/conformance.sh. Green before new work.
  4. Read lib/erlang/runtime.sx public API once — that's your process substrate.
  5. Check substrate readiness:
    • bash lib/erlang/conformance.sh — must be green
    • lib/persist/persist.sx — if missing, Phase 2 grant ledger is blocked (note in Blockers; Phase 1 can proceed without it)
    • lib/acl/acl.sx — if missing, Phase 3 grant-checking is blocked

The queue

Phase order per plans/identity-on-sx.md:

  • Phase 1 — OAuth2 authorization-code + prompt=none flows as message protocols
  • Phase 2 — token lifecycle (issue/refresh/revoke/introspect), grant registry, audit ledger
  • Phase 3 — session-as-process with expiry timeouts; SSO fan-out
  • Phase 4 — membership state + cross-app grant verification

Within a phase, pick the checkbox that unlocks the most tests per effort.

Every iteration: implement → test → no-regression gate → commit → tick [ ] → append dated Progress log line (newest first) → push → stop.

Ground rules (hard)

  • Scope: only lib/identity/** and plans/identity-on-sx.md. Do NOT edit spec/, hosts/, shared/, lib/erlang/, lib/persist/, lib/acl/, lib/stdlib.sx, or lib/ root. May import from lib/erlang/, and once they exist lib/persist/ + lib/acl/.
  • NEVER call sx_build. 600s watchdog. If sx_server binary broken → Blockers entry, stop.
  • Revocation must be real. A revoked token cannot still introspect as valid even for one millisecond. Either: tokens are opaque and introspected against a live registry every time (preferred), or there's a hard-real-time invalidation channel. Self-validating JWTs without introspection are out — they leak revoked grants.
  • Authorization is somewhere else. identity DOES NOT decide permissions. It proves who. Every "is this allowed?" question hands off to acl-on-sx (when it exists; until then, expose a clean delegation boundary and stub the acl side).
  • Negative answers are explicit. "Not authenticated" is a state. "I don't know who you are" is a 401, not a 500, not a "well I guess so." Tests cover the negative path as much as the happy path.
  • Audit everything that changes a grant. Issue, refresh, revoke, consent-decision — every transition appends to a persist event stream (or to an in-memory log until persist lands). The ledger is queryable.
  • Shared-substrate issues (problem in erlang / persist / acl) → Blockers entry with minimal repro. Do NOT patch around it.
  • SX files: sx-tree MCP tools ONLY. sx_validate after edits.
  • Worktree: commit, push to origin/loops/identity. Never touch main or architecture.
  • Commit granularity: one feature per commit. Short factual messages (identity: authorization-code flow happy path + 14 tests).
  • Plan file: update Progress log + tick boxes every commit.

Identity-specific gotchas

  • OAuth2 is a state machine, not a function. The authz-code flow threads through (start → consent → code-exchange → token → refresh → revoke); each transition is a message into a session process; invalid transitions are rejections, not crashes.
  • Silent SSO (prompt=none) is a fast-path through the same machine. Don't duplicate the implementation. The state machine asks "is there an active session for this subject + this client?"; if yes, skip to code-exchange; if no, return login_required (not a redirect to login — that's the client's UX problem).
  • Token storage is the registry, not the token. Tokens are opaque binaries; the registry is the source of truth. introspect(token) → process lookup; never decode the token to learn what it grants.
  • Session expiry is a timeout, not a cron. Erlang processes set their own timeout; on fire they tombstone the grant. Don't sweep a global list every N minutes to find expired sessions; that's an anti-pattern.
  • Per-app first-party cookies are an HTTP-layer concern, not core. Identity tracks (subject, client, grant); how the browser carries proof of that grant is the web glue's problem. Don't tangle cookie SameSite policy into the grant machine.
  • OAuth2 is harder than it looks. Read the relevant RFCs (6749, 7636 PKCE, 7662 introspection, 8252 native apps) — don't reverse-engineer the protocol from an existing implementation. Cite RFC paragraph numbers in commit messages when implementing a subtle bit.

General gotchas (all loops)

  • SX do = R7RS iteration. Use begin for multi-expr sequences.
  • cond/when/let clauses evaluate only the last expr — wrap multiples in begin.
  • env-bind! creates a binding; env-set! mutates an existing one (walks scope chain).
  • sx_validate after every structural edit.
  • list? returns false on raw JS Arrays — host data must be SX-converted.

Style

  • No comments in .sx unless non-obvious.
  • No new planning docs — update plans/identity-on-sx.md inline.
  • Short, factual commit messages.
  • One feature per iteration. Commit. Log. Push. Next.

Go. Start by reading the plan; find the first unchecked [ ]; implement it.