One notice channel, and notice settings — 2026-09-29¶
The user, on a rig toast reading "obsync merged concurrent edits to Both-mun1mwp3.md.": notices must be "sharp, timely, human readable, and actually give useful updates", and any choice with fixed answers is a setting reachable from Settings, the command palette and Obsidian's command line. This run proves 1.1.5's first step on two real desktops: every notice through one channel, the combined-edits notice named by title and device, the two settings, Recent, and the command line.
Setup¶
- Two isolated Obsidian 1.13.4 instances on macOS 27.0 (rig A, rig B), each
with its own
--user-data-dir, disposable vault and its ownHOME, driven through their DevTools ports. Obsidian binds its command-line socket at$HOME/.obsidian-cli.sockafter deleting whatever is there, so a rig launched under the desktop user's ownHOMEtakes that user's command line away from their own Obsidian; with aHOMEof its own, each rig binds a socket beside its profile and nothing else changes. - Server:
obsyncdbuilt from this branch (server code unchanged from 1.1.4), plain HTTP on the Mac's loopback. - Builds (
main.jsSHA-256): - 1.1.4 (
afbf7e7):19d3202059551d72f53f3f3a0deaf3eb159971604c1d9fb481f756ac56c28d0c; - this branch:
79dbe1a52a4b74bd399605d3ad04b558e1150d465c052895e7b1395aa2563f07(the last #269 check). Earlier builds of it ran the rest: runs 1 to 48f50d43e…, the sweepa01bafff…(two refusals and an empty Recent reworded), run 59f3b8a4d…(a notice without a kind is no longer held once drawn), run 6 and the first #269 check49bd35ea…(#269 for Show sync status); this one adds #269 for the Recent dialog. The commit's own build,226a0f4a103ea22fe41aa5930b013bd8bee169a02d5009ce1bb1b04a881a372d, differs from it only in the order of two methods of the Sync status dialog. - Load: two CPU burners (
node -e 'for(;;){}') during every typing run, beside the other lanes of the train on the same Mac.
Two devices typing into one note¶
Both rigs open one new note; rig A types at its end and rig B at the end of
its first line, one token every 200 ms, as trusted input. Every toast Obsidian
draws on each side is counted from the page itself (a MutationObserver on
.notice). Driver: cotype-notices.mjs in the lab.
Continuous typing merges once. At 1.1.4, 60 s of continuous typing gave
one merge per side, applied 11.7 s after the typing stopped, and one toast per
side: obsync merged concurrent edits to Both-r0base.md. -- the raw path with
its extension, an internal term, and no device. A merge waits while someone
types, so every run below types in three 8-second bursts 12 s apart: three
merges per side, one in each pause.
| Run | Build | Settings (set through) | Merges per side | Toasts A / B | Recent A / B | Sync |
|---|---|---|---|---|---|---|
| 0 | 1.1.4 | -- | 3 | 3 / 3 | -- | pass |
| 1 | branch | Everything useful, Once per note (defaults) | 3 | 1 / 1 | 3 / 3 | pass |
| 2 | branch | Every time (Settings on A, command palette on B) | 3 | 3 / 3 | 3 / 3 | pass |
| 3 | branch | Recent only (command line on both) | 3 | 0 / 0 | 3 / 3 | pass |
| 4 | branch | Only what needs me over Every time (palette on A, Settings on B; merges by command line) | 3 | 0 / 0 | 3 / 3 | pass |
| 5 | branch, 9f3b8a4d… |
defaults | 3 | 1 / 1 | 3 / 3 | pass |
| 6 | branch, 49bd35ea… |
defaults | 3 | 1 / 1 | 3 / 3 | pass |
The branch's toast: obsync: combined your edits to "Both-r1once" with Mac
F4MQ's. -- the note's title and the other rig's name. Each appeared the
moment its merge was applied, 18.6 to 19.7 s into a run (10.6 s after the
first burst ended; the merge's own wait, not the notice's). "Sync" is the
convergence check of every earlier typing run: the same bytes on both disks,
every typed token present, the fixed lines intact, no conflict copy.
The command line¶
The real obsidian-cli, run with each rig's HOME and from inside its vault;
the lab's wrapper refuses to run unless the socket is held by that rig's own
process.
obsync-private-sync:noticesprintedNotifications: Everything useful (level=everything)andCombined edits: Once per note (merges=once); withformat=json,{"level":"everything","merges":"once"};merges=offset it, and the data file then held"notices": {"level": "everything", "merges": "off"}withstorageVersion1.obsync-private-sync:recentprinted thirteen lines, newest first, in local time; withformat=json, thirteen objects of exactlytime(UTC),kind,note_title,device,text.obsync-private-sync:status format=jsonprintedstate,text,server,device(the name),has_vault_keyand four counts; no id.- Refusals:
level=loudprintedError: level takes everything or needs-me.(withformat=json,{"error":{"code":"unknown_value",...}}), andrecent limit=5refused the unknown flag. Obsidian's command line exits 0 even for these, so a program reads theerrorobject. obsidian helplists the three commands with their flags. A reload of the plugin re-registered all three with no warning.
Across versions¶
Rig B's data file held level=needs-me, merges=off when 1.1.4 was installed
over the branch: it loaded, stayed paired and idle, and two notes crossed in
about 1 s each way. 1.1.4's next save dropped the field; the branch, installed
again, read the defaults. A 1.1.4 data file, with no field, loaded on the
branch as the defaults.
Show sync status asked for twice (#269)¶
On rig A, Show sync status by command, again by command (the path a hotkey
takes), then a click on an "N more" toast while it showed. At 9f3b8a4d…:
one, two, then three Sync status dialogs, stacked; three Escapes to clear. At
49bd35ea… and again at 79dbe1a5…: one dialog after every request, one
Escape to clear. With Show recent sync activity opened over it, asking for
Show sync status again put it in front (Recent sync activity < Sync status
in page order), and the first Escape closed it, the second Recent. Show recent
sync activity, the same at 79dbe1a5…: asked for twice, one dialog; asked for
again under Show sync status, in front, closed first. No warning or error in
the console.
Whole-app sweep¶
Captured from both rig windows, then every dialog and the Settings window closed:
- The combined-edits toast on each rig, while up (above).
- A command from the palette under Only what needs me still answered, for
4 s:
obsync: notifications: Only what needs me; combined edits: Once per note. - Five notices raised at once through the channel (synthetic text) on rig A:
three on screen, the third slot still held by the fading palette answer, so
two plus
obsync: 3 more — see Recent in Show sync status.; clicking it opened Show sync status. Three toasts dismissed by a click left room: the next notice had a toast of its own. - Show sync status lists Recent under its table, newest first, each entry with its time and an Open button for its note; Show recent sync activity lists all of them. Settings shows Notifications last, with both dropdowns and a Show button for Recent.
- The status item stayed one 24 px icon,
obsync: idle, on both rigs. - Console: no warning or error from either rig through the whole sweep.
- Stacked: Show sync status opened a second time over itself when the driver clicked a fading "N more" toast twice. That is #269, fixed above; nothing else was stacked, stale or contradictory.
Not run here: a phone and Windows (phase 2 of this change, on the composed
train); a toast joined by a repeat, (2 times), which needs two merges of one
note within 8 s and is pinned by plugin/test/notice-channel.test.mjs.
Every notice with a kind (phase 2, 2026-09-30)¶
The second step gives every notice a kind and plain words. It also closes
Pair a new device once the approval lands, shows the alert icon while the
security warning stands, and counts a repeat instead of stacking it. The
same scenario ran on two builds, one after the other, on the rig above: two
isolated Obsidian instances, each with its own HOME, and one server on
loopback.
- Builds (
main.jsSHA-256): - before, the train at
38fedadd:cb6c119fb7a350f3ac456991ad3d2cc8b31ee8184ffb71d38407d78e6246fa9c; - after, this change:
7a0ddcffb74c0c54bf695a4a38ed33f9dae90dd21a086d520eab4d8a5c2ef235. An earlier build of it,6d32ef5a…, ran the same steps before Recent counted repeats. Its toasts matched, and it also ran "Room again" below. - The scenario,
live.shin the lab: - set up rig A, and write five notes;
- pair rig B, with the approval given on A;
- B's first sync;
- two typists in one note, in three 8-second bursts, beside two CPU burners;
- 20 presses of Sync now on A, 250 ms apart;
- the security warning standing on A;
- a full server;
- B revoked from A.
- Every toast, in every window of either rig, is counted from the page
(a
MutationObserveron.notice). A toast drawn is one count; words changed on a toast already up (a repeat joining it) are not. - The security warning needs a second recovery key on the server. The one
step not real is the server's
409 recovery_mismatchanswer to A's registration, which is SIMULATED IN THE PAGE. Then the real server answers A's next registration, which ends the warning. - The full server is real. Its blob volume declares 64 MiB with a 48 MiB
reserve. A writes a 24 MiB file and then a note, and the server answers
507.
Toasts drawn, rig A / rig B:
| Phase | Before | After | What changed |
|---|---|---|---|
| Set up | 2 / 0 | 2 / 0 | the words (below) |
| Pair | 0 / 1 | 1 / 1 | A says the approval, as the dialog closes |
| B's first sync | 1 / 0 | 1 / 0 | the words |
| Two typists | 1 / 1 | 1 / 1 | -- |
| 20 × Sync now | 20 / 0 | 2 / 0 | one toast counts the presses: "(16 times)", then a second one, "(4 times)", after the first had gone 4 s in |
| Security warning | 1 / 0 | 1 / 0 | the status item (below) |
| Full server | 0 / 0 | 0 / 0 | a desktop says it on the status item |
| B revoked | 0 / 0 | 0 / 0 | the same |
| Total | 25 / 2 | 8 / 2 |
A's twenty presses stacked twenty identical toasts before. After, the two toasts were never on screen together. The words, before and after:
- "Account created and this device enrolled." became "obsync: set up your server's vault; this device syncs with it."
- "Recovery phrase confirmed." became "obsync: recovery phrase confirmed."
- "This device is paired. The first sync is running." became "obsync: this device is paired, and its first sync is running."
- 'The new device, "Mac 9GTM", is paired: it holds the vault key now.' became 'obsync: "Mac B8CQ" is paired: it holds the vault key now.' The rigs name themselves at random.
Recent, read from each plugin at the end, newest first:
- Before: 3 lines on each rig, the three combines, one line each. Nothing else reached it: setup, pairing, the twenty answers and the security warning were drawn past the channel.
- After, rig A, 7 lines: the security warning; "nothing to send; this device is up to date (20 times)."; 'combined your edits to "Both-after" with Mac B8CQ's (3 times).'; '"Mac B8CQ" is paired…'; 'approved "Mac B8CQ"…'; "recovery phrase confirmed."; "set up your server's vault…".
- After, rig B, 2 lines: the combines "(3 times)", and "this device is paired, and its first sync is running."
No line in either Recent, or on any toast, carries a run of digits a pairing code could be.
Pair a new device, 1.5 s after Approve: before, OPEN. It stayed open until B had opened the vault key. After, closed. A said 'approved "Mac B8CQ": it finishes pairing by itself, and obsync tells you here when it has.' and, 4.0 s later, '"Mac B8CQ" is paired: it holds the vault key now.'
The status item while the security warning stood on A, with the warning toast on screen in both builds:
- Before, the check:
obsync: idle. The red crossed arrows beside it are not one of obsync's icons (ui/indicator.ts,ICONS).

- After, the alert:
obsync: idle — security warning: see Show sync status.


Once A's own key was registered, both read obsync: idle with the check, and
the warning's toast was gone.
Full server and revoked device, the same in both builds: A read obsync:
error — Your server is out of storage, so it refuses new changes. Free space
on the server or raise its quota. Sync resumes by itself. with the alert at
the first 507. B read obsync: error — This device was removed from your
server. … with the alert. A phone also says each once in a notice (#209),
which the phone runs of the train cover.
Room again (build 6d32ef5a…): the server restarted with room, nothing
pressed or edited. A sent the file the server had refused 105 s later: the
next walk of the vault, which comes every five minutes. That is why the
storage words proposed for the composed train end "then select Sync now"
rather than "Sync resumes by itself". In that run the server refused the
24 MiB file itself (three 507s) and then took the small note written after
it. The
note's success cleared the storage words, and A read syncing 1 file for the
90 s the run waited, with the refused file still unsent. That is reported
separately.
Runs: two of each. The first "before" run stopped when the toast watcher
waited on a window that had closed, and its full phase never met a refusal:
the server took the whole 24 MiB file, and nothing was written after it. The
watcher now gives up on a silent window after 3 s and saves as it goes, and
the full phase writes a note after the big file. The first run's dialog check
and Recent agree with the table. Every run ended with both rigs quit and the
server stopped, verified by rig.sh teardown.
Not run here: a phone and Windows, which the composed train's device runs cover. The security warning's server answer was simulated, as above.