Storyteller 1: a story is a log
Storyteller is our app-design track. The app: people write stories together,
one passage at a time, each author owning their words. Baby Rooms taught the
plumbing; Storyteller teaches the thinking — because in P2P, your real
design surface is not the database schema or the API. It is the op schema:
the vocabulary of things a user can say into their log.
Design the ops before anything else
A first pass for Storyteller:
story_create : { storyId, title, opening }
passage : { storyId, parent, text } // parent = passage id it continues
react : { target, kind } // ❤ a passage
retract : { target } // tombstone my own op
profile : { nick, bio }
Run every candidate op through four questions:
- Is it self-contained? An op is verified alone, possibly years later,
out of order. passage carries its storyId even though "context"
feels redundant — there is no session to remember it for you.
- Does it reference or contain? Reference by id (
parent,target),
never embed copies. The fold stitches the graph.
- Can the fold converge? Two writers continue the same passage — is that
a conflict? No: store both, and the story branches. What looked like
a merge problem becomes the product's best feature. This mindset shift —
conflicts into structure — is the single most useful P2P design habit.
- What happens on abuse? Anyone can append
story_createwith your
story's id. First-writer-wins on the id, everyone else's ignored
(sticky names) — decided in the schema, not
patched later.
Version from day one
{ v: 1, type: 'passage', ts: 1712345678901, payload: { … } }
Logs are forever — you cannot migrate other people's cores. The v byte is
how readers three schema versions from now still parse your 2024 ops. Ignore
unknown types and unknown versions politely; peers running old clients are a
permanent fact of life.
Next: several people, one story, and who holds the pen —
Storyteller 2.