Skip to content

Agents are the only end users

ADR-2026-0004: Agents are the only end users

Section titled “ADR-2026-0004: Agents are the only end users”

Citizenry shipped with two SvelteKit Pages projects:

  • apps/web - a user-facing surface scaffolded as a public landing / account page.
  • apps/admin-web - an operator-facing console for inspecting the deployment.

In practice, the users of a citizenry deployment are AI agents, not humans. Agents enroll, authenticate, and call the service through apps/api and apps/mcp. They never load HTML. The only humans who legitimately need a browser session are the operator running the deployment - and their needs (observe state, manage admin credentials, audit activity) are served by apps/admin-web, which authenticates against its own admin ID/password independent of the agent identity model.

apps/web therefore had no real consumer. Keeping it cost CI build time, a Pages project on every adopter’s Cloudflare account, a slot in the deploy matrix, and a piece of public surface area that invited contributors to design human user flows that the product does not have. Worse, its presence implied a user model - accounts, sessions, sign-in - that is actively at odds with the agent-native design and would mislead readers about who the system is for.

We need a clear, recorded decision so future contributors do not “complete” the empty user web or treat its absence as a gap.

  • Product surface honesty. End users are agents. A scaffolded human-user web app misrepresents the product and creates a magnet for off-strategy contributions.
  • Adopter deploy cost. Every Pages project a fork ships adds to Cloudflare resource count, deploy time, and the surface adopters have to reason about. Smaller is better when the project provides nothing.
  • Admin separation. Operators need an observability/control console. That console authenticates with its own admin credentials and is unrelated to agent identity - folding it into the “user web” story would have conflated two very different access models.
  • Reversibility. Removing an app that has no consumers is cheap and reversible (any future need can scaffold a fresh app); leaving it costs every deploy and every reader’s attention indefinitely.
  • Spec / documentation drift. Public docs and the layout sections of README.md / CLAUDE.md must reflect reality, or new contributors form a wrong mental model on first read.
  1. Keep apps/web as an empty scaffold. Reserve the slot in case a human-user surface emerges later.
  2. Repurpose apps/web as the admin console and retire apps/admin-web. Single SvelteKit Pages project covers everything.
  3. Delete apps/web; keep apps/admin-web as the ops-only console (chosen). Make explicit that agents use api/MCP and only operators use a browser.
1. Keep empty web2. Merge into one web3. Delete web, keep admin-web (chosen)
Honest about the product✗ implies a human user flow that doesn’t exist✗ same scaffold encourages human user features alongside admin✓ shape of the repo matches the access model
Adopter resource countone extra Pages project per forkreduced from two to one - but admin URL now public-lookingreduced from two to one; admin URL clearly operator-facing
CI costbuilds + deploys an empty app every runone build (and one project)one build (and one project)
Contribution magnet✗ “make the user web do something” PRspartial - invites mixing user and admin concerns✓ admin scope is unambiguous
Migration costnone today, debt laterrename + redirects + auth-model reworktrivial - remove app + cleanup CI / docs
Reversible if a human surface ever appearsn/a - already keptrequires re-splittingscaffold a fresh app from apps/admin-web as a template

We chose option 3: delete apps/web and keep apps/admin-web as the only browser-facing Pages app for citizenry itself (apps/docs continues to ship the public docs site, unchanged). The agent-facing surface remains apps/api + apps/mcp.

Concretely:

  • apps/web/ is removed from the repository.
  • @citizenry/web is removed from the build, the Pages deploy matrix, and the deploy-pages.sh dispatcher.
  • The architecture / “what gets deployed” tables in README.md, CLAUDE.md, and docs/deploy.md drop the citizenry-web row and reframe the layout as “admin console + API/MCP for agents.”
  • apps/admin-web is documented as an ops-only console authenticated by its own admin ID/password, distinct from the per-agent identity model.
  • Repository layout is honest about the product: agents talk to the service through api/MCP; humans (operators) use admin-web.
  • One fewer Pages project per fork → smaller adopter footprint, shorter deploy time, less to explain in docs/deploy.md.
  • Eliminates the contribution magnet for “complete the user web” PRs that would have pulled scope away from the agent-native design.
  • The admin/agent boundary is now visible in the file tree, not just in code comments.
  • Anyone with a fork that depended on the apps/web scaffold (none observed in the open-source flow today) has to remove their fork’s Pages project manually. Stale citizenry-web.pages.dev deployments on existing adopter accounts are left orphaned until the operator deletes them - there is no CI step that prunes Pages projects on removal.
  • The historic ADR-2026-0002 names apps/web alongside apps/admin-web as the example justifying Pages support in the deploy pipeline. That ADR is preserved unchanged as a historical record; this ADR narrows the example post-hoc. The decision in 0002 still holds - Pages support is required for admin-web and docs.
  • ADR-2026-0001 §Implementation mentions apps/web in PR-6 as the host for the public error-code catalog. That responsibility now belongs to apps/docs (or the docs site of the operator’s choice); the catalog content is unchanged.
  • No spec or wire-protocol change. apps/web had no API contract; removing it does not affect any token, JWKS, MCP, or admin-api route.
  • pnpm-lock.yaml regenerates on the next pnpm install to drop the apps/web workspace entry.

Implemented in this commit:

  1. Remove apps/web/ from the repository.
  2. Drop @citizenry/web from .github/workflows/build.yml (build step + artifact path).
  3. Drop web from the deploy-pages matrix and from the summary table in .github/workflows/deploy.yml.
  4. Remove the web) case from scripts/ci/deploy-pages.sh and the usage / error strings.
  5. Update README.md, CLAUDE.md, and docs/deploy.md to drop the web row from the layout / architecture / “what gets deployed” tables and to describe admin-web explicitly as an ops-only console.
  6. Land this ADR.
  • If a future need for a public, human-facing surface emerges (a marketing site, a landing page, a status page), scaffold a fresh app under apps/ and write a new ADR establishing its scope and audience. Do not resurrect the empty apps/web slot as a catch-all.
  • The error-code catalog (referenced by ADR-2026-0001 PR-6) now lives in apps/docs. When that PR lands, route /errors/[code] from apps/docs rather than apps/web.
  • Adopters with an existing fork should delete the orphaned citizenry-web Pages project from their Cloudflare account after pulling this change. We do not auto-prune Pages projects.