Skip to content
bflo.sh

ADR-0006: The public API is read-only and unauthenticated; no admin panel

ADR-0006 — Accepted 2026-07-05. Part of the platform's decision record.

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

  1. 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.
  2. 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/DELETE on public resources; no login. Unpublished (DRAFT/SCHEDULED) items return 404, never 403 — existence is never disclosed (ADR-0008 supplies the gate).
  • Follow-up: contract-owner keeps the OpenAPI document read-only and adds the guard rule; backend-api implements 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/delete operations on public paths — ddev contract-lint fails 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/SCHEDULED path returns 404 with a problem+json body identical to a truly-absent path (no existence leak).