::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.

Read from main at 11e6722 and the live pie and gurgle outlines, .

26 days, 780 landings, one protocol number

780
landed on main
Sep 27 – Oct 22 (#6 → #850)
270
landed Oct 15–21
busiest day: 59, Oct 18
582
roadmap items done
227 queued · 90 in validate
256
wire PROTOCOL
schema v7 · no version negotiation

§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.

keys · clicks · act · MCP tools · page views

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

unix socket · requests + change events · ping checks PROTOCOL 256

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.

one shared vocabulary on both ends of the socket

the service

outline host, one per machine

Serves every ~/outlines/<name>.sqlite by name. It owns the answers, so clients only draw.

blocks + propertieslinks + backlinksqueries + facetssearch (Jev ranking)drafts + draft.patchrevisions + undoproposalscomment threadsattributionchange feedEchoNet tosser
machine link (outbound HTTPS) · ssh forward · netmail queue for an away machine

where it lives

float-2 · VPS

The outlines and their services: host, MCP gateway, publishers, Litestream.

piegurglerotfieldpie-holeecho-*

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-hubsysops-logreadwise

float-box · the crew

Agents, builders and their worktrees, test runs, and the door behind ssh ep0ch.

lokibuildersbox-test

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

outliner121k source · 94k tests
door100k source · 71k tests
outline-core14k source · 4k tests
claude-mod7k source · 4k tests

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

Shared: how a change is recorded (attribution, [from::], revisions, undo). Never shared: what permits it.

§3 ::act

A key, a click and an agent's act take the same path

  1. Input arrives

    A key, a mouse click, a note's button, or ep0ch act from an agent. Mouse and agents are first-class.

  2. 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.

  3. The action runs

    An ActionDef from the screen's ActionSet, with declared arguments and tile= in one grammar everywhere.

    layout changethrough the screen-layout module's apply: one transaction, a new state or a refusal with its reason
    note writeto the outline host: the raw text lands first, then references resolve; anything unresolved is a warning with a did-you-mean
  4. Recorded

    Author and actor id (author: agent, loki/claude-code@float-box), a new revision, undo, an event on the change feed.

  5. 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

1 outline ▾ Workboard ▸ Build logs ▾ Roadmap hub PIE-1187 ● PIE-1189 PIE-1194 ▸ Inbox ▸ Your status bar select → 2 2 reader · PIE-1187 An open swaps the note into its tile in place [work-stage::doing] The note it replaced is born a spine beside it. 3 links ← backlinks4 → links2 ▲ your drawer · claude │ shell · travels with you desk │ pie@float-2 │ ▲ claude +2 │ ^K 1 2 3 4 5 6
  • 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

  1. 1Oct 18
    A 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.
  2. 2Oct 19
    The 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.
  3. 3Oct 20
    Deploys 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.
  4. 4Oct 21
    Your 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.
  5. 5Oct 22
    Keys 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 · #849

who acts?

Standing

A pressed script acts with your standing, even while you type.

✓ PIE-1186 · #847

where does it land?

Placement

An open swaps the note in place; the replaced one is born a spine.

◐ PIE-1187 · doing

what does it accept?

Contract

Allowed values and argument rules checked in one place.

○ PIE-1194 · queued

The 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

unprioritized→queued→doing→review→validate→done

Validate means merged, with a named verification gap that an agent owns. Done needs linked proof.

§10 ::crew

How a change gets made

TicketPIE-n with a Parts-it-uses table, a "New (glue only)" row and a Sketch
Builderits own worktree, test-first, a kitchen-sink demo
box-testwhole suites on float-box, never on float-2
ReviewCodex second opinion, bot reviews, the hydra check
Merge--merge, so each session's commits stay in history
Deployloki runs ep0ch install --apply, outlines backed up first
Validate → donereal-app proof, then Evan's daily use
ddaddyclaude.ai · eyes in the sky, writes in gurgle
Llokideployer and coordinator for ep0ch
kkittyruns the gurgle: watch logs, tidy passes
ccowboyrent-due work, across devices
bbuildersone ticket or slice each, end to end

§11 ::next

Up next: search that says how sure it is

TicketWhatStage
PIE-1187Placement: an open lands in place, no make-then-fold reflowdoing
PIE-1194Contract: the action contract checked in one placequeued
PIE-1196A capability that fell back says so in the status bar ("ranking off")queued
PIE-1197–1200Jev search: evals, confidence, structured candidates, deep searchqueued
PIE-1181A tile something opened into stays lit; door events as rule triggersqueued
PIE-1201A hydra lint in CI: does this diff decide what an owner already decides?unprioritized
ADR 0007The glass and the strip, the two surfaces still to builddesigned
Sources
  • git: float-ritual-stack/ep0ch main 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).