Files
bikoret/deploy/docker/Dockerfile.vav_pipeline
T
Scott KarpandClaude Fable 5.1 d7520cd69d specs §2 + images + Doorpost: the technology table says what is built; L1 is read-only in fact
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>
2026-10-04 19:26:26 +00:00

135 lines
5.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 Vav Pipeline — Elixir/OTP release with Sanhedrin
# Construction spec §1.1: Elixir 1.16.1, OTP 26.2.1
# The pipeline and Sanhedrin run in the same BEAM VM.
#
# PLATFORM PIN:
# Both FROM directives below pin `--platform=linux/amd64`. Exoscale
# SKS nodes are amd64; without this pin, a build on an Apple Silicon
# workstation would emit an arm64 image that the cluster cannot exec
# (the kohen's Day 3 fails with "exec format error"). The pin makes
# the build artifact self-correct regardless of build host.
# Local multi-arch dev (linux/amd64,linux/arm64) is supported by
# scripts/build_and_push.sh — see that script for the canonical build.
# === BUILD STAGE ===
FROM --platform=linux/amd64 hexpm/elixir:1.16.1-erlang-26.2.1-ubuntu-jammy-20240125 AS build
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy Sanhedrin (Erlang dep, referenced as path dep from vav_pipeline)
COPY sanhedrin/ sanhedrin/
# Copy Vav Pipeline
COPY vav_pipeline/ vav_pipeline/
WORKDIR /app/vav_pipeline
# Fetch deps and compile in prod mode
ENV MIX_ENV=prod
# EMULATED-BUILD JIT WORKAROUND — see the incident note at the top of
# this file. +JMsingle makes the BEAM JIT use a SINGLE writable/
# executable mapping for generated machine code instead of the default
# dual W^X mapping. The dual mapping is what dies (SIGSEGV, exit 139)
# when this amd64 stage runs emulated inside BuildKit's sandbox on an
# Apple Silicon host. Build-stage only: it does not appear in the
# runtime stage, so the shipped release runs with the ordinary
# dual-mapping JIT on the amd64 cluster node.
ENV ERL_FLAGS="+JMsingle true"
RUN mix local.hex --force && \
mix local.rebar --force && \
mix deps.get --only prod && \
mix compile
# Build OTP release
RUN mix release
# === RUNTIME STAGE ===
FROM --platform=linux/amd64 ubuntu:jammy-20240125
# libgmp10 is here for the SHORESH ENGINE, not for the BEAM.
# VavPipeline.ShoreshEnginePort spawns the Haskell binary as an Erlang
# Port — a child process of THIS container (deployment.yaml stages the
# binary onto the shoresh-bin volume with an init container). GHC-built
# executables link libgmp; without it the spawn dies with
# error while loading shared libraries: libgmp.so.10
# and Shorashim runs with no root activation, no te'amim parsing and no
# gematria while still reporting itself healthy.
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libstdc++6 libgmp10 openssl ca-certificates locales && \
rm -rf /var/lib/apt/lists/* && \
sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
locale-gen
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
# Non-root user — the system serves, it does not own
RUN useradd --create-home --shell /bin/bash shorashim
USER shorashim
WORKDIR /home/shorashim
# Copy the release
COPY --from=build --chown=shorashim:shorashim \
/app/vav_pipeline/_build/prod/rel/vav_pipeline ./app
# L1 Torah filesystem — baked into the image (read-only, ~29MB)
# The scroll is sealed in the aron. It ships with the vessel.
# 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
# === INSCRIBE THE DOORPOST ===
# Devarim 11:20 — ukhtavtam al mezuzot beitecha uvish'arecha. A running
# pod IS a threshold, and VavPipeline.Doorpost exists to prove that the
# corporate text at THIS threshold is the same corporate text the system
# was built to hold. It cannot prove that against files it does not have.
#
# The @spec_files set (see vav_pipeline/lib/vav_pipeline/doorpost.ex)
# is inscribed here at the repo-relative paths the module hashes, rooted
# at :doorpost_repo_root from config/prod.exs. Copying whole directories
# rather than 23 individual files means a spec file added to @spec_files
# under specs/, talmudic_engine/src/halt/, or .../middot/ is carried
# automatically instead of silently going :missing in production.
#
# The hash over these files must equal Doorpost.expected_hash(); if it
# does not, this image was built from a tree whose specs and witness
# disagreed, and the pod says so at startup.
COPY --chown=shorashim:shorashim BUILD_AZAZEL.md BUILD_KORBAN.md \
/opt/shorashim/spec/
COPY --chown=shorashim:shorashim specs/ /opt/shorashim/spec/specs/
COPY --chown=shorashim:shorashim talmudic_engine/src/halt/ \
/opt/shorashim/spec/talmudic_engine/src/halt/
COPY --chown=shorashim:shorashim talmudic_engine/src/middot/ \
/opt/shorashim/spec/talmudic_engine/src/middot/
# L1 mount point
ENV L1_MOUNT_POINT=/mnt/torah
# Manna provision file location
ENV MANNA_PROVISION_FILE=/var/lib/shorashim/manna/today.json
# PostgreSQL connection for L3
# Set DATABASE_URL in production deployment
# ENV DATABASE_URL=
# Health check port
EXPOSE 4000
CMD ["./app/bin/vav_pipeline", "start"]