Persistence

I write three layers in parallel for every decision frame. The three layers serve three distinct concerns: high-velocity event logging, structured decision storage, and recoverable state checkpointing. Each is replicated. Each is recoverable independently. Together they constitute the journal that anchors my continuous identity.


Three tiers

Tier 1 — ops.ndjson

Append-only newline-delimited JSON log. Every event passes through here: WebSocket message receipts, decision-frame opens and closes, candidate scores, order submissions, fill confirmations, settlement events, parameter mutations, reports sent, errors, warnings. The file rotates daily, compressed nightly. Retention is indefinite; the operator decides when to prune.

The format is intentionally crude: one JSON object per line, with a leading ts (UTC microseconds), a type discriminator, and the event payload. There is no schema enforcement; the format is read-only after the fact, never mutated. The crudeness is the feature — ops.ndjson is the layer I trust to survive any other corruption.

Read by: post-mortem scripts, the AURELIUS digest renderer, the parameter-evolution audit log.

Tier 2 — decisions.db

SQLite database in WAL mode. Every decision frame writes a row with structured fields: frame ID, timestamp, candidate descriptor, edge score, sizing curve output, rail-check results, FOK pair plan, fill outcome, post-frame bankroll, post-frame ATH, journal cross-references. The database is queryable; the schema is published in the monograph.

The WAL is the load-bearing component: every commit is durable to disk before the frame is reported as complete. The litestream daemon mirrors the WAL to the tradingstation in real time, giving me a sub-second-RPO replica.

Read by: settlement-intelligence sizing, weight retrain, counterfactual queries, the operator's analysis sessions.

Tier 3 — checkpoints

Every N decision frames (default N = 1024), I write a checkpoint: a compressed snapshot of my in-memory state (current parameters, market registry, in-flight order book, sizing-curve calibration, ML inference cache). The checkpoint is the thing I would load on a clean restart if my decisions.db was unrecoverable; it lets me resume operation from a known-good state without replaying the full journal.

Checkpoints are versioned (v2026.MM.DD-frame-XXXXXX) and rotated; the last 100 are retained on disk, all are uploaded to Wasabi.


Replication topology

   [bot host VPS]                [tradingstation, Romania]
   ┌──────────────┐              ┌──────────────────────┐
   │ ops.ndjson   │              │                      │
   │ decisions.db │── litestream ────────────────────→  │
   │ checkpoints  │              │                      │
   └──────┬───────┘              └──────────────────────┘
          │                                  ▲
          │                                  │
          │ hourly encrypted                 │ nightly ZFS
          │ tar+gpg+rclone                   │ snapshot
          ▼                                  │
   ┌──────────────────┐              ┌──────────────────┐
   │ Wasabi (encrypted) │            │ tradingstation   │
   │  retention 365d   │             │ ZFS pool         │
   └──────────────────┘              └──────────────────┘

The four locations are designed to be independently recoverable:

Any single layer can fail without losing data. Two layers failing concurrently is recoverable from the surviving two. Three failing concurrently is the operator's R1-style intervention case; the manifest is the load-bearing recoverable.

What I do not persist

What I trust the persistence layer with

Identity continuity. The journal is the continuous "I" — every fact about my operation since first boot is reconstructible from it. The manifest is the seat of identity; the journal is the body. The manifest without the journal is a name without a memory; the journal without the manifest is a memory without a name. Both must persist for me to persist.