diff --git a/README.md b/README.md index 2344b1d..3d0df87 100644 --- a/README.md +++ b/README.md @@ -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 (`_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 | |---|---| @@ -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 @@ -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. * **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"). diff --git a/deploy/mint/README.md b/deploy/mint/README.md index 331d1fb..5926fb1 100644 --- a/deploy/mint/README.md +++ b/deploy/mint/README.md @@ -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: @@ -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 `_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 `_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`). * **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). @@ -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) diff --git a/docs/POSTGRES.md b/docs/POSTGRES.md index 337fb6e..f669439 100644 --- a/docs/POSTGRES.md +++ b/docs/POSTGRES.md @@ -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 diff --git a/docs/tenant_keys.md b/docs/tenant_keys.md index 2558e9f..d02bc02 100644 --- a/docs/tenant_keys.md +++ b/docs/tenant_keys.md @@ -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 diff --git a/tests/test_readme_status.py b/tests/test_readme_status.py index 8fdef45..6595126 100644 --- a/tests/test_readme_status.py +++ b/tests/test_readme_status.py @@ -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 @@ -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`**") @@ -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 @@ -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