Two systems with deliberately different jobs.
GESH authenticates roots and devices, stores immutable encrypted event blobs, exposes an incremental feed, and deletes events after every active device has collected them. Skald owns encryption, key management, serialization, path validation, conflict resolution and local durability.
One QR code carries two halves of trust.
The relay supplies a short-lived, single-use enrollment code. Skald appends the vault content key to the URI fragment locally. URI fragments are not sent in HTTP requests, so the QR code can contain both halves while GESH only receives one.
The pairing URI
Relay URL + enrollment code. GESH knows this half.
Appended locally by Skald. GESH never receives this fragment.
device token, root authority token and content key are kept in OS-protected secret storage, not beside Markdown that may be copied or committed.
Gets its own device credential and imports the content key.
Root token
The authority credential. It enrolls and revokes devices. Daily syncing does not need it.
Device token
A per-device sync credential. Revoking one device does not force every other device to re-pair.
Pull first. Acknowledge only after disk.
The ordering is intentionally strict because acknowledgements are destructive. Once every active peer has acknowledged an event, GESH may erase it.
Page from the persisted opaque cursor, skip this device's own events, download ciphertext.
Decrypt, fully revalidate, merge operations, then write notes or staged attachments to disk.
Only after the changes are durably committed. Never ack merely because a blob downloaded.
Diff the current vault against agreed state, encrypt new events, record only what successfully shipped.
Events are encrypted frames, not remote database rows.
A fresh 96-bit nonce seals each event. Notes travel as operations in the JSON header. Binary attachments travel as raw body bytes so files do not pay base64's roughly one-third size penalty.
delta / snapshot: header only
blob: one binary attachment in the body
{
"v": 1,
"kind": "delta",
"device": "desktop_…",
"ops": [
{
"op": "put",
"path": "Projects/Skald.md",
"rev": 4,
"hash": "…",
"content": "…"
}
]
}Paths are hostile input after decrypt
Every path is validated again before it becomes a filename. Anything escaping the vault, hiding in .skald/, or using reserved platform names is refused.
Attachments are atomic
Incoming files are staged under .skald/ and published by rename so readers never see a half-written attachment.
Converge automatically. Keep the loser.
Merge decisions use each path's logical revision first, then device id as the deterministic tiebreak. Wall-clock time is not trusted for ordering.
Different local content
Wins deterministically
So conflict resolution can be automatic without pretending the losing human edit never existed. Deletion also carries a logical clock, preventing stale events from resurrecting deleted notes.
The feed is not an eternal backup.
GESH deliberately garbage-collects. An old device can miss events after it has been offline beyond retention, so Skald uses full-vault snapshot events when provisioning and before handing out a pairing code.
A lost device can lose access without resetting the household.
Each paired device has its own credential. The root authority can list and revoke a single device; every other device credential remains valid. A revoked credential receives authorization failure and Skald stops automatic retries instead of hammering the relay forever.
Failures should degrade, not silently corrupt.
The sync engine treats several network and data failures differently because pretending every error is retryable is how distributed systems become haunted.
401
Stop automatic syncing. A revoked or invalid credential will not heal itself on a timer.
429
Honor Retry-After. Do not spin or mint fresh pairing codes to evade lockout.
Bad encrypted event
Warn and skip rather than wedge the entire feed forever.
Oversize attachment
Name it in sync status and continue syncing everything else. Current Skald does not chunk files across events.
The portable half is intentionally boring.
Skald keeps protocol, cryptography, pairing, payload validation and merge logic under src-shared/ using web-platform primitives. Desktop-specific code mainly supplies storage and OS secret handling, making the same sync model reusable by mobile.