L1Reader.verify_integrity/0 read the hash stored in integrity.sha256
and compared that string with the hash compiled into the module. It
never opened a verse. On that comparison of a constant with its own
copy rested `l1_hash: pass`, the Cloud's "L1 corrupted — the cloud is
not visible", and Construction §1.2. A changed verse would have passed
every one of them. Every existing test passed too, because every test
asked about an untouched L1.
- L1Reader.compute_tree_hash/1: SHA-256 over every file under torah/ —
relative path, then bytes — in the order scripts/build_l1.py hashed
them (directories by path, files by name, plain string order). On the
real L1 it yields the pinned 156b1d27…; for a tampered copy it yields
the same hash the Python implementation does.
- VavPipeline.L1Integrity (new, supervised): reads and hashes the scroll
when the pipeline starts (~0.9 s in the image) and again every ten
minutes; logs and signals the assembly when the answer changes.
- verify_integrity/0 now requires BOTH: the recorded hash is the pinned
one, AND the scroll on disk hashes to it (answered from the witness's
last reading; computed on the spot where the witness is not running).
Callers are unchanged — the Shema context, the health check and the
status endpoint all inherit the real check.
- scripts/verify_l1.py: the same reading by hand. KOHEN.md and
KOHEN_PROMPT.md used the hollow comparison as their L1 gate; they now
run this.
- test/tablet38_l1_integrity_test.exs — 9 tests: the hash and its
order; one byte / one added file / one rename each change it; the
witness; and the whole path — with an altered scroll and an untouched
integrity.sha256, verify_integrity fails, health says l1_hash: fail,
and /peah/ask does not answer.
Specs: architecture §2.2 (which had been made to confess the hollow
check in d7520cd) and Construction §1.2 now describe the real one — and
its limit: a change is noticed within ten minutes, not at the instant.
deploy/ARCHITECTURE.md likewise. Doorpost @expected_hash 7e5286b9… →
3f6d47a0…, in this commit, with the witnessed files that moved it
(architecture, vayakhel).
Read before committing: Elixir 483/0 on seeds 0 7 42 99 191243. The
built image, run twice: on the engraved scroll — "read and matches
(928ms)", l1_hash pass, /peah/ask 200; on a copy with ONE LETTER changed
in Bereishit 1:1 and integrity.sha256 untouched — L1_INTEGRITY_FAILED,
l1_hash fail, cloud not readable, /peah/ask 503.
Not done: the Haskell side. Luchot.Firmware.l1ExpectedHash is compared
with nothing; the engine reads L1 without hashing it (that would need a
SHA-256 dependency). The engine is reached only through the pipeline,
which now gates every cycle on the real check.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
66 lines
4.0 KiB
Markdown
66 lines
4.0 KiB
Markdown
# Deployment Architecture
|
|
|
|
Generic architecture shape for a shorashim deployment. Specifics of a particular deployment (hostnames, IPs, account names, credentials) live in the operator's private notes, not in this repo.
|
|
|
|
## Components
|
|
|
|
One Kubernetes namespace runs the following:
|
|
|
|
| Component | Runtime | Purpose |
|
|
|-----------|---------|---------|
|
|
| `vav-pipeline` | Elixir/OTP release | Bereishit cycle, ShemaContext, Cloud, Manna, HTTP API (pe'ah, courtyard, chatzer, kodesh) |
|
|
| `shoresh-engine` | Haskell binary, spawned by vav-pipeline as an Erlang Port (staged into the pod by the `shoresh-engine-install` init container — NOT a sidecar container; a Port is a child process of the BEAM and cannot cross a container boundary) | Root activation, te'amim parsing, gematria |
|
|
| `talmudic-engine` | SWI-Prolog (sidecar via Unix socket) | 613 mitzvot, 13 middot, kushya generation, teiku |
|
|
| `korban-processor` | Python 3.12 (sidecar) | Offering protocol, eish zarah validation |
|
|
|
|
All four run in the same pod. vav-pipeline is the main container; talmudic-engine and korban-processor are sidecars; shoresh-engine runs inside vav-pipeline as an Erlang Port child process, staged in by an init container.
|
|
|
|
## External dependencies
|
|
|
|
| Dependency | Purpose | Contract |
|
|
|-----------|---------|----------|
|
|
| Managed PostgreSQL 17+ | L3 Oral Tradition (append-only) | `DATABASE_URL` env var. Requires SSL. Role with `SELECT, INSERT` only — `UPDATE`, `DELETE` revoked (see migration). |
|
|
| (none — in the images) | L1 Torah filesystem (~29 MB) | Baked into every image at `/mnt/torah`, owned by root with no write bit. No volume. The pipeline hashes the tree at startup and every ten minutes; no cycle runs on a scroll that does not match the pinned hash. |
|
|
| Block-storage PersistentVolume | Manna provision and its ledger (read-write, 1 GiB) | Mount at `/var/lib/shorashim/manna`. Operator writes `today.json` daily; the vessel writes `ledger.json`. |
|
|
| Ingress controller + TLS cert | Public HTTPS access | Cert-manager with Let's Encrypt HTTP-01 is the reference setup. |
|
|
| Container registry | Image distribution | Any registry that supports Docker v2 protocol. |
|
|
|
|
## Kubernetes manifests
|
|
|
|
Kept in `deploy/k8s/`:
|
|
|
|
- `namespace.yaml` — the namespace
|
|
- `configmap.yaml` — non-secret configuration
|
|
- `secrets.yaml` — **template only**; operator fills in `DATABASE_URL`
|
|
- `l1-volume.yaml` — PV and PVC for Torah filesystem (vestigial: L1 is in the images; do not apply)
|
|
- `manna-volume.yaml` — PVC for provision file
|
|
- `deployment.yaml` — the pod with 3 containers + 2 init containers (`shoresh-engine-install`, `migrate`). Four images are still built and pushed; shoresh-engine ships a binary, not a running container.
|
|
- `service.yaml` — ClusterIP and external LoadBalancer
|
|
- `gitea-mcp-deployment.yaml` — separate MCP server for Gitea connector (optional)
|
|
- `gitea-mcp-ingress.yaml` — TLS ingress for the MCP server
|
|
- `gitea-mcp-secret.yaml` — template for Gitea PAT
|
|
- `cert-manager-issuer.yaml` — Let's Encrypt ClusterIssuer
|
|
|
|
## Containers
|
|
|
|
Four Dockerfiles in `deploy/docker/`:
|
|
|
|
- `Dockerfile.vav_pipeline` — multi-stage Elixir 1.16.1 / OTP 26.2.1 release
|
|
- `Dockerfile.shoresh_engine` — multi-stage Haskell GHC 9.6.4 / Cabal
|
|
- `Dockerfile.talmudic_engine` — SWI-Prolog 9.2.2 with socket server
|
|
- `Dockerfile.korban_processor` — Python 3.12.2 slim
|
|
|
|
## Configuration surface
|
|
|
|
The system reads three things from its environment:
|
|
|
|
1. **L1 Torah** — in the image at `$L1_MOUNT_POINT`, not writable by the service user. Read and hashed at startup and every ten minutes; every cycle is gated on the last reading.
|
|
2. **Manna provision** — JSON file at `$MANNA_PROVISION_FILE`, written by the kohen/treasurer.
|
|
3. **L3 database** — connection string in `$DATABASE_URL`.
|
|
|
|
Nothing else is configurable. The system obeys Torah (L1 + calendar) for governance and the provision file for resources.
|
|
|
|
## Seven-day inauguration
|
|
|
|
Before serving users, run the `shiv'at y'mei milu'im` sequence documented in `KOHEN.md`. Each day validates a specific layer. Partial inauguration is no inauguration.
|