Skip to content

Commit 91ab8a9

Browse files
committed
docs: reconcile port/unimplemented/absolute claims + storage caveats (7.2, 7.5, 7.6, 5.3, 5.6)
7.2 — cli.md notes the port is operator-chosen (loti init suggests 7000, the examples use it; the sim uses a fixed 666) rather than implying a single canonical port. 7.5 — cli.md flags node start / --reference / config as planned-not-built and marks the node-start transcript accordingly, so the command reference isn't read as all-implemented. 7.6 — README calls discoveries best-effort (can abort/expire/undetermined); the 'prove bounds for any event' goal is what a *found* chain guarantees, not that one is always findable. 5.3 — cli.md: only clock-event storage is ring-pruned; published-event content is a separate, unpruned store (a heavy publisher's disk grows), reported by db stat. Also fixed the undefined 'a' in the precision formula to 1/C (matching theory.md). 5.6 — embedded.md documents that safe-mode per-commit fsync runs on the single reactor thread and stalls networking under storage latency; lazy mode moves it off the hot path.
1 parent 5385c83 commit 91ab8a9

3 files changed

Lines changed: 25 additions & 6 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ Start here depending on what you want:
4545
- **Event chain** — a hash-linked sequence `lowerBound · event · upperBound` that starts and
4646
ends at the querying node's own clock events and passes through the target event. It is a
4747
proof of that node's time bounds for the event.
48-
- **Discoveries** — three queries a node's *daemon* can run:
48+
- **Discoveries** — three queries a node's *daemon* can run. Each is **best-effort**: it succeeds
49+
when a chain can be reconstructed and reverse-links have been learned, and otherwise aborts or
50+
expires (an order query can also return `undetermined`). The "prove bounds for any event" goal
51+
above is what a *found* chain guarantees; finding one is not guaranteed for every event at every
52+
moment.
4953
- **event chain discovery** — reconstruct the enclosing chain for an event (routed to the
5054
event's creator and back, accreting each hop's clock events);
5155
- **event bounds discovery** — the enclosing chain's endpoint timestamps `(lower, upper)`;

doc/cli.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,13 @@ verify offline** — plus persistence. All of the daemon promotions above are im
9999
**Retention.** `lotid` runs a fixed default schedule of several independent clock chains at
100100
geometrically spaced intervals (fastest first) instead of one; every published event pins into
101101
every chain at creation, and each chain is ring-pruned to a fixed capacity after each tick, so
102-
clock-event storage is **bounded** rather than growing with wall-clock time. This retires the
102+
**clock-event** storage is **bounded** rather than growing with wall-clock time. (Published-event
103+
*content* is a separate store and is **not** ring-pruned — a heavy publisher's disk still grows
104+
with what it publishes; `db stat` reports the event count.) This retires the
103105
earlier rule *local events and the local clock chain are never dropped*: the invariant now is
104106
that the local clock chain is never dropped **below the coarsest retained resolution** — every
105-
event stays boundable, at a precision that degrades to roughly `a/C` of its age (`C` = clock
106-
events kept per chain) rather than being lost. `db gc` from [Storage &
107+
event within the horizon stays boundable, at a precision that degrades to roughly `1/C` of its
108+
age (`C` = clock events kept per chain) rather than being lost. `db gc` from [Storage &
107109
maintenance](#storage--maintenance) is implemented — it re-asserts every chain's ring cap
108110
(normally a no-op, since the same pruning already runs after every clock tick); `db verify` is
109111
not implemented.
@@ -192,11 +194,17 @@ Human-readable tables by default; `--json` emits stable machine JSON. Standard e
192194
| `loti version` | Client + daemon version, protocol version, build info. |
193195
| `loti config get <key>` / `set <key> <value>` / `list` | Read/update configuration; `set` may require `loti node restart` for some keys (flagged in output). |
194196

197+
The command tables above document the full **intended** surface; several rows — `node
198+
start`/`restart`, `config get/set/list`, and the `--reference <node>` flag — are **planned, not
199+
yet built** (see [Implementation status](#implementation-status-mvp)). Today you start the node by
200+
running `lotid` directly (see the Quickstart), and a proof is always anchored in the local node.
201+
The console transcripts below illustrate the intended experience, including planned commands.
202+
195203
```console
196204
$ loti init
197205
identity node:9f3a…c1 (ed25519)
198206
home ~/.loti
199-
$ loti node start
207+
$ loti node start # planned; today run `lotid …` directly
200208
lotid started (pid 4821), listening udp/:4666, control ~/.loti/control.sock
201209
$ loti status
202210
node node:9f3a…c1
@@ -433,7 +441,7 @@ loti verify order.loti
433441
| `home` | `~/.loti` | State directory. |
434442
| `identity.key` | `key.pem` | Signing key path. |
435443
| `identity.sign_events` | `true` | Sign published events. |
436-
| `network.listen` | `udp://:4666` | P2P transport bind address. |
444+
| `network.listen` | `udp://:4666` | P2P transport bind address. The port is operator-chosen (there is no protocol-mandated port); `loti init` suggests `7000` and the real-node quickstarts use it, while the OMNeT++ simulation uses a fixed `666`. |
437445
| `network.control` | `control.sock` | Local RPC socket for `loti`. |
438446
| `clock.interval` | `1s` | Clock-event creation interval. |
439447
| `discovery.expiry` | `1s``30s` | Discovery timeout before abort (raise for real WANs). |

doc/embedded.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@ The database stays **crash-consistent** either way (LMDB is copy-on-write; witho
160160
window**: a crash in lazy mode may lose up to the last `<interval>` of commits — never the
161161
database's integrity. A clean shutdown and the `save` command always force a final flush.
162162

163+
There is a second reason to prefer lazy mode on slow or high-latency storage: the daemon is
164+
single-threaded, so in the default **safe** mode the per-commit `fsync` runs *on the reactor
165+
thread* — while it blocks, no packets or control commands are serviced. On a busy SD card or
166+
network-backed disk that stall is visible. `--store-sync-interval > 0` moves the flush to a
167+
periodic timer, so a storage-latency spike no longer blocks networking. (Moving `fsync` to a
168+
background thread is a possible future improvement.)
169+
163170
| `--store-sync-interval` | Behaviour | Crash risk |
164171
|---|---|---|
165172
| `0` (default) | fsync every commit | none (fully durable) |

0 commit comments

Comments
 (0)