Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ A governed, evidence-first memory ledger for AI agents, backed by PostgreSQL. It
provenance, and proves it was not quietly changed. It does **not** decide what is true (`docs/CONTINUITY_LEDGER_SOC.md`).
Continuity lives in ledger records, not in chat transcripts; the service itself is replaceable.

## What is live on 8011 (last deployed 2026-10-11)
## What is live on 8011 (last deployed 2026-10-18)

One ledger runs in Docker on a private Linux box at **`127.0.0.1:8011`** (PostgreSQL row store, schema **v9**). Nothing is
One ledger runs in Docker on a private Linux box at **`127.0.0.1:8011`** (PostgreSQL row store, schema **v10**). Nothing is
reachable from the network; the PC and the agents reach it through an SSH tunnel. Deployment, operations and the rehearsed
restore are in [`deploy/mint/README.md`](deploy/mint/README.md). **This table describes the deployed build**, which is `main` as of 2026-10-11 (`5a8d756`, schema v9);
anything merged after that day is not deployed until the next deploy.
restore are in [`deploy/mint/README.md`](deploy/mint/README.md). **This table describes the deployed build**, which is `main` as of 2026-10-18 (`3638995`, schema v10);
anything merged after that day is not deployed until the next deploy. Deploys since 2026-10-11: multi-tenant phase 0 (2026-10-12), the scoped recall credential (2026-10-13), and schema v10 with per-tenant API keys (2026-10-18).

**Schema v10 is in the code, not yet on the box.** `main` is at schema **v10** (the per-tenant API-key control schema, merged 2026-10-16; see [`docs/tenant_keys.md`](docs/tenant_keys.md)). The deployed ledger is still v9: a deploy of `main` runs the v10 migration, which changes no ledger row, and is rehearsed on a throwaway stack first. Per-tenant keys are off by default (`JARVIS_KEY_TENANTS`). The way back is `pg_schema.v10_rollback_sql`.
**Schema v10 is live (deployed 2026-10-18), with per-tenant API keys switched off.** The v10 migration added the control schema (`<schema>_control`, see [`docs/tenant_keys.md`](docs/tenant_keys.md)) and changed no ledger row or hash. No tenant and no key exists on the box, `JARVIS_KEY_TENANTS`, `JARVIS_MULTI_TENANT` and OAuth are unset, and a `jlk_` token is an ordinary wrong key (401). Turning keys on needs a pepper file and an explicit decision. The way back is `pg_schema.v10_rollback_sql` (keeps every write) or the pre-deploy backup.

| Capability | Status |
|---|---|
Expand All @@ -21,7 +21,7 @@ anything merged after that day is not deployed until the next deploy.
| **Continuity Blocks**: sealed, Merkle-rooted, hash-chained ranges of the history; every block hash is also kept outside the database in the backup anchors | **live**, **unsigned** (`docs/CONTINUITY_BLOCKS.md`) |
| **Replay Contracts, `RC.Ledger.v1`**: rebuild the ledger's state and ordered events as of any seq or sealed block, with a state root; receipts at sealed points (Evidence Objects); offline verifier `python -m app.replay verify`; `jarvisctl replay`; a restore-drill step | **live** since 2026-10-06: the `/api/jarvis/replay/*` routes, receipts at sealed points, the offline verifier, `jarvisctl replay` and the restore-drill step run on the box (`docs/REPLAY_CONTRACTS.md`). A receipt is an unsigned claim until it is re-derived |
| Domain Replay Contracts (`RC.AIKI`, `ARIS`, `SX`, `Lineage`, `Mandala`) | **on hold**: declared only (no schema, owner or algorithm exists) |
| **Signatures**: attestations of blocks, receipts and checkpoints by a key a root key authorized, a trust log, the host signer, the PC witness/cosign tool, signature levels L0/L1/L2 in replay verification, `JARVIS_SIGNATURES=off\|warn\|require` (schema v7, `docs/SIGNATURES.md`, `docs/SIGNING_RUNBOOK.md`) | **deployed since 2026-10-08 (schema v7 tables), shelved**: no key has been made and no key ceremony done, the sign timer is off, nothing is signed; `JARVIS_SIGNATURES=warn` with an empty trust roots file, so a replay reports blocks as unsigned and never as verified (the live database is schema v9) |
| **Signatures**: attestations of blocks, receipts and checkpoints by a key a root key authorized, a trust log, the host signer, the PC witness/cosign tool, signature levels L0/L1/L2 in replay verification, `JARVIS_SIGNATURES=off\|warn\|require` (schema v7, `docs/SIGNATURES.md`, `docs/SIGNING_RUNBOOK.md`) | **deployed since 2026-10-08 (schema v7 tables), shelved**: no key has been made and no key ceremony done, the sign timer is off, nothing is signed; `JARVIS_SIGNATURES=warn` with an empty trust roots file, so a replay reports blocks as unsigned and never as verified (the live database is schema v10) |
| CES registry, unified provenance chain, ESFR promotion | **not built** (`docs/CCS_CHARTER.md`) |

Also enforced by tests: continuity across sessions, replay of a retrieve with why / where / when / session, conflicts surfaced
Expand Down Expand Up @@ -104,7 +104,7 @@ database URL or that opt-in the service answers 503 rather than creating a ledge
A block sealed after the last backup is not anchored yet; a receipt taken earlier also exposes a later rewrite.
* **Domain Replay Contracts (on hold) and CES schemas are not built.** Earlier docs claimed stubs under `schemas/rc/` and `schemas/ces/`;
they never existed (corrected). Only `RC.Ledger.v1` has files (`schemas/rc/`). Receipts are unsigned claims until re-derived.
* **Signing is shelved.** The live ledger is schema v9 (deployed 2026-10-10), with `JARVIS_SIGNATURES=warn` and no trust roots: no key is authorized, nothing is signed, and a replay reports blocks as unsigned, never as verified.
* **Signing is shelved.** The live ledger is schema v10 (v9 deployed 2026-10-10, v10 on 2026-10-18), with `JARVIS_SIGNATURES=warn` and no trust roots: no key is authorized, nothing is signed, and a replay reports blocks as unsigned, never as verified.
Comment thread
warheart1984-ctrl marked this conversation as resolved.
* **Soft Clause V rules only warn** (emotion, transient state, transcripts) until the operator flips `JARVIS_CLAUSE_V_SOFT`.
* **One box.** Wi-Fi only, disk not encrypted, an hour's worth of data at risk between backups, no automatic security updates
(`deploy/mint/README.md`, "Honest limits").
Expand Down
12 changes: 6 additions & 6 deletions deploy/mint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ Windows PC ──ssh -L──► 127.0.0.1:8011 ─► app (non-root, read-on
backups (hourly, verified) ──► ~/jarvis-ledger/backups ──► age-encrypted daily ──► Windows PC (G:)
```

## Where things stand (updated 2026-10-11; the box was last deployed 2026-10-11)
## Where things stand (updated 2026-10-18; the box was last deployed 2026-10-18)

* **Running:** the stack on `127.0.0.1:8011`, on Linux Mint 22.3 (4 CPUs, 15 GiB RAM, Wi-Fi only, disk not
encrypted). Docker 29.1.3, compose 2.40.3 and age 1.1.1 come from Ubuntu's own archive.
* **Data:** 59 records imported from the PC's `jarvis-store.json` with `pg_import` on 2026-10-05 (manifest in
`~/jarvis-ledger/state/import-manifest.json`), plus one record migrated from the old service
(`mem-da3ddefda22a`, copied from `mem-79f9aa7e7e44`). Since then agents have written their own records through the API: **62 live records at
history seq 137 on 2026-10-11** (`jarvisctl replay state`, i.e. `GET /api/jarvis/replay/state`, reports the live count and is the source of truth).
(`mem-da3ddefda22a`, copied from `mem-79f9aa7e7e44`). Since then agents have written their own records through the API: **66 live records at
history seq 143 on 2026-10-18** (`jarvisctl replay state`, i.e. `GET /api/jarvis/replay/state`, reports the live count and is the source of truth).
* **The old service on 8001 is retired:** `persistence-memory.service` is stopped and disabled. Its data
(`~/.local/share/persistence-memory/`) and `~/.config/persistence-memory/memory.env` are untouched, and
`~/persistence-memory` is pinned at `d5157ba`. To bring it back:
Expand All @@ -35,7 +35,7 @@ Windows PC ──ssh -L──► 127.0.0.1:8011 ─► app (non-root, read-on
* **Deployed on 2026-10-08 and 2026-10-10:** schema **v7** (the signature tables) on 2026-10-08; schema **v8** and **v9** on 2026-10-10 (`emr_latest`'s
supersedes index and the ledger-search index; `main` at `792e99b`), with `emr_latest`, `emr_search_ledger`, the nx-search bridge and the removal of
the digital twin line. The rollback for 2026-10-10 is the named backup restored under the `pre-v8v9` images; there is no down-migration for V8.
* **Schema v10 (merged 2026-10-16, not deployed):** the control schema `<schema>_control` (tenants and per-tenant API keys; `docs/tenant_keys.md`). It touches no ledger row and no hash; a v9 application refuses a v10 database and the reverse. The box stays at v9 until a deploy of `main`, which needs the migration rehearsed on a throwaway restore first (the 2026-10-16 drill did this: `~/jarvis-ledger/drills/2026-10-16-keys/`). Down-migration: `app.pg_schema.v10_rollback_sql(schema)` as the migration role (drops the control schema and the version row; no ledger data changes).
* **Deployed on 2026-10-18:** schema **v10** (`main` at `3638995`): the control schema `<schema>_control` (tenants and per-tenant API keys; `docs/tenant_keys.md`), the key-authentication code (off: `JARVIS_KEY_TENANTS` unset, no pepper, no tenants or keys), the per-credential rate limit and failed-key throttle. The migration touches no ledger row and no hash; a v9 application refuses a v10 database and the reverse. Rehearsed first on a restore of a real backup (`~/jarvis-ledger/drills/2026-10-17-v10-main`, 52/52). Rollback: tags `jarvis-ledger-app:pre-v10` / `jarvis-ledger-db:pre-v10` and either `app.pg_schema.v10_rollback_sql(schema)` as the migration role (drops the control schema and the version row; no ledger data changes) or the backup taken with the app stopped just before (`jarvis-20261018T161754Z`, kept in `~/jarvis-ledger/keep-pre-v10`).
Comment thread
warheart1984-ctrl marked this conversation as resolved.
* **Deployed on 2026-10-11:** no schema change (still **v9**). The server-side **call log** (`docs/call_log.md`; on, `JARVIS_CALL_LOG_ENABLED=1`, in the appdata
volume and so in the backups) and the **drift watcher** (`docs/drift_watcher.md`; on, `JARVIS_DRIFT_CHECK_ENABLED=1`, operator-only, results go to its own
log, never to the ledger); `main` at `5a8d756`, an app-only swap. The rollback is the `pre-drift` image tags (remove the flag line, recreate the app only).
Expand Down Expand Up @@ -226,8 +226,8 @@ Backup first (`jarvisctl backup`, then `jarvisctl drill`), then pull and `jarvis
schema version exactly, so **v5 code refuses a v6 database and v6 code refuses a v5 one: rolling back means checking out the old
code and restoring the backup taken before the upgrade** (writes made after it are lost). After such a rollback the newer
backup sets and anchors no longer fit the restored database (their blocks "vanished"), so move the newer `jarvis-*` sets and the
newer `anchors/anchors-*.txt` files aside before the next backup, or it will quarantine its dump. The box is at **v9** now; `main` is at **v10**, not yet deployed (v6 on 2026-10-06, v7 on
2026-10-08, v8 and v9 on 2026-10-10). V8 has no down-migration, so the way back across it is the named backup restored under the `pre-v8v9` images. V9 alone
newer `anchors/anchors-*.txt` files aside before the next backup, or it will quarantine its dump. The box is at **v10** now (v6 on 2026-10-06, v7 on
2026-10-08, v8 and v9 on 2026-10-10, v10 on 2026-10-18). V8 has no down-migration, so the way back across it is the named backup restored under the `pre-v8v9` images. V9 alone
does have one: run `app.pg_schema.V9_ROLLBACK` as the migration role in one transaction and deploy the V8 code (`docs/ledger_search.md`), which keeps every write made since.

### Signatures (schema v7)
Expand Down
2 changes: 1 addition & 1 deletion docs/POSTGRES.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pool is shed with an immediate 503 rather than queued behind a slow database.
* `GET /health` - **liveness**: the process is up. Never touches the ledger, reports no counts or paths.
* `GET /ready` - **readiness**: 200 only if all of these hold, checked live on every call (never cached);
otherwise 503 with `Retry-After` and the failing check names (no details):
`database` (`SELECT 1` as the application role), `schema_version` (matches the code exactly) (the code is at schema 10; a database at 9 is refused until migrated), `role`
`database` (`SELECT 1` as the application role), `schema_version` (matches the code exactly) (the code is at schema 10; a database at another version is refused until migrated; the live box is at 10), `role`
(not superuser, no `BYPASSRLS`), `rls_tables` (every table of the schema with a `tenant_key` column has row-level security enabled, **forced**, and at least one policy; the role check proves the app role cannot bypass RLS, this proves there is RLS to be subject to, so a migration that adds a tenant table without it, or drops its FORCE, makes `/ready` 503; the offending tables are named in the log only; the one exception is the retained legacy blob table `jarvis_tenant_ledgers`, which the importer leaves as a rollback copy and the row store never reads: `legacy_data` watches it), `history_write_denied` (the role is *proven* unable to write
`record_history`, `chain_heads` or `history_counters`: the catalog shows no write privilege and attempted
INSERT/UPDATE/DELETE are refused, inside rolled-back savepoints; `TRUNCATE` is checked in the catalog
Expand Down
2 changes: 1 addition & 1 deletion docs/tenant_keys.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Per-tenant API keys (RFC phase 1, slice 1)

Status: **built, not deployed.** Slice 1 adds the storage, the token format, the verification primitive and the migrator-role CLI. Slice 2 (below) authenticates requests with a tenant key behind
Status: **schema v10 is deployed (2026-10-18) with key authentication switched off**: the control schema exists on the live box, no tenant or key has been created, and `JARVIS_KEY_TENANTS` is unset. Slice 1 adds the storage, the token format, the verification primitive and the migrator-role CLI. Slice 2 (below) authenticates requests with a tenant key behind
`JARVIS_KEY_TENANTS`, which is off by default; with it off the only change to a running deployment is that the schema version becomes 10.

## Schema v10: the control schema
Expand Down
12 changes: 6 additions & 6 deletions tests/test_readme_status.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""What the READMEs say is live on 8011 must match what is deployed (the box was last deployed 2026-10-11, at schema v9 since 2026-10-10: Clause V, Evidence
"""What the READMEs say is live on 8011 must match what is deployed (the box was last deployed 2026-10-18, at schema v10 (v9 since 2026-10-10): Clause V, Evidence
Objects, Continuity Blocks, Replay Contracts, emr_latest and ledger search; the signature tables since 2026-10-08; the call log and drift watcher on since 2026-10-11), and what is shelved must say so. If a deploy changes this, change the READMEs and this test together."""

from __future__ import annotations
Expand All @@ -16,7 +16,7 @@ def row(text: str, start: str) -> str:


def test_the_status_table_says_what_is_live():
assert "## What is live on 8011 (last deployed 2026-10-11)" in README
assert "## What is live on 8011 (last deployed 2026-10-18)" in README
for name in ("**Postgres row store**", "**Clause V**", "**Evidence Objects**", "**Continuity Blocks**"):
assert "**live**" in row(README, f"| {name}"), name
replay = row(README, "| **Replay Contracts, `RC.Ledger.v1`**")
Expand All @@ -31,7 +31,7 @@ def test_domain_replay_contracts_are_on_hold():

def test_signatures_are_deployed_but_shelved():
sig = row(README, "| **Signatures**")
for needle in ("shelved", "no key", "sign timer is off", "nothing is signed", "schema v9", "JARVIS_SIGNATURES=warn", "never as verified"):
for needle in ("shelved", "no key", "sign timer is off", "nothing is signed", "schema v10", "JARVIS_SIGNATURES=warn", "never as verified"):
assert needle in sig, needle
assert "**live**" not in sig and "not deployed" not in sig and "schema v6" not in sig

Expand All @@ -41,13 +41,13 @@ def test_no_stale_claim_that_replay_is_missing_from_the_box():
for stale in ("not live yet", "not deployed yet", "has no `/api/jarvis/replay", "needs the build with Replay Contracts deployed", "is not on the live box until it is deployed"):
assert stale not in text, stale
assert "`main` is ahead of the box" not in README # it is not: the box was deployed from main on 2026-10-11
assert "This table describes the deployed build**, which is `main` as of 2026-10-11" in README
assert "This table describes the deployed build**, which is `main` as of 2026-10-18" in README


def test_the_mint_readme_lists_what_is_deployed_and_what_is_not():
assert "Where things stand (updated 2026-10-11; the box was last deployed 2026-10-11)" in MINT
assert "Where things stand (updated 2026-10-18; the box was last deployed 2026-10-18)" in MINT
assert "**Deployed on 2026-10-06:**" in MINT and "Replay Contracts (`RC.Ledger.v1`" in MINT
assert "**Deployed on 2026-10-08 and 2026-10-10:**" in MINT and "schema **v8** and **v9**" in MINT
assert "**Shelved, though the code is deployed:** signatures" in MINT and "the sign timer is off" in MINT
assert "The sign timer is **off**" in MINT
assert "**Deployed on 2026-10-11:**" in MINT and "JARVIS_CALL_LOG_ENABLED=1" in MINT and "JARVIS_DRIFT_CHECK_ENABLED=1" in MINT and "pre-drift" in MINT
assert "**Deployed on 2026-10-18:**" in MINT and "schema **v10**" in MINT and "**Deployed on 2026-10-11:**" in MINT and "JARVIS_CALL_LOG_ENABLED=1" in MINT and "JARVIS_DRIFT_CHECK_ENABLED=1" in MINT and "pre-drift" in MINT
Loading