Architecture §2 described a self-hosted server: Phoenix for L2,
PostgreSQL 16, Ubuntu 24.04 under systemd, rootless Podman isolating
Sanhedrin panels, Erlang distribution between them, systemd timers for
Shabbat and the cycle, L1 on a read-only mount. None of that is what
runs. Each row was checked against the code and the images before it
was rewritten.
§2.1 exact versions (no "9.6+"); Korban and Azazel Processors listed.
§2.2 L1: in every image, root-owned, no write bit; recorded hash
compared before every cycle. L2: a process in the pipeline, not
yet over HTTP and not yet called by the cycle. L3: PostgreSQL 17
managed, append-only by REVOKE.
§2.3 Kubernetes, one pod. OTP supervisors + the Deployment's probes.
Sanhedrin panels are Erlang processes in the pipeline's BEAM — no
container boundary. Message passing within one node; a port to
the engine; a Unix socket to the Talmudic Engine. Logging: stdout
and L3; halt events in memory. ShabbatTimer process, UTC stub.
No cycle timer: erev/voker is a stage of each cycle. The manna
volume.
A DEFECT FOUND BY CHECKING THE L1 ROW, AND FIXED:
The spec said L1 is a read-only filesystem. In all four images the
scroll was owned by the service user, mode 664 — `touch` inside
/mnt/torah succeeded. The process that reads Torah could have rewritten
it. All four Dockerfiles now copy L1 as root and strip every write bit
(dirs 555, files 444). Verified in each image as the service user:
cannot create, append, or delete. The hash is unchanged (it covers
paths and contents, not modes).
A DEFECT FOUND AND NOT FIXED, now written into the spec as it is:
L1Reader.verify_integrity/0 reads the hash stored in integrity.sha256
and compares it with the compiled-in constant. It never hashes the
verse files. `l1_hash: pass` witnesses which L1 was built into the
image, not that the files are unaltered. §2.2 says so in plain words.
deploy/ARCHITECTURE.md: L1 is in the images, not on a volume; the manna
volume is 1 GiB and holds the ledger too; l1-volume.yaml is vestigial.
Doorpost @expected_hash 1cd07079… → 7e5286b9…, in this commit, with the
witnessed file that moved it (architecture).
Read before committing: Elixir 474/0 (seeds 0, 42). Four images
rebuilt; full local stack (real engine binary, PostgreSQL with SSL,
migrations): Hebrew 200, English 422, health 7/7, 2 interpretations in
L3, no errors; talmudic and korban containers start.
Left as written: §1.4 hardware (a 64–128 GB server; the node has 4 GB),
§2.4 external models (Steps 21–22, unbuilt), §4's build steps
("Install PostgreSQL 16"), terumah §B (systemd, squashfs, Caddy,
nftables).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
85 lines
3.3 KiB
Docker
85 lines
3.3 KiB
Docker
# check=skip=FromPlatformFlagConstDisallowed
|
|
# ^ The --platform=linux/amd64 pins below are DELIBERATE (Exoscale SKS
|
|
# nodes are amd64). BuildKit lints a constant --platform as a smell;
|
|
# here it is the fix. Silenced so a real warning is not lost in noise.
|
|
# Shorashim Shoresh Engine — Haskell root activation engine
|
|
# Construction spec §1.1: GHC 9.6.4, Cabal 3.10
|
|
# Communicates with vav_pipeline via Erlang Port (stdin/stdout)
|
|
#
|
|
# PLATFORM PIN: see Dockerfile.vav_pipeline for the rationale.
|
|
# Exoscale SKS = amd64; arm64 images cannot exec on the cluster.
|
|
|
|
# === BUILD STAGE ===
|
|
FROM --platform=linux/amd64 haskell:9.6.4-slim AS build
|
|
|
|
# haskell:9.6.4-slim is Debian Buster (EOL) — use archive repos
|
|
RUN sed -i 's|deb.debian.org/debian|archive.debian.org/debian|g' /etc/apt/sources.list && \
|
|
sed -i 's|deb.debian.org/debian-security|archive.debian.org/debian-security|g' /etc/apt/sources.list && \
|
|
sed -i '/buster-updates/d' /etc/apt/sources.list && \
|
|
apt-get update && apt-get install -y zlib1g-dev && rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy cabal files first for dependency caching
|
|
COPY shoresh-engine/shoresh-engine.cabal ./
|
|
COPY shoresh-engine/cabal.project.freeze ./
|
|
|
|
RUN cabal update && cabal build --only-dependencies
|
|
|
|
# Copy source and build
|
|
COPY shoresh-engine/ ./
|
|
|
|
RUN cabal build && \
|
|
cp $(cabal list-bin shoresh-engine) /usr/local/bin/shoresh-engine
|
|
|
|
# === RUNTIME STAGE ===
|
|
FROM --platform=linux/amd64 ubuntu:jammy-20240125
|
|
|
|
RUN apt-get update && \
|
|
apt-get install -y --no-install-recommends \
|
|
libgmp10 zlib1g locales && \
|
|
rm -rf /var/lib/apt/lists/* && \
|
|
sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
|
|
locale-gen
|
|
|
|
# === UTF-8 IS LOAD-BEARING HERE ===
|
|
# This engine's entire job is Hebrew roots. GHC picks stdout's encoding
|
|
# from the locale at startup; ubuntu:jammy with no locale set gives the
|
|
# POSIX/C locale, i.e. ASCII, and the FIRST non-ASCII byte the engine
|
|
# tries to write kills it:
|
|
# Shoresh Engine ...: <stdout>: commitBuffer: invalid argument
|
|
# (cannot encode character '\8212')
|
|
# It died on the em-dash in its own banner, before reading one root.
|
|
# vav_pipeline's runtime stage has always set these three; this stage
|
|
# did not, and shoresh-engine is the one component that CANNOT run
|
|
# without them. Matches Dockerfile.vav_pipeline so both halves of the
|
|
# Erlang Port speak the same encoding.
|
|
ENV LANG=en_US.UTF-8
|
|
ENV LANGUAGE=en_US:en
|
|
ENV LC_ALL=en_US.UTF-8
|
|
|
|
RUN useradd --create-home --shell /bin/bash shorashim
|
|
USER shorashim
|
|
|
|
WORKDIR /home/shorashim
|
|
|
|
COPY --from=build /usr/local/bin/shoresh-engine ./shoresh-engine
|
|
|
|
# L1 Torah filesystem — baked into the image
|
|
# L1 is read-only in fact, not only in name. Until 2026-10-04 the scroll
|
|
# was copied in owned by the service user with its write bits on: the
|
|
# process that reads Torah could also have rewritten it, and only the
|
|
# SHA-256 check would have noticed afterwards. Owned by root, no write
|
|
# bit for anyone; the service user can read and traverse, nothing more.
|
|
USER root
|
|
COPY --chown=root:root l1/ /mnt/torah/
|
|
RUN chmod -R a-w,a+rX /mnt/torah
|
|
USER shorashim
|
|
|
|
# L1 mount point
|
|
ENV L1_MOUNT_POINT=/mnt/torah
|
|
|
|
# The engine is invoked as an Erlang Port by vav_pipeline
|
|
# In K8s, it runs as a sidecar or is called via the Port protocol
|
|
CMD ["./shoresh-engine"]
|