The site's live rooms and their sync with files on disk. The main addition is map rooms: a JSON file under the site's data/ can be opened as a live Y.Map whose keys merge independently, beside the existing text rooms. The rest follows other work: the HTTP handlers left room_sync.rs for the site's API, the crate was renamed from lichen-cms, children and the environment go through lichen's sandbox and lichen-env, and a lichen-yrs framing test was fixed.
AST-verified changes
crates/lichen-cms/src/sync/*renamed tocrates/lichen-site/src/sync/*with the crate rename (PER-SITE-PROCESS P2c-1, d7c4a668). Run id not recorded: "Rename move check with rn6 at f3e7470b..d7c4a668: ok (4365 items, 113 renamed, 1874 carried, 0 visibility)" (lichen-builder.md 2322); adversary "d7c4a668 equal=true 0-err 113-renamed 1874-carried 0-vis". Allowed:lichen_cmsrewritten tolichen_site, and rustfmt rewraps.sync/mod.rsrenamed tosync.rs, one of OPERATOR-REVIEW-FIXES U1's 19foo/mod.rsrenames (f6a10c32). Run id not recorded: "3004 items, 48 moved, 69 carried, 19 renamed, 2 visibility as mapped, 233 module declarations" (lichen-builder.md 2087); adversary notify-abab368a, same counts.- The room handlers (snapshot, save, recovery, the websocket) left
room_sync.rsforapi/live.rsin item 6's api-layer move (7601a57c); the added half is in #35. Run FUHHLT: "VERIFY OK (ast 3035 items/146 moved/25 visibility/3690 comments; routes 130=130 ...)"; adversary run eMUcJc VERIFY OK. Allowed: path rewrites intocrate::api, member visibility.
Roughly 130 of this PR's 1,267 changed lines are code these moves carried verbatim, nearly all of it the handlers leaving room_sync.rs. In git diff -M, git pairs the renamed files, so their unchanged lines are not counted.
On this forge the 6 renamed files here show as a whole deletion and a whole addition, so its diff reads 12,169 changed lines; with rename detection (git diff -M full/12-iroh full/13-rooms) it is 1,267.
- Map rooms (provisional;
site.openYmapin lichen-ts): adata/**/*.jsonfile is a liveY.Map(sync/map_room.rs). JSON objects are maps at any depth and merge per key; arrays and plain values are atomic. The file on disk is canonical JSON, as JavaScript writes it, so a browser and the server write the same bytes. A room's kind is fixed by its path (RoomKindinrooms.rs), so one file never has two rooms. The resync actor handles both kinds: a map room is saved on every sweep (it has no explicit save), merges per key against the file, keeps a conflicting part underconflicts/, and a write underdata/rebuilds nothing. - Room routes:
room_sync.rskeeps the room logic (open a snapshot, admit a socket, recover an incompatible generation) for both kinds, and its handlers are now in the site's API. JSON field names are snake_case (item 6, convention 6c). - Smaller changes:
git_recovery.rskeeps aborting a rebase only for an older lichen's interrupted pull (now crate-private). It andthreeway_merge.rsrun git as sandbox children (lichen_flat_file::sandbox::command, SANDBOX-UNIFY). Environment reads go throughlichen-env. lichen-yrs/src/conn.rs: the framing property test now allows exactly one panic, yrs's debug assertion that a client id fits in 53 bits, and fails on any other. A second test holds that a message of a few bytes naming billions of entries is refused without reserving them (yrs 0.27.4 bounds it; 0.27.2 aborted the process).
Files (crates/lichen-site/src/sync/ unless named)
map_room.rs— new: the JSON ↔Y.Mapmapping, the canonical form and the per-key merge. Read this first.rooms.rs—RoomKind; map rooms seeded from their file;content_offor either kind.resync.rs— the actor's save and reconcile for both kinds.room_sync.rs— room logic for both kinds; the handlers left for the API.git_recovery.rs,threeway_merge.rs,../sync.rs— the small changes above.crates/lichen-yrs/src/conn.rs— the framing properties (inline tests).
Lives elsewhere
- The room routes (
api/live.rs): #35. A peer's room exchange, now served from the site process's own iroh endpoint (the front server's bridge is gone): #28. docs/SITE-SYNC-MACHINERY.mdanddocs/SITE-CONCURRENCY-MODEL.md, which these modules cite, and lichen-ts'sopenYmap, are in the base commit.- Tests (
sync/tests/, the map-room tests, e2e) are in the base commit.
Category: Live rooms and their sync with git
Size: 8 files, +1,047/−220
Rounds: round 0 at stack ae42e3c0, unchanged at 29a7c15b and 6b650b2a; round 1 at b66397c0 (FULL-SITE-PROCESS): only lichen-yrs/src/conn.rs's inline test module gains a test, that a room message naming billions of entries is refused without reserving them (yrs 0.27.4; Cargo.lock is in the base commit). Read this round in its interdiff: https://tangled.org/notplants-bot.bsky.social/lichen.page.october/pulls/29/round/1/interdiff
Plans and ledger: map rooms (2026-09-26, 406b2f27; provisional), OPERATOR-REVIEW-FOLLOWUPS-2026-09-30.md (item 6: the api-layer move, the one URL scheme, conventions 6c), PER-SITE-PROCESS-2026-10-09.md (P2c-1), OPERATOR-REVIEW-FIXES-2026-10-07.md (U1), CHILD-ENV-CLEAR-2026-10-08.md (E2), SANDBOX-UNIFY-2026-10-09.md (U1); lichen-builder.md 1891, 2087, 2322.
Inline #[cfg(test)] modules: sync/rooms.rs (base_pointer_tests, restart_tests, ydoc_properties) and lichen-yrs/src/conn.rs (wire_properties, test). Four more files point to a test file with #[path].
Bucket moves: none against round 1. Split out of round 1's "iroh, sync and rooms": the rooms are the site's and not iroh's.
Round 1: #9 (iroh, sync and rooms) covered room_sync.rs, git_recovery.rs, threeway_merge.rs and conn.rs from 0b92cb61; map_room.rs, rooms.rs, resync.rs and sync.rs are new in this range.