Compare commits

..
Author SHA1 Message Date
Scott KarpandClaude Fable 5.1 d3df10dafd talmudic-engine: take Debian's libbrotli1 fix for CVE-2026-111497
The Brotli decoder out-of-bounds write (Brotli 1.0.3–1.2.0, fixed
1.2.1) was checked against every deployed image on 2026-10-25. No
decoder is on any attacker-reachable path: Gitea's Go binary has no
Brotli, vav and korban carry no libbrotli and no Python Brotli module,
ingress-nginx has the module unloaded and only ever encodes. Three
dormant copies remain: Gitea's Alpine brotli-libs 1.2.0 (no rebuilt
image on Docker Hub yet), ingress-nginx's brotli-libs 1.1.0, and this
image's libbrotli1 1.0.9-2 from the swipl:9.2.2 base, linked only by
libcurl3-gnutls which nothing here calls.

This one is ours to close. bookworm-security has 1.0.9-2+deb12u1;
the runtime stage now upgrades that single package and nothing else,
so the SWI-Prolog 9.2.2 pin (Construction §1.1) stands.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-25 01:56:58 +00:00
10 changed files with 44 additions and 49595 deletions

No files matched your search

+1 -3
View File
@@ -599,9 +599,7 @@ aligned, not that the output is true. Read `limits` in the response.
### Rolling out — *Nesi'at haMishkan*
`scripts/shorashim-rollout` (also installed at `~/.local/bin/`) restarts
the deployment, waits for both pods (two replicas since 2026-10-25,
surge rollout: one pod stays Ready throughout, so a rollout no longer
shows in the watchdog log), and takes two consecutive 200s from
the deployment, waits for 3/3, and takes two consecutive 200s from
`/kodesh/health` before reporting — a rollout is not done at 3/3 (NLB
re-admission lag). A new image must already be pushed as `:latest`. If
the day's manna has not been written, the new pod hangs at 2/3 until it
+13
View File
@@ -21,6 +21,19 @@ RUN swipl -g "consult('src/main'),halt(0)" -t "halt(1)"
# === RUNTIME STAGE ===
FROM --platform=linux/amd64 swipl:9.2.2
# CVE-2026-111497 (Brotli decoder out-of-bounds write, October 2026).
# The swipl:9.2.2 base carries Debian bookworm's libbrotli1 1.0.9-2,
# pulled in by libcurl3-gnutls. Nothing in this image decodes Brotli:
# swipl does not link it, there is no curl binary, and the engine reads
# JSON from vav over a Unix socket. The copy was dormant, not a path.
# Debian fixed it in 1.0.9-2+deb12u1 (bookworm-security); take that one
# package without moving the pinned base. The pin on SWI-Prolog 9.2.2
# (Construction §1.1) is the point of the base image, so this is
# --only-upgrade of the one library, not a general upgrade.
RUN apt-get update \
&& apt-get install -y --no-install-recommends --only-upgrade libbrotli1 \
&& rm -rf /var/lib/apt/lists/*
RUN useradd --create-home --shell /bin/bash bikoret
USER bikoret
+2 -2
View File
@@ -66,5 +66,5 @@ data:
# a pool of 20 fills its connection ceiling by itself (2026-10-15:
# refusals "remaining connection slots are reserved" for 24 h, one
# pool worker looping, Aiven's monitor refused). Ten is ample for a
# single replica; two replicas since 2026-10-25 → 5 each, 10 in all.
POOL_SIZE: "5"
# single replica.
POOL_SIZE: "10"
+14 -21
View File
@@ -18,25 +18,18 @@ metadata:
app.kubernetes.io/name: shorashim
app.kubernetes.io/version: "0.1.0"
spec:
# Two replicas (2026-10-25). A pod that is not ready took every caller
# behind the gate dark, and every rollout left a five-minute 503 in
# the watchdog log; with two pods and a surge rollout the Service keeps
# one Ready pod throughout. One node: this covers rollouts and pod
# crashes, not the node. The node has 1700m CPU allocatable and 1470m
# was requested (tablet 31, 2026-10-04: a surge pod at 450m hung
# Pending), so the requests below are sized to what the pod USES
# (vav 20m, sidecars 2m, measured) with headroom — 200m per pod, 400m
# for two — not to the old 450m. Limits are unchanged. Each pod reads
# the day's manna from the manna-pvc volume and is Ready without the
# kohen as long as today's portion was written; the database pool is
# 5 per pod (POOL_SIZE in the ConfigMap), 10 in all, under the
# hobbyist tier's ceiling.
replicas: 2
replicas: 1
# One replica on one node with no room to surge. The default
# (maxSurge 25% → 1 extra pod) asks for 450m CPU the node does not
# have beside the running pod, and the rollout hangs Pending while the
# old image keeps serving (tablet 31, 2026-10-04). Replace in place.
# The new pod reads the day's manna from the manna-pvc volume; it is
# Ready again without the kohen, as long as today's portion was written.
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
maxSurge: 0
maxUnavailable: 1
selector:
matchLabels:
app.kubernetes.io/name: shorashim
@@ -244,7 +237,7 @@ spec:
resources:
requests:
memory: "256Mi"
cpu: "120m"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
@@ -300,8 +293,8 @@ spec:
mountPath: /tmp
resources:
requests:
memory: "64Mi"
cpu: "40m"
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
@@ -325,8 +318,8 @@ spec:
mountPath: /tmp
resources:
requests:
memory: "64Mi"
cpu: "40m"
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
+1 -9
View File
@@ -59,7 +59,6 @@ def verse_files():
def build():
h = hashlib.sha256()
facts = []
index = [] # root_in/4: the verses by root — what the pairwise middot read instead of scanning (2026-10-25)
nverses = nroots = 0
for book, ch, vs, path in verse_files():
data = open(path, "rb").read()
@@ -73,8 +72,6 @@ def build():
roots.append(a)
gloss = " ".join(w.get("consonants", "") for w in d.get("words", []) if w.get("consonants"))
facts.append(f"verse({book}, {ch}, {vs}, [{', '.join(roots)}], {quote(gloss)}).")
for r in roots:
index.append(f"root_in({r}, {book}, {ch}, {vs}).")
nverses += 1
nroots += len(roots)
header = f"""%% l1_verses.pl — GENERATED by scripts/build_engine_verses.py from l1/torah.
@@ -85,13 +82,8 @@ def build():
%%
%% l1_sha256: {h.hexdigest()}
%% verses: {nverses} root_occurrences: {nroots}
%%
%% root_in(Root, Book, Ch, Vs) — the same facts inverted, one per root
%% occurrence, so a rule that asks "which verses carry this root" reads
%% an indexed table instead of scanning 5,853 verses (the pairwise middot:
%% gezerah shavah, binyan av, shnei ketuvim).
"""
return header + "\n".join(facts) + "\n\n" + "\n".join(index) + "\n", h.hexdigest(), nverses
return header + "\n".join(facts) + "\n", h.hexdigest(), nverses
def main():
+2 -1
View File
@@ -55,9 +55,10 @@ binyan_av_two(
CommonRoot
) :-
verse(B1, C1, V1, Roots1, _),
verse(B2, C2, V2, Roots2, _),
member(CommonRoot, Roots1),
member(CommonRoot, Roots2),
root_domain(CommonRoot, Domain),
root_in(CommonRoot, B2, C2, V2), % by the index, not a scan
ref(B1,C1,V1) \= ref(B2,C2,V2).
%% ============================================================
+2 -4
View File
@@ -25,13 +25,11 @@
%% shared_keyword(Verse1, Verse2, SharedRoot)
%% Two verses share a Hebrew root, creating a verbal link.
%% The first verse is read; the second is found by the root's index
%% (root_in/4), not by scanning the scroll (2026-10-25: over 5,853
%% verses the scan was quadratic).
shared_keyword(ref(Book1, Ch1, Vs1), ref(Book2, Ch2, Vs2), SharedRoot) :-
verse(Book1, Ch1, Vs1, Roots1, _),
verse(Book2, Ch2, Vs2, Roots2, _),
member(SharedRoot, Roots1),
root_in(SharedRoot, Book2, Ch2, Vs2),
member(SharedRoot, Roots2),
ref(Book1, Ch1, Vs1) \= ref(Book2, Ch2, Vs2).
%% ============================================================
+5 -4
View File
@@ -33,10 +33,11 @@
%% Verses contradict if their roots map to conflicting domains
verses_contradict(ref(B1,C1,V1), ref(B2,C2,V2), domain_conflict(D1, D2)) :-
verse(B1, C1, V1, Roots1, _),
verse(B2, C2, V2, Roots2, _),
member(R1, Roots1),
member(R2, Roots2),
root_domain(R1, D1),
root_domain(R2, D2),
root_in(R2, B2, C2, V2), % by the index, not a scan
domains_in_tension(D1, D2),
ref(B1,C1,V1) \= ref(B2,C2,V2).
@@ -61,10 +62,10 @@ domains_in_tension(covenant, authority).
resolving_verse(ref(B1,C1,V1), ref(B2,C2,V2), ref(B3,C3,V3)) :-
verse(B1, C1, V1, Roots1, _),
verse(B2, C2, V2, Roots2, _),
%% Third verse shares a root with first verse — by the index
member(SharedRoot1, Roots1),
root_in(SharedRoot1, B3, C3, V3),
verse(B3, C3, V3, Roots3, _),
%% Third verse shares a root with first verse
member(SharedRoot1, Roots1),
member(SharedRoot1, Roots3),
%% Third verse shares a root with second verse
member(SharedRoot2, Roots2),
member(SharedRoot2, Roots3),
File diff suppressed because it is too large. Load diff
@@ -10,7 +10,6 @@
:- module(torah_text, [
verse/5,
root_in/4,
verse_roots/4,
verses_in_chapter/3,
verses_mentioning_root/2
@@ -43,6 +42,8 @@ verse_roots(Book, Chapter, Verse, Roots) :-
verses_in_chapter(Book, Chapter, Count) :-
aggregate_all(count, verse(Book, Chapter, _, _, _), Count).
%% Find all verses mentioning a specific root — by the index.
%% Find all verses mentioning a specific root
verses_mentioning_root(Root, Verses) :-
findall(ref(Book, Ch, Vs), root_in(Root, Book, Ch, Vs), Verses).
findall(ref(Book, Ch, Vs),
(verse(Book, Ch, Vs, Roots, _), member(Root, Roots)),
Verses).