::ep0ch · architecture + the last four weeks
How ep0ch works
A BBS-style terminal door into a programmable outline, and the outline host it calls. One SQLite file per outline; people and agents read and write it through the same actions, always attributed.
26 days, 780 landings, one protocol number
§1 ::stack
Many clients, one outline host per machine
Every client is thin: it calls the host, parses with outline-core, and draws with a renderer that already exists. The host answers resolution, search, backlinks, views, drafts and revisions, so no client implements them again.
who calls in
You, at a terminal
Local, or ssh ep0ch straight into the door.
Claude Code agents
loki, kitty, cowboy, builders: each writes as its persona.
claude.ai and other apps
daddy, Codex, ChatGPT, Grok, over MCP with OAuth.
Readers on the web
Public notes, share links, the signed-in web client.
clients
the door ep0ch
The board people call into: screens built from tiles over one note surface. Runs as a session you attach to.
packages/door
Tree · Detail · Preview
The sysop console in Herdr: find any block and fix it.
packages/outliner
the CLI
ep0ch find, show, act, install --apply, doctor.
door + outliner
the Claude mod
Outline and workboard tools, reference links and Recent Mentions in every Claude session.
packages/claude-mod
MCP gateway
OAuth resource server; each outline's mcp access decides read, propose or full.
mcp.ep0ch.sh
the publisher
[publish::…] notes as pages, share links, the browser door.
pie.ep0ch.sh · my.ep0ch.sh
outline-core: both sides import it, nobody copies it
Pure code, no I/O: the property, header, link and code-fence grammars, the block record, the search matcher, PROTOCOL and the wire types. Change what it matches and the protocol number goes up.
the service
outline host, one per machine
Serves every ~/outlines/<name>.sqlite by name. It owns the answers, so clients only draw.
where it lives
float-2 · VPS
The outlines and their services: host, MCP gateway, publishers, Litestream.
laptop · MacBook
Its own host under launchd. float-2 reads it live, or from a read-only mirror while it sleeps; writes queue until it pulls.
float-box · the crew
Agents, builders and their worktrees, test runs, and the door behind ssh ep0ch.
Outlines are backed up continuously (Litestream), nightly (restic), and before every ep0ch install --apply.
The service and the door are about the same size; the shared core is small on purpose
Lines of TypeScript in tracked files, comments included, at 11e6722.
§2 ::consent · ADR 0005
Agents act, nodes mail, readers read
Three things reach an outline from outside its own clients. They look alike from far away, so they kept collapsing into one system; ADR 0005 gives each its own gate.
Agents act
- who
- an agent, as your delegate, now
- gate
ep0ch mcp access: none · read · propose · full- lift
- yolo, only while your own door says so
Nodes mail
- who
- this outline's own tosser, importing a packet
- gate
- its subscription notes:
[subscribe::…] - never
- writes another outline's blocks
Readers read
- who
- nobody writes; a reader only sees
- gate
[publish::…], share links, sign-in- never
- reads the MCP access level
§3 ::act
A key, a click and an agent's act take the same path
Input arrives
A key, a mouse click, a note's button, or
ep0ch actfrom an agent. Mouse and agents are first-class.One dispatcher per screen host
The key table names the action (PIE-1173: keys are data, and every hint is drawn from the same table). It also settles standing: a press acts fully as you; a run nobody pressed keeps the agent rules and never takes your focus or keys.
The action runs
An
ActionDeffrom the screen'sActionSet, with declared arguments andtile=in one grammar everywhere.layout changethrough the screen-layout module'sapply: one transaction, a new state or a refusal with its reasonnote writeto the outline host: the raw text lands first, then references resolve; anything unresolved is a warning with a did-you-meanRecorded
Author and actor id (
author: agent,loki/claude-code@float-box), a new revision, undo, an event on the change feed.Every client redraws
Door tiles, Tree and Detail, MCP readers and the web: each follows the feed, and an agent's change is said on screen.
Capture over interpretation: saving a note never depends on every reference, property or component being valid. A refused action keeps your text in front of you, with why and what to do.
§4 ::grammar · UI-GRAMMAR.md
Inside the door: block, tile, container, screen
- 1block: content in the outline. Data, never UI. Written in Blockdown: Markdown-adjacent plus
[key::value],((id)),[[page]],::component. - 2tile: a UI leaf that shows one thing (a note, a query, a terminal, a tree). Its kind comes from one registry; extensions register the same way.
- 3container: split, tabs, columns, dock or flow, each with a policy (locked, accepts, opens-into, agents).
- 4screen: a screen spec on the desk. Board, river, brief and welcome are all specs, plain data a note can hold.
- 5host layer: your drawer, kept across every screen; a tile put in it travels with you.
- 6wire: a tile's select, ⏎ and alt routes to other tiles. As of the one-desk wave, wires point at tile ids, not paths.
"Pane" isn't a door word: it means Herdr's or tmux's box.
§5 ::identity · doctrine candidate, Oct 22
ep0ch is a postal system: names are addresses, ids are residents
[[page]]
An address
A name you can write before anyone lives there: a reserved lot. It may dangle; it's looked up whenever someone moves in.
((block id))
A resident
Identity, not a place. It has to exist, and it keeps pointing at the same block wherever that block moves.
tiles
The trailer park
The path is the lot, the tile id is the registration, and wires are mail to the trailer, never the lot.
The test for any new primitive: is this an address or a resident? Wires, embeds, held readers and anything stored should be addressed to residents.
§6 ::git log
Recent changes: about 30 merges a day, and the door takes most of them
First-parent commits on main by author date, through Oct 21 (Oct 22 had 5 by 3:26 AM ET). Squash merges until Oct 16, merge commits since.
Non-merge commits; one commit touching two packages counts for both.
§7 ::shipped · Oct 18–22
The last five days, by what changed for you
- 1Oct 18A tile can hold a note from another outline, and notes link across outlines (PIE-1056). Select, open and alt-open each get their own wire.
- 2Oct 19The laptop reaches its outline over outbound HTTPS (PIE-1058). Runbooks run step by step from a note. Calm frames and x-ray: a tile says what it can do on demand. Big outlines scroll in 2 ms frames, down from 37 ms.
- 3Oct 20Deploys reach the Mac over the machine link (PIE-1124). An agent edit that cannot apply is refused like a stale file edit, so the proposal pile stops. The power bar forgives typos.
- 4Oct 21Your status bar is a note, with ::button items. Scripts live as named fences beside their button, and a button runs a sequence with one undo. ADR 0007: Sources joins the comment box as one attached surface. Typing :: completes from the component schema.
- 5Oct 22Keys are said once, from the tables that bind them (PIE-1173). Ranked search reads its Jev key again (PIE-1195). Review now names the hydra (#846). The one-desk wave (#850): tiles by id across screen, drawer and groups; a press acts as you.
§8 ::now · the one-desk wave
One button found five bugs; the fix is four owners
neat, a status-bar button that arranges tiles, was ruled a probe, not a feature: every failure gets a ticket, and the script is never bent around a bug. Its builder hit the same "is this an agent?" check in three places, so loki paused for root cause. The answer: four questions, one owner each.
which tile?
Address
Tile ids and keys follow tiles through groups and the drawer.
✓ PIE-1192 · #849who acts?
Standing
A pressed script acts with your standing, even while you type.
✓ PIE-1186 · #847where does it land?
Placement
An open swaps the note in place; the replaced one is born a spine.
◐ PIE-1187 · doingwhat does it accept?
Contract
Allowed values and argument rules checked in one place.
○ PIE-1194 · queuedThe nvim test (Evan, Oct 22): "would nvim bother locking this down?" The invocation carries the authority. The line is who started it, not whether you're typing.
§9 ::workboard · pie
582 done, 227 queued, and only five in hand at once
Validate means merged, with a named verification gap that an agent owns. Done needs linked proof.
§10 ::crew
How a change gets made
--merge, so each session's commits stay in historyep0ch install --apply, outlines backed up first§11 ::next
Up next: search that says how sure it is
| Ticket | What | Stage |
|---|---|---|
| PIE-1187 | Placement: an open lands in place, no make-then-fold reflow | doing |
| PIE-1194 | Contract: the action contract checked in one place | queued |
| PIE-1196 | A capability that fell back says so in the status bar ("ranking off") | queued |
| PIE-1197–1200 | Jev search: evals, confidence, structured candidates, deep search | queued |
| PIE-1181 | A tile something opened into stays lit; door events as rule triggers | queued |
| PIE-1201 | A hydra lint in CI: does this diff decide what an owner already decides? | unprioritized |
| ADR 0007 | The glass and the strip, the two surfaces still to build | designed |
Sources
- git:
float-ritual-stack/ep0chmain at 11e6722 (Oct 22, 3:26 AM ET), first-parent history Sep 27 – Oct 22; line counts from tracked .ts/.tsx files. - Repo docs: README.md, AGENTS.md, packages/door/docs/UI-GRAMMAR.md (TL;DR, glossary), docs/adr/0005-agents-act-nodes-mail.md, docs/adr/0007-surfaces-about-the-current-thing.md, packages/outline-core/src/protocol.ts, packages/door/src/mcp-gateway.ts.
- pie (live, float-2): How this workboard works (d5b3e557), Infrastructure hub (50fe932d), Evan's briefing (aca85598), stage counts by
outline_query, PIE-1173 to PIE-1201. - gurgle (live, float-2): Kitty's watch, Oct 22 (65f6d8de); neat is a probe, not a feature (87a98a6e); ep0ch is a postal system (a896f749); Week check, Oct 15–21 (74598ca4).