fieldlog
v0.15.1
Published
Fieldlog — write anywhere, resolve later. Offline-first append-log + SQLite read-model + sync.
Readme
fieldlog — Fieldlog
Write anywhere, resolve later.
Offline-first event log for apps that keep writing through outages and sync later: append-only log (source of truth) + SQLite read-model + sync-later. Works for game events, file versions, telemetry samples — a ledger entry (below) is one domain, not the whole story.
import { createKernel } from 'fieldlog';
const k = await createKernel({ file: 'ledger.db' });
await k.append({ type: 'entry', value: 5000, actor: 'device-01' });
const rows = await k.query('SELECT SUM(value) AS total FROM entries WHERE voided = 0');
console.log(rows[0].total); // 5000 — RECORDED, not resolved
k.close();Any event shape is stored and synced — game kills, file versions, telemetry samples ride the same log:
import { createKernel } from 'fieldlog';
const k = await createKernel({ file: 'app.db' });
await k.append({ type: 'kill', killer: 'player-1', victim: 'boss-3' });
await k.append({ type: 'version', file: 'notes.txt', rev: 3 });
await k.append({ type: 'sample', sensor: 'temp-1', celsius: 21.5 });
k.close();Offline writes are stored as DRAFT/RECORDED (pre-resolved); sync/ack happens when online.
append/query/undo never touch the network — only sync does. Any event
shape is stored and synced; the read-model projects entry / tally / undo
into queryable tables.
Getting started
- quickstart — 1 device offline, 2 devices syncing (dev + signed mode), runnable
- cli —
serve/sync/demo, every flag verified againstbin/fieldlog.ts - Two demos, one regime each, no surviving state:
bun run demo(demo/two-node.ts, unsigned dev, fixed port 8091) vsbun bin/fieldlog.ts demo(bin/fieldlog.ts:cmdDemo, signed, ephemeral port). Either proves 20-entry totals then exits; neither graduates to the other (unsigned rows carry no signatures). Real ledger example:example/ledger.mjs(bun example/ledger.mjs) - Single writer, one process per file: the kernel mutex is cooperative
in-process only (
src/kernel.ts:145-156). Details: kernel-api, limits-troubleshooting.
Gallery
| | | |---|---| | hash chain — every append seals to the previous entry | delta sync — only the diff flies, resuming from the last ack | | relay — offline devices exchange messages via the server | snapshot+truncate — trim the log without losing the trail | | capability+revoke — signed tokens, ruthless revocation | quarantine — corrupt entries jailed, never silently dropped | | read model — SQLite rebuilt from the log | soft delete — delete = tombstone, history stays intact |
Concepts & architecture
- architecture — module map: log/store/kernel/sync/relay/cas/retain
- contracts — binding promises: dead-letter, blind compensators, seal<=ack, device.explicit, incremental purge, deterministic jitter, v0.5 superset
- kernel-api —
createKernel,Kernel,LogEvent,EventStore - sync-protocol — push/pull, failover, backoff, deltasync
- relay —
WsRelayServer+WsRelayClient - retention — snapshot + truncate
- auth — device key, grant, capability token, countersign, revoke
Subsystems (details)
- token: capability-token · revoke: revoke-handshake, revoke-event-log — regrouped in auth
- delta-sync: delta-sync · hash chain: hash-chain-log · quarantine: quarantine
- attachments: cas-store · soft-delete: tombstone-engine · quota: quota-guard
- rig & harness: multi-device-rig, corpus-generator, corruption-generator, cold-drill, soak-runner, chaos-kill, flake-hunter, conformance-gate, watchdog, mismatch-stop, completion-protocol, merge-runner, model-oracle, compat-vectors, decision-log
Numbers, limits, contributing
- Benchmark: bench — all figures live there (single source
of truth). Re-run via
bun run bench:append | bench:query | bench:sync. - Log compat: compat · changelog: CHANGELOG
- Limits + troubleshooting: limits-troubleshooting
- Contributing: CONTRIBUTING · security: SECURITY · conduct: CODE_OF_CONDUCT
CLI serve/sync default to signed mode: serve needs --trust <id=pub.pem>
(repeat per device), sync needs --key <priv.pem> --as <device>.
--unsigned open relay is for local dev only, not production.
