The Yard
The Yard is where ships are built and serviced: the web admin that holds accounts, projects, locks, and previews. Its founding rule is architectural — the Yard holds the objects, not the reasoning. Every judgement about the story (the write gate, blast radius, the two clocks) lives in the keel binary; the Yard stores, serves, and coordinates.
Starting it
Two containers: Postgres 16 (the database — source of truth for hosted
series) and the Yard itself on port 8000. On startup the Yard migrates
and seeds the admin account idempotently. The version in the header and footer of every
page is read from the same single source the TUI shows in its head — one number,
everywhere, in x.y.z form.
Signing in
username
admin · password mvpAdmins are de-facto editors: the same account signs into the web admin and the TUI.
Three doors, one account:
- The web admin — /admin/, the form above.
- The TUI’s editor login — the first thing keel shows: yard URL
and editor prefilled, masked password,
Entersigns in,Tabmoves between fields,F2drops to local dev mode (no Yard),Escquits. A stored session that still validates skips the screen — being signed in is the login. - The CLI —
keel login --url http://localhost:8000 --username admin --password …for scripts,keel logoutto return to dev mode.
Sessions are bearer tokens stored under keel’s home directory; the TUI validates
the session at launch and says who is holding the pen in its head bar
(◉ admin).
What the admin manages
The admin pages (KEEL · YARD branding, the site’s palette, a link back to the main site under the login box) expose the store itself:
| Series | the projects — slug, name, author |
| Objects | every scene, beat, arc, movement, entity, matter — with head revision |
| Revisions | the immutable history: author, message, hash, status |
| Locks | who holds what, since when — exclusivity is structural |
| Changesets | amendments and their pending counts |
| Tokens | issued sessions |
Previews — the book, in the browser
Render locally, view from the Yard:
The Yard stores one preview per series; re-publishing overwrites it. Uploading requires
a signed-in session and a real PDF (anything without a %PDF- header is
refused with a loud error); viewing is open on your local network. Until hosted series
arrive, your machine renders and the Yard displays — the honest local-first split.
The API
Interactive documentation lives at /api/docs. The surface, in families:
| POST /api/token | credentials → bearer token (the only unauthenticated write) |
| GET /api/version | the running version, no auth |
| /api/series | list and create projects |
| …/objects | create, read (any revision), checkout, checkin, unlock, log, locks — the same strict-RCS semantics as the local store, transaction for transaction |
| …/changesets | open, list, members, land, abandon — amendments |
| …/preview | upload the rendered PDF |
Every error body is machine-readable — {"error": …, "code": …}
with codes like locked (plus holder and since), stale_base (plus
head and base), pending_in_changeset — so the client maps failures without
parsing prose. The check-in path mirrors the Rust store exactly, down to the
“INTEGRITY VIOLATION” wording and the rule that revisions grow at
MAX(rev)+1 over retained history.
Clearly marked: coming next
- Store::Yard — the TUI reading and writing hosted series over this API, with a local read-through cache keyed by (object, revision); revisions are immutable, so the cache never goes stale and navigating leaves never re-downloads what it has seen.
- Project setup in the browser reaching the TUI — create the series here, open it there.
- The spine planner — tree and card forms for arcs, movements, and beats, for planning sessions; the TUI keeps full spine capability for write-time work (both surfaces, one store, same locks).
- Build-the-sample-novel button, hosted rendering, and the nightly export job with a tested restore — the go-live checklist items.