Exorcise sweep: rewrite connective dashes in synckit comments and docs
Last of the uncommitted sweep. Not my edits; verified rather than authored.
Comment and doc prose only, across both crates: Cargo.toml dependency
rationale, the synckit-config module and spec headers, store/config.rs, the
architecture doc, and the root README. No code changes.
synckit-client 514 tests passed, synckit-config 13 passed, 0 failed.
Note for future sweeps: comment prose is now exempt from the dash rule
(ruled 2026-07-27, recorded in brand.md and both exorcise skip lists), so
this class of edit should not recur. Existing conversions stay; there is no
revert and no further tree-wide pass.
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
7 files changed,
+18 insertions,
-18 deletions
| 6 |
6 |
|
independently. Two standalone crates, no root workspace (each builds on its own,
|
| 7 |
7 |
|
matching the ecosystem convention):
|
| 8 |
8 |
|
|
| 9 |
|
- |
- **`synckit-client`** — the sync SDK: transport, crypto, the declarative
|
|
9 |
+ |
- **`synckit-client`**, the sync SDK: transport, crypto, the declarative
|
| 10 |
10 |
|
`SyncStore` engine over SQLite. Internal; consumed by path or git dependency,
|
| 11 |
11 |
|
never published to crates.io.
|
| 12 |
|
- |
- **`synckit-config`** — a local key/value settings store with per-key sync
|
|
12 |
+ |
- **`synckit-config`**, a local key/value settings store with per-key sync
|
| 13 |
13 |
|
postures. The store half of portable config; usable with no network, so a
|
| 14 |
14 |
|
no-sync consumer (a TUI remembering a theme) links it without the SDK. The
|
| 15 |
15 |
|
sync adapter that turns a `ConfigSpec` into a `SyncTable` lives in
|
| 16 |
16 |
|
`synckit-client` behind its `store` feature.
|
| 17 |
17 |
|
|
| 18 |
18 |
|
The synckit **server** still lives in the MNW server. Splitting it into a
|
| 19 |
|
- |
standalone service on its own VPS is a separate, later effort — see the wiki
|
|
19 |
+ |
standalone service on its own VPS is a separate, later effort. See the wiki
|
| 20 |
20 |
|
note `mnw-strategy-synckit-vps-separation`, triggered by the first external
|
| 21 |
21 |
|
developer.
|
| 26 |
26 |
|
rand = "0.10"
|
| 27 |
27 |
|
base64 = "0.22"
|
| 28 |
28 |
|
zeroize = "1"
|
| 29 |
|
- |
# X25519 ECDH — the only asymmetric primitive in the crate, used solely by the
|
|
29 |
+ |
# X25519 ECDH: the only asymmetric primitive in the crate, used solely by the
|
| 30 |
30 |
|
# group-key layer (`identity.rs`) to seal a Group Content Key to a member's public
|
| 31 |
31 |
|
# key. Pure-Rust (curve25519-dalek), no aws-lc/ring. Deliberately just the curve op:
|
| 32 |
32 |
|
# the GCK itself is sealed with the XChaCha20-Poly1305 above, not a sealed-box crate.
|
| 39 |
39 |
|
|
| 40 |
40 |
|
# HTTP
|
| 41 |
41 |
|
# rustls-no-provider: rustls TLS with the OS-native trust store (rustls-platform-verifier),
|
| 42 |
|
- |
# no bundled crypto provider — the consuming app installs one process default (ring).
|
|
42 |
+ |
# no bundled crypto provider, so the consuming app installs one process default (ring).
|
| 43 |
43 |
|
reqwest = { version = "0.13", default-features = false, features = ["json", "rustls-no-provider", "stream", "form", "charset", "http2", "system-proxy"] }
|
| 44 |
44 |
|
bytes = "1"
|
| 45 |
45 |
|
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "fs", "io-util"] }
|
| 84 |
84 |
|
# Self dev-dependency: turns on the `testing` feature for this crate's own
|
| 85 |
85 |
|
# unit/integration test builds without leaking it into a consumer's default set.
|
| 86 |
86 |
|
# `default-features = false` so it only ADDS `testing` to whatever the test run
|
| 87 |
|
- |
# already selected — under a plain `cargo test` the package still builds with its
|
|
87 |
+ |
# already selected. Under a plain `cargo test` the package still builds with its
|
| 88 |
88 |
|
# own defaults (keychain + store), but `cargo test --no-default-features` can turn
|
| 89 |
89 |
|
# keychain OFF (making `keystore::store_key` the no-op stub) to run the keychain-
|
| 90 |
90 |
|
# free rotation orchestration test below.
|
| 545 |
545 |
|
SQLite file** (given a path or a connection factory). Under WAL, which all three
|
| 546 |
546 |
|
apps already use, the engine's short read/apply transactions coexist with the
|
| 547 |
547 |
|
app's own pool on the same file. The app's domain code is untouched; the sync
|
| 548 |
|
- |
engine simply becomes another connection that reads `sync_changelog` and writes
|
|
548 |
+ |
engine becomes another connection that reads `sync_changelog` and writes
|
| 549 |
549 |
|
the domain tables under the `applying_remote` guard. This also matches audiofiles
|
| 550 |
550 |
|
exactly and removes GO/BB's async-sync code entirely.
|
| 551 |
551 |
|
|
| 791 |
791 |
|
|
| 792 |
792 |
|
### What GO proved about the design
|
| 793 |
793 |
|
|
| 794 |
|
- |
- **Confirmed: credential exclusion collapses.** The single most bespoke piece of
|
|
794 |
+ |
- **Confirmed: credential exclusion collapses.** The single most hand-rolled piece of
|
| 795 |
795 |
|
GO's apply layer (`apply_email_account_upsert`, its own INSERT-with-`password=''`
|
| 796 |
796 |
|
and 16-column ON CONFLICT) is fully expressed by `preserve_local` +
|
| 797 |
797 |
|
`insert_defaults`. No per-table override function survives. This was the strongest
|
| 4 |
4 |
|
//! does not sync. A [`ConfigStore`] is a SQLite key/value table an app reads and
|
| 5 |
5 |
|
//! writes with no network in sight; a [`ConfigSpec`] declares which of those keys
|
| 6 |
6 |
|
//! may cross the sync boundary. `synckit-client` turns a spec into a
|
| 7 |
|
- |
//! `SyncTable`, but nothing here depends on it, so a no-sync consumer — the Alloy
|
| 8 |
|
- |
//! console remembering a theme — links this crate and not the sync runtime.
|
|
7 |
+ |
//! `SyncTable`, but nothing here depends on it, so a no-sync consumer (the Alloy
|
|
8 |
+ |
//! console remembering a theme) links this crate and not the sync runtime.
|
| 9 |
9 |
|
//!
|
| 10 |
10 |
|
//! ## Why this is its own crate, and why it is SQLite
|
| 11 |
11 |
|
//!
|
| 16 |
16 |
|
//! different keys must both survive (row-level HLC merge, which a whole-file blob
|
| 17 |
17 |
|
//! cannot do), and some keys must never sync at all (see posture). Rows are what
|
| 18 |
18 |
|
//! buy both. A text file would need a projection layer and a sidecar for the
|
| 19 |
|
- |
//! per-key merge metadata — SQLite by the back door. Design note:
|
|
19 |
+ |
//! per-key merge metadata: SQLite by the back door. Design note:
|
| 20 |
20 |
|
//! `synckit-config-design` in the wiki.
|
| 21 |
21 |
|
//!
|
| 22 |
22 |
|
//! ## Posture, and why it fails closed
|
| 13 |
13 |
|
///
|
| 14 |
14 |
|
/// The default is [`Local`](Posture::Local), and so is the answer for any key a
|
| 15 |
15 |
|
/// spec does not mention: see [`ConfigSpec::posture`]. That is the whole safety
|
| 16 |
|
- |
/// property — a key crosses the sync boundary only when someone wrote down that
|
|
16 |
+ |
/// property: a key crosses the sync boundary only when someone wrote down that
|
| 17 |
17 |
|
/// it should.
|
| 18 |
18 |
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
| 19 |
19 |
|
pub enum Posture {
|
| 46 |
46 |
|
/// The keys a config knows, and their postures.
|
| 47 |
47 |
|
///
|
| 48 |
48 |
|
/// Declared as a `const` per app: the table it lives in, and every key with its
|
| 49 |
|
- |
/// posture. Only [`Synced`](Posture::Synced) keys need listing for correctness —
|
| 50 |
|
- |
/// an unlisted key reads as [`Local`](Posture::Local) — but listing the local
|
|
49 |
+ |
/// posture. Only [`Synced`](Posture::Synced) keys need listing for correctness
|
|
50 |
+ |
/// (an unlisted key reads as [`Local`](Posture::Local)), but listing the local
|
| 51 |
51 |
|
/// ones too documents the surface and lets a test assert the set is complete.
|
| 52 |
52 |
|
#[derive(Debug, Clone, Copy)]
|
| 53 |
53 |
|
pub struct ConfigSpec {
|
| 80 |
80 |
|
///
|
| 81 |
81 |
|
/// **Fail-closed: an undeclared key is [`Local`](Posture::Local).** This is
|
| 82 |
82 |
|
/// the rule the whole sync boundary rests on. A key nobody classified never
|
| 83 |
|
- |
/// replicates, so forgetting to name a new local path does not leak it — the
|
|
83 |
+ |
/// replicates, so forgetting to name a new local path does not leak it. The
|
| 84 |
84 |
|
/// failure of omission keeps data home rather than sending it.
|
| 85 |
85 |
|
pub fn posture(&self, key: &str) -> Posture {
|
| 86 |
86 |
|
self.keys
|
| 1 |
1 |
|
//! The local key/value store: a SQLite table an app reads and writes.
|
| 2 |
2 |
|
//!
|
| 3 |
|
- |
//! No network, no posture in sight — posture governs what *syncs*, and this is
|
|
3 |
+ |
//! No network, no posture in sight. Posture governs what *syncs*, and this is
|
| 4 |
4 |
|
//! the store that exists whether or not an app syncs at all. It creates one
|
| 5 |
5 |
|
//! table, `key TEXT PRIMARY KEY, value TEXT NOT NULL`, and offers get/set/unset
|
| 6 |
6 |
|
//! over it. A no-sync consumer (the Alloy console) uses only this; a syncing one
|
| 5 |
5 |
|
//! terms. An app calls [`config_sync_table`] to fold config into its
|
| 6 |
6 |
|
//! [`SyncSchema`] and [`install_policy`] once at open to seed the filter the
|
| 7 |
7 |
|
//! generated trigger reads. After that the app's `ConfigStore` writes replicate
|
| 8 |
|
- |
//! transparently — the consumer never learns whether sync is wired.
|
|
8 |
+ |
//! transparently: the consumer never learns whether sync is wired.
|
| 9 |
9 |
|
//!
|
| 10 |
10 |
|
//! ## The posture filter, and why it is an allowlist
|
| 11 |
11 |
|
//!
|
| 18 |
18 |
|
//! ```
|
| 19 |
19 |
|
//!
|
| 20 |
20 |
|
//! An **allowlist**, not a denylist, and that is the security property. A key
|
| 21 |
|
- |
//! with no policy row does not match, so it never enters the changelog — an
|
|
21 |
+ |
//! with no policy row does not match, so it never enters the changelog: an
|
| 22 |
22 |
|
//! unclassified key stays on the device. audiofiles reached the same predicate
|
| 23 |
23 |
|
//! the hard way: its earlier `key != 'loose_files'` denylist never covered
|
| 24 |
24 |
|
//! `mirror_path`/`mirror_enabled`, letting a hostile server steer a local write
|