Adapted from the repo's docs/adr/0006-read-only-public-api-no-admin-panel.md — Part of Internals: the platform's design set, published as content on the platform it describes.
- Status: Accepted
- Date: 2026-07-05
- Deciders: contract-owner, lead
- Tiers affected: contract, backend, frontend
- Related: ADR-0005 (files are the authoring
surface), ADR-0007,
ADR-0008 (gating decides visibility),
../master-blueprint.md§1, §5.3, §8
Context
Content is authored out-of-band as files (ADR-0005); the platform's job is to read that content and serve it to a public reader front-end. A blog has no logged-in users and no write surface. Deciding this now bounds the entire contract surface (only reads) and the Http layer's security posture (unauthenticated, rate-limited) before either is built — and keeps a future auth/write surface from being bolted on informally, since that carries real breaking and security weight.
Decision
We will publish a read-only, unauthenticated public API: only GET operations on public
resources (articles, sections, tags, health, and phase-2 search/authors). There is no admin
panel and no write/auth surface. Any future write or authenticated read (e.g. draft preview)
is a new contract major version plus a dedicated auth ADR — never an incremental addition.
Alternatives considered
- CMS / admin UI — the conventional way to manage a blog. Rejected as a product decision (blueprint §1.2): authoring is file-based and out-of-band, so an admin UI would duplicate the editor and contradict ADR-0005.
- Authenticated write endpoints now — future-proofs for editing via the API. Rejected: adds auth, authorization, and mutation semantics no current use case needs; any such surface is deferred to its own major + auth ADR so it gets deliberate security review.
Consequences
- Easier: the contract stays small and cacheable; the Http layer needs no auth stack, only per-IP rate limiting and CORS to the front-end origin; every response is safely cacheable.
- Harder / now forbidden: no
POST/PUT/PATCH/DELETEon public resources; no login. Unpublished (DRAFT/SCHEDULED) items return404, never403— existence is never disclosed (ADR-0008 supplies the gate). - Follow-up:
contract-ownerkeeps the OpenAPI document read-only and adds the guard rule;backend-apiimplements only read routes + the RFC 9457 renderer; a write/auth surface, if ever wanted, starts as a new ADR.
Compliance
- Spectral rule (candidate): a ruleset assertion that the OpenAPI document declares no
post/put/patch/deleteoperations on public paths —ddev contract-lintfails if one appears. - Review gate: any PR introducing an admin UI, a write path, or an auth middleware on a public route is rejected pending a new major + auth ADR.
- Behaviour test: requesting a known
DRAFT/SCHEDULEDpath returns404with aproblem+jsonbody identical to a truly-absent path (no existence leak).