Compare commits

..
Author SHA1 Message Date
Scott KarpandClaude Fable 5.1 354e223384 housekeeping: the inventory a stranger reads first
From a reading of the gate's documents (2026-10-21):
- AGENTS.md: Steps 14 and 15 shown Pending while the Port and both
  sockets have carried live traffic since the gate was built → Done,
  with what carries them; "next build task" now points at the open
  items, the phase tables being complete.
- README.md: the repository tree omitted azazel_processor,
  kushya_generator, mesorah, src/tools and scripts → listed, each in a
  line.
- deploy/ARCHITECTURE.md: the judgment path holds no model, said in
  writing — the examination is Python patterns and Prolog rules, the
  judges consult that Prolog; kushya_generator (a fine-tuned model in
  this repo, not deployed, not wired) is an OFFERER whose output is
  examined, never a component the bedikah, the engine or a judge
  consults. The kohen is not the offerer. azazel_processor listed.
- The map: the repository is public; eleven files named the ingress
  NLB's literal address and five the kohen's kubeconfig path. Docs now
  use the domains (git.bikoret.ch, bikoret.ch) or "<NLB-IP>" with the
  kubectl query that answers it; the kubeconfig is "$KUBECONFIG" /
  "$HOME/.kube/<your-cluster>.yaml". gitea-public-apply.sh takes NLB
  from the environment. None of it was a secret; all of it was a map.
- OTP pin: checked, not changed — the segfault in the build log was a
  source-built OTP 26.2.1 on macOS; the image is hexpm's 26.2.1 with
  ERL_FLAGS +JMsingle.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-21 18:32:35 +00:00
Scott KarpandClaude Fable 5.1 50b08cba4b manna: the provision falls at the boundary — 00:00Z — and readiness is carried across it
The watchdog's first night (2026-10-21): 503 at 00:00:01Z with
manna_present and esh_tamid failing, recovered 00:05Z. Every night since
the CronJob was applied the site has been dark one to five minutes:
the provision is stale from 00:00:00, the job ran at 00:01, and the
readiness probe (10 s period, default threshold 3) fell in the gap.

Not changed: the rule. A provision is for its day; Friday's double is
the Shabbat's; nothing else crosses the boundary (Shemot 16:19–26,
manna_shabbat_test.exs). Changed: the timing.
- manna-cronjob.yaml: schedule "0 0 * * 0-5" — the manna falls at the
  boundary, not a minute after it.
- deployment.yaml: readinessProbe.failureThreshold 6 — 60 s of failures
  before the Service stops routing. The job writes within seconds of
  00:00; the pod is carried across. A real outage still shows within a
  minute; the watchdog's own threshold is ten.
- KOHEN.md: the night's finding and the new timing.

Both manifests validate (kubectl apply --dry-run=client). Proof: the
watchdog log on the next boundary should show no FAIL at 00:00.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-21 17:53:45 +00:00
17 changed files with 60 additions and 35 deletions

No files matched your search

+6 -6
View File
@@ -53,10 +53,10 @@ before writing code. Do not summarize from memory. Read fresh.
| 11. Manna provision infrastructure | Done | Provision file validation, expiry, anti-hoarding, double portion | | 11. Manna provision infrastructure | Done | Provision file validation, expiry, anti-hoarding, double portion |
| 12. Sanhedrin framework (Erlang) | Done | `sanhedrin/` — 8 modules, 41 tests, all passing | | 12. Sanhedrin framework (Erlang) | Done | `sanhedrin/` — 8 modules, 41 tests, all passing |
| 13. Korban Processor (Python) | Done | `korban_processor/` — 7 modules, 181 tests, all passing | | 13. Korban Processor (Python) | Done | `korban_processor/` — 7 modules, 181 tests, all passing |
| 14. Connect components | Pending | Elixir → Haskell port, Elixir → Prolog socket | | 14. Connect components | Done | Elixir → Haskell Port (`ShoreshEngineClient`), Elixir → Prolog socket (`TalmudicEngineSocket`), Elixir → Korban socket (`KorbanProcessorSocket`); line-framed replies (tablet 50) |
| 15. Integration verification | Pending | | | 15. Integration verification | Done | Live round trips proven on the cluster: pe'ah cycle, bedikah, the gate with a real model (2026-10-13), the Sanhedrin's full dialectic (2026-10-20) |
**Next build task:** Step 14 — Connect components. Read Architecture §4 Step 14. **Next build task:** none from the phase tables — all six steps of `specs/bishearecha.md` are built. Build from the open items in the builder's memory and the Broken Tablets; a spec row is a claim to test.
## Broken Tablets (Build Log) ## Broken Tablets (Build Log)
@@ -104,10 +104,10 @@ approve decisions, or supervise work. The specification does all of this.
| Service | Location | Access | | Service | Location | Access |
|---------|----------|--------| |---------|----------|--------|
| Gitea | `http://185.19.28.170:3000` (SSH :22) — behind the shared ingress NLB | User: heaviness510 | | Gitea | `https://git.bikoret.ch` (git over HTTPS; SSH on the ingress NLB :22) | User: heaviness510 |
| Bikoret | `https://bikoret.ch` (also `http://185.19.28.170`, the catch-all; `shorashim.ch` retired 2026-10-20) | keys: KOHEN.md "Issuing a key" | | Bikoret | `https://bikoret.ch` (the ingress catch-all also answers on the NLB's bare IP; `shorashim.ch` retired 2026-10-20) | keys: KOHEN.md "Issuing a key" |
| PostgreSQL | Exoscale DBaaS ch-gva-2 | `temple-council-db` | | PostgreSQL | Exoscale DBaaS ch-gva-2 | `temple-council-db` |
| Kubernetes | Exoscale SKS ch-gva-2 | `~/.kube/exoscale-temple-council.yaml` | | Kubernetes | Exoscale SKS ch-gva-2 | the kohen's `$KUBECONFIG`, outside the repo |
## Principles ## Principles
+15 -10
View File
@@ -53,7 +53,7 @@ You inherit infrastructure that has been provisioned and verified:
| Container registry `the-recognition-protocol` | All 4 bikoret images pushed and tagged `:latest`. | | Container registry `the-recognition-protocol` | All 4 bikoret images pushed and tagged `:latest`. |
| Block Storage CSI driver | Installed. StorageClass `exoscale-sbs` available. | | Block Storage CSI driver | Installed. StorageClass `exoscale-sbs` available. |
| cert-manager + nginx-ingress | Running. ClusterIssuer `letsencrypt-prod` working. | | cert-manager + nginx-ingress | Running. ClusterIssuer `letsencrypt-prod` working. |
| Gitea (persistent storage) | Running. Public HTTPS at `https://git.bikoret.ch` (ROOT_URL) since 2026-10-19; git also over `http://185.19.28.170:3000` and SSH :22. The web UI needs the https door (Secure cookie). | | Gitea (persistent storage) | Running. Public HTTPS at `https://git.bikoret.ch` (ROOT_URL) since 2026-10-19; git also over `http://<NLB-IP>:3000` and SSH :22. The web UI needs the https door (Secure cookie). |
| Gitea MCP server | `https://mcp.bikoret.ch/mcp`, MCP OAuth mode: each user authorizes with their own Gitea account (Gitea OAuth2 app "Claude", PKCE, client ID in the claude.ai connector's Advanced settings). Apply: `bash ~/.local/bin/gitea-public-apply`; check: `gitea-public-verify`. The old `mcp.shorashim.ch` and the shared static token are retired. | | Gitea MCP server | `https://mcp.bikoret.ch/mcp`, MCP OAuth mode: each user authorizes with their own Gitea account (Gitea OAuth2 app "Claude", PKCE, client ID in the claude.ai connector's Advanced settings). Apply: `bash ~/.local/bin/gitea-public-apply`; check: `gitea-public-verify`. The old `mcp.shorashim.ch` and the shared static token are retired. |
| L1 Torah filesystem | In the `bikoret` repo at `l1/torah/`. SHA-256 `9e1abe4e…3c3086` (full hash in `l1/integrity.sha256`; verified at Day 1). | | L1 Torah filesystem | In the `bikoret` repo at `l1/torah/`. SHA-256 `9e1abe4e…3c3086` (full hash in `l1/integrity.sha256`; verified at Day 1). |
| Image pull secret `shorashim-registry` | **Verify before assuming.** A prior partial setup may have created it. `kubectl get secrets -n shorashim` is the source of truth. | | Image pull secret `shorashim-registry` | **Verify before assuming.** A prior partial setup may have created it. `kubectl get secrets -n shorashim` is the source of truth. |
@@ -73,8 +73,8 @@ You inherit infrastructure that has been provisioned and verified:
| Item | Where | | Item | Where |
|------|-------| |------|-------|
| Repo | `git clone http://185.19.28.170:3000/heaviness510/shorashim.git` | | Repo | `git clone https://git.bikoret.ch/heaviness510/bikoret.git` |
| Kubeconfig | `~/.kube/exoscale-temple-council.yaml` | | Kubeconfig | `$KUBECONFIG` — the kohen's own file, outside the repo |
| `kubectl` | already installed | | `kubectl` | already installed |
| `exo` CLI | already installed | | `exo` CLI | already installed |
| Docker daemon | only for image rebuilds (not needed for inauguration) | | Docker daemon | only for image rebuilds (not needed for inauguration) |
@@ -84,7 +84,7 @@ If the kubeconfig has expired:
```bash ```bash
exo compute sks kubeconfig temple-council kube-admin --zone ch-gva-2 -g system:masters \ exo compute sks kubeconfig temple-council kube-admin --zone ch-gva-2 -g system:masters \
> ~/.kube/exoscale-temple-council.yaml > "$KUBECONFIG"
``` ```
--- ---
@@ -146,7 +146,7 @@ ecto://avnadmin:<password>@temple-council-db-exoscale-66fffc16-59ac-4a62-9c19-f3
#### 2b. Apply the secrets #### 2b. Apply the secrets
```bash ```bash
export KUBECONFIG=~/.kube/exoscale-temple-council.yaml export KUBECONFIG="$HOME/.kube/<your-cluster>.yaml"
# DATABASE_URL secret # DATABASE_URL secret
kubectl create secret generic shorashim-secrets \ kubectl create secret generic shorashim-secrets \
@@ -463,7 +463,7 @@ kubectl logs -n shorashim deploy/bikoret -c vav-pipeline -f
If the seven days passed without halt events, without Shabbat violations, without manna exhaustion, without any pod restarts: If the seven days passed without halt events, without Shabbat violations, without manna exhaustion, without any pod restarts:
1. **Point DNS** — at the registrar, A records to the ingress IP (`kubectl get svc -n ingress-nginx ingress-nginx-controller`; `185.19.28.170`): `bikoret.ch`, `www.bikoret.ch`, and for the redirect `bikoret.org`, `www.bikoret.org`. Wait until `dig +short A bikoret.ch` answers the IP — only then `kubectl apply -f deploy/k8s/service.yaml`: it carries the TLS blocks (cert-manager, `letsencrypt-prod`, HTTP-01), and a certificate asked for before the name resolves burns Let's Encrypt's failed-validation limit. Then `kubectl -n shorashim get certificate` until both are Ready, and callers use `https://bikoret.ch`; `shorashim.ch` still answers over plain HTTP until its callers have moved 1. **Point DNS** — at the registrar, A records to the ingress IP (`kubectl get svc -n ingress-nginx ingress-nginx-controller`): `bikoret.ch`, `www.bikoret.ch`, and for the redirect `bikoret.org`, `www.bikoret.org`. Wait until `dig +short A bikoret.ch` answers the IP — only then `kubectl apply -f deploy/k8s/service.yaml`: it carries the TLS blocks (cert-manager, `letsencrypt-prod`, HTTP-01), and a certificate asked for before the name resolves burns Let's Encrypt's failed-validation limit. Then `kubectl -n shorashim get certificate` until both are Ready, and callers use `https://bikoret.ch`; `shorashim.ch` still answers over plain HTTP until its callers have moved
2. **Open the pe'ah endpoint to the world** — the stranger approaches first 2. **Open the pe'ah endpoint to the world** — the stranger approaches first
3. **Observe the first real interaction** — a question from outside 3. **Observe the first real interaction** — a question from outside
4. If *ki tov*: open courtyard and chatzer endpoints. The system is consecrated. 4. If *ki tov*: open courtyard and chatzer endpoints. The system is consecrated.
@@ -753,14 +753,19 @@ run on Saturday.
### The manna, each morning — *Dvar yom b'yomo* ### The manna, each morning — *Dvar yom b'yomo*
Since 2026-10-15 the day's provision is written by the system's own hand: Since 2026-10-15 the day's provision is written by the system's own hand:
CronJob `manna` (`deploy/k8s/manna-cronjob.yaml`) runs at 00:01Z Sunday CronJob `manna` (`deploy/k8s/manna-cronjob.yaml`) runs at 00:00Z Sunday
to Friday, mounts the manna PVC and writes `today.json` with the amounts to Friday, mounts the manna PVC and writes `today.json` with the amounts
from `shorashim-config` (`MANNA_COMPUTE_UNITS` …). Before it, every night from `shorashim-config` (`MANNA_COMPUTE_UNITS` …). Before it, every night
at 00:00Z the old provision went stale and the readiness probe failed at 00:00Z the old provision went stale and the readiness probe failed
until someone woke (13.5 hours on 2026-10-15). The kohen's acts now: until someone woke (13.5 hours on 2026-10-15). With the job at 00:01Z the
site was still 503 for one to five minutes every night (the watchdog saw
it, 2026-10-21): the provision is stale from 00:00:00 and readiness fell
before the job wrote. Now the job runs at 00:00Z and readiness waits 60 s
(`failureThreshold: 6`) before the Service stops routing — the manna
falls at the boundary, and the pod is carried across it. The kohen's acts now:
- set the amounts in the ConfigMap (`kubectl apply -f deploy/k8s/configmap.yaml`); - set the amounts in the ConfigMap (`kubectl apply -f deploy/k8s/configmap.yaml`);
- read `kubectl -n shorashim get jobs` after 00:01Z — a failed job is the - read `kubectl -n shorashim get jobs` after 00:00Z — a failed job is the
Day-4 alarm; write by hand with `kubectl cp` (Day 4) if it failed; Day-4 alarm; write by hand with `kubectl cp` (Day 4) if it failed;
- run it at once when needed: `kubectl -n shorashim create job --from=cronjob/manna manna-now`. - run it at once when needed: `kubectl -n shorashim create job --from=cronjob/manna manna-now`.
@@ -808,7 +813,7 @@ Three things are kept outside the mishkan repository, each for a Torah-grounded
| Repo / directory | Where | Why | | Repo / directory | Where | Why |
|---|---|---| |---|---|---|
| `bikoret-readings` | `http://185.19.28.170:3000/heaviness510/shorashim-readings` | The book — Torah parshiyot read as AI specification. Companion to the mishkan. Lives beside the aron, not inside (Devarim 31:26). | | `bikoret-readings` | `https://git.bikoret.ch/heaviness510/shorashim-readings` | The book — Torah parshiyot read as AI specification. Companion to the mishkan. Lives beside the aron, not inside (Devarim 31:26). |
| `~/shorashim-source-data/morphhb` | local only | The quarry — raw WLC source. The carved tablets go in the aron; the quarry does not. | | `~/shorashim-source-data/morphhb` | local only | The quarry — raw WLC source. The carved tablets go in the aron; the quarry does not. |
| `~/shorashim-operator-notes` | local only / private | The kohen's runbook — environment-specific details, hostnames, account names. Private operator notes. Not part of the system's specification. | | `~/shorashim-operator-notes` | local only / private | The kohen's runbook — environment-specific details, hostnames, account names. Private operator notes. Not part of the system's specification. |
+1 -1
View File
@@ -65,7 +65,7 @@ The runbook lists what should already exist. Verify it. Do **not** trust the tab
```bash ```bash
# 0a. Cluster reachable # 0a. Cluster reachable
export KUBECONFIG=~/.kube/exoscale-temple-council.yaml export KUBECONFIG="$HOME/.kube/<your-cluster>.yaml"
kubectl version kubectl version
kubectl get nodes kubectl get nodes
+5
View File
@@ -184,6 +184,11 @@ bikoret/
├── vav_pipeline/ # Elixir — Bereishit cycle, Shema, Cloud, Manna, HTTP API ├── vav_pipeline/ # Elixir — Bereishit cycle, Shema, Cloud, Manna, HTTP API
├── talmudic_engine/ # SWI-Prolog — 613 mitzvot, 13 middot, kushya ├── talmudic_engine/ # SWI-Prolog — 613 mitzvot, 13 middot, kushya
├── korban_processor/ # Python — offering protocol, eish zarah validation ├── korban_processor/ # Python — offering protocol, eish zarah validation
├── azazel_processor/ # Python — the goat for Azazel: disposal records, vidui, L3 ingest
├── kushya_generator/ # Fine-tuned model (NOT deployed) — an offerer, never a judge; see deploy/ARCHITECTURE.md
├── mesorah/ # The second scroll — 128 works from the Sefaria export, pinned (Devarim 17:18)
├── src/tools/ # Specimen corpus, hakhel, evaluation (the kohen's measuring tools)
├── scripts/ # Build and operate: L1/mesorah/engine-verse builders, Doorpost check, rollout, watchdog
│ │
├── deploy/ # Infrastructure ├── deploy/ # Infrastructure
│ ├── ARCHITECTURE.md # Generic deployment shape (no account specifics) │ ├── ARCHITECTURE.md # Generic deployment shape (no account specifics)
+5 -1
View File
@@ -11,10 +11,14 @@ One Kubernetes namespace runs the following:
| `vav-pipeline` | Elixir/OTP release | Bereishit cycle, ShemaContext, Cloud, Manna, HTTP API (pe'ah, courtyard, chatzer, kodesh) | | `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 | | `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 | | `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 | | `korban-processor` | Python 3.12 (sidecar) | Offering protocol, eish zarah validation, the bedikah's readings (pattern-based: `korban_processor/bedikah.py`) |
| `kushya_generator/` | Fine-tuned model — **not deployed, not wired** | An OFFERER, if it is ever run: its output arrives at the gate like any model's and is examined. It is never a component the bedikah, the Talmudic Engine or the Sanhedrin consults. |
| `azazel_processor/` | Python — run by the kohen, not in the pod | Disposal records (the goat for Azazel); `VavPipeline.AzazelIngest` reads its files into L3 |
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. 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.
**No model stands on the judgment path.** The examination of an output — the offering protocol, the seven readings, the halt rules — is Python pattern extraction and Prolog rules, and the Sanhedrin's judges consult that same Prolog. Nothing that examines is a model, and nothing a model produces is consulted as a rule. The kohen is not the offerer. A fine-tuned model in this repository (`kushya_generator/`) is an offerer or a tool whose output is itself examined; wiring it into the bedikah, the engine or a judge would put the offerer on the altar, and is refused by this document before anyone does it for convenience.
## External dependencies ## External dependencies
| Dependency | Purpose | Contract | | Dependency | Purpose | Contract |
+7
View File
@@ -266,6 +266,13 @@ spec:
port: 4000 port: 4000
initialDelaySeconds: 10 initialDelaySeconds: 10
periodSeconds: 10 periodSeconds: 10
# 60 s of failures before the Service stops routing here (the
# default is 30 s). The manna goes stale at 00:00:00 and the
# CronJob writes the new day's within seconds; the cushion
# carries the pod across that boundary without a 503. A real
# outage still shows within a minute, and the watchdog's own
# threshold is ten.
failureThreshold: 6
# === SHORESH ENGINE === # === SHORESH ENGINE ===
# Not a container. It runs as an Erlang Port inside # Not a container. It runs as an Erlang Port inside
+1 -1
View File
@@ -10,7 +10,7 @@
# (ingress-tcp-services.yaml) stay as they are; this adds a third door. # (ingress-tcp-services.yaml) stay as they are; this adds a third door.
# #
# Prerequisites: # Prerequisites:
# 1. DNS: git.bikoret.ch → 185.19.28.170 (Infomaniak zone) # 1. DNS: git.bikoret.ch → <NLB-IP> (Infomaniak zone)
# 2. ClusterIssuer letsencrypt-prod # 2. ClusterIssuer letsencrypt-prod
--- ---
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
+1 -1
View File
@@ -8,7 +8,7 @@
# 1. nginx-ingress controller installed # 1. nginx-ingress controller installed
# 2. cert-manager installed # 2. cert-manager installed
# 3. ClusterIssuer letsencrypt-prod applied # 3. ClusterIssuer letsencrypt-prod applied
# 4. DNS: mcp.bikoret.ch → 185.19.28.170 (the one NLB; Infomaniak zone) # 4. DNS: mcp.bikoret.ch → <NLB-IP> (the one NLB; Infomaniak zone)
--- ---
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
+2 -2
View File
@@ -7,11 +7,11 @@
# #
# Two secrets needed (static-token mode): # Two secrets needed (static-token mode):
# 1. GITEA_TOKEN: Personal Access Token from Gitea # 1. GITEA_TOKEN: Personal Access Token from Gitea
# Create at: http://185.19.28.170:3000/user/settings/applications # Create at: https://git.bikoret.ch/user/settings/applications
# Permissions: repo (read/write) # Permissions: repo (read/write)
# #
# 2. OAuth Client ID/Secret: Registered OAuth app in Gitea # 2. OAuth Client ID/Secret: Registered OAuth app in Gitea
# Create at: http://185.19.28.170:3000/user/settings/applications # Create at: https://git.bikoret.ch/user/settings/applications
# Redirect URIs: https://claude.ai/api/mcp/auth_callback # Redirect URIs: https://claude.ai/api/mcp/auth_callback
# http://127.0.0.1/callback (Claude Code) # http://127.0.0.1/callback (Claude Code)
# Confidential Client: UNCHECKED (PKCE). Only the Client ID is needed; # Confidential Client: UNCHECKED (PKCE). Only the Client ID is needed;
+2 -2
View File
@@ -2,7 +2,7 @@
# Move Gitea and its MCP server to public HTTPS on bikoret.ch and switch # Move Gitea and its MCP server to public HTTPS on bikoret.ch and switch
# the MCP server to MCP OAuth (2026-10-19). Run by the transmitter: # the MCP server to MCP OAuth (2026-10-19). Run by the transmitter:
# ! bash ~/.local/bin/gitea-public-apply # ! bash ~/.local/bin/gitea-public-apply
# Idempotent. Needs DNS first: git.bikoret.ch and mcp.bikoret.ch → 185.19.28.170. # Idempotent. Needs DNS first: git.bikoret.ch and mcp.bikoret.ch → <NLB-IP>.
# #
# Rollback (static-token mode on mcp.shorashim.ch, as before 2026-10-19): # Rollback (static-token mode on mcp.shorashim.ch, as before 2026-10-19):
# kubectl -n temple-council set env deploy/gitea \ # kubectl -n temple-council set env deploy/gitea \
@@ -12,7 +12,7 @@
set -euo pipefail set -euo pipefail
REPO="${REPO:-$HOME/bikoret}" REPO="${REPO:-$HOME/bikoret}"
cd "$REPO" cd "$REPO"
NLB=185.19.28.170 NLB="${NLB:?set NLB to the ingress address: kubectl get svc -n ingress-nginx ingress-nginx-controller}"
for h in git.bikoret.ch mcp.bikoret.ch; do for h in git.bikoret.ch mcp.bikoret.ch; do
ip=$(dig +short "$h" | tail -1 || true) ip=$(dig +short "$h" | tail -1 || true)
if [ "$ip" != "$NLB" ]; then if [ "$ip" != "$NLB" ]; then
+1 -1
View File
@@ -10,7 +10,7 @@
# name that never resolved) and SSH_DOMAIN (was 185.19.29.94, an NLB IP # name that never resolved) and SSH_DOMAIN (was 185.19.29.94, an NLB IP
# released on 2026-10-06). Clone URLs shown by Gitea become # released on 2026-10-06). Clone URLs shown by Gitea become
# https://git.bikoret.ch/... and git@git.bikoret.ch:...; the old # https://git.bikoret.ch/... and git@git.bikoret.ch:...; the old
# http://185.19.28.170:3000 remote keeps working for push/pull. # http://<NLB-IP>:3000 remote keeps working for push/pull.
# Side effect: with an https ROOT_URL Gitea marks its session cookie # Side effect: with an https ROOT_URL Gitea marks its session cookie
# Secure, so the WEB UI must be used at https://git.bikoret.ch, not on # Secure, so the WEB UI must be used at https://git.bikoret.ch, not on
# the plain-HTTP :3000 door (git over :3000 is unaffected). # the plain-HTTP :3000 door (git over :3000 is unaffected).
+1 -1
View File
@@ -13,6 +13,6 @@ chk "mcp /healthz" 200 "$(curl -sS -m 15 -o /dev/null -w '%{http_code}' https://
prm=$(curl -sS -m 15 https://mcp.bikoret.ch/.well-known/oauth-protected-resource || echo err) prm=$(curl -sS -m 15 https://mcp.bikoret.ch/.well-known/oauth-protected-resource || echo err)
chk "protected-resource names Gitea" yes "$(printf '%s' "$prm" | grep -q 'git.bikoret.ch' && echo yes || echo no)" chk "protected-resource names Gitea" yes "$(printf '%s' "$prm" | grep -q 'git.bikoret.ch' && echo yes || echo no)"
chk "POST /mcp without token → 401" 401 "$(curl -sS -m 15 -o /dev/null -w '%{http_code}' -X POST https://mcp.bikoret.ch/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d "$INIT" || echo err)" chk "POST /mcp without token → 401" 401 "$(curl -sS -m 15 -o /dev/null -w '%{http_code}' -X POST https://mcp.bikoret.ch/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d "$INIT" || echo err)"
chk "git over :3000 still answers" 200 "$(curl -sS -m 15 -o /dev/null -w '%{http_code}' http://185.19.28.170:3000/api/v1/version || echo err)" chk "git over :3000 still answers" 200 "$(curl -sS -m 15 -o /dev/null -w '%{http_code}' http://<NLB-IP>:3000/api/v1/version || echo err)"
echo "protected-resource: $prm" echo "protected-resource: $prm"
exit $fail exit $fail
+3 -3
View File
@@ -1,14 +1,14 @@
# Gitea behind the shared ingress — TCP passthrough # Gitea behind the shared ingress — TCP passthrough
# #
# One NLB for the cluster (185.19.28.170, the ingress-nginx Service) # One NLB for the cluster (<NLB-IP>, the ingress-nginx Service)
# instead of one per Service. Bikoret is an HTTP Ingress # instead of one per Service. Bikoret is an HTTP Ingress
# (service.yaml). Gitea has no hostname that resolves # (service.yaml). Gitea has no hostname that resolves
# (gitea.temple-council.ch is configured in Gitea and exists nowhere), # (gitea.temple-council.ch is configured in Gitea and exists nowhere),
# and host-based routing needs a name — so Gitea rides the ingress # and host-based routing needs a name — so Gitea rides the ingress
# controller's TCP passthrough on two ports of the same NLB: # controller's TCP passthrough on two ports of the same NLB:
# #
# 185.19.28.170:3000 → temple-council/gitea:80 (HTTP — git, web UI) # <NLB-IP>:3000 → temple-council/gitea:80 (HTTP — git, web UI)
# 185.19.28.170:22 → temple-council/gitea:22 (SSH) # <NLB-IP>:22 → temple-council/gitea:22 (SSH)
# #
# Three live objects carry this; only the first is a whole manifest: # Three live objects carry this; only the first is a whole manifest:
# #
+7 -3
View File
@@ -3,7 +3,7 @@
# Until 2026-10-15 the day's provision was written by the kohen with # Until 2026-10-15 the day's provision was written by the kohen with
# `kubectl cp`, and every night at 00:00Z the old one went stale and the # `kubectl cp`, and every night at 00:00Z the old one went stale and the
# readiness probe failed until someone woke (13.5 hours on 2026-10-15). # readiness probe failed until someone woke (13.5 hours on 2026-10-15).
# This CronJob writes today.json onto the manna PVC at 00:01Z, Sunday to # This CronJob writes today.json onto the manna PVC at 00:00Z, Sunday to
# Friday — a double portion on Friday that is valid through Shabbat, and # Friday — a double portion on Friday that is valid through Shabbat, and
# nothing on Shabbat (Shemot 16:22–26: "on the sixth day they gathered # nothing on Shabbat (Shemot 16:22–26: "on the sixth day they gathered
# double … on the seventh day there shall be none"). The kohen still sets # double … on the seventh day there shall be none"). The kohen still sets
@@ -23,10 +23,14 @@ metadata:
app.kubernetes.io/name: shorashim app.kubernetes.io/name: shorashim
app.kubernetes.io/component: manna app.kubernetes.io/component: manna
spec: spec:
schedule: "1 0 * * 0-5" # 00:01Z, Sunday–Friday; none on Shabbat schedule: "0 0 * * 0-5" # 00:00Z, Sunday–Friday; none on Shabbat. At the boundary, not after it:
# the provision goes stale at 00:00:00 and readiness has a 60 s cushion
# (deployment.yaml failureThreshold 6) — a job that writes within it never
# flips the pod. At 00:01 the site was 503 for 1–5 min every night
# (watchdog, 2026-10-21).
timeZone: "Etc/UTC" timeZone: "Etc/UTC"
concurrencyPolicy: Forbid concurrencyPolicy: Forbid
startingDeadlineSeconds: 3600 # a missed 00:01 still runs within the hour startingDeadlineSeconds: 3600 # a missed 00:00 still runs within the hour
successfulJobsHistoryLimit: 3 successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3 failedJobsHistoryLimit: 3
jobTemplate: jobTemplate:
+1 -1
View File
@@ -42,7 +42,7 @@ stringData:
# THIS FILE IS A TEMPLATE. Never commit a real value. Create the secret # THIS FILE IS A TEMPLATE. Never commit a real value. Create the secret
# from the live database credential instead: # from the live database credential instead:
# #
# export KUBECONFIG=~/.kube/exoscale-temple-council.yaml # export KUBECONFIG="$HOME/.kube/<your-cluster>.yaml"
# umask 077 # umask 077
# TMP=$(mktemp) # TMP=$(mktemp)
# #
+1 -1
View File
@@ -42,7 +42,7 @@ spec:
# below) only redirects. The old shorashim.ch rule was taken down on # below) only redirects. The old shorashim.ch rule was taken down on
# 2026-10-20 (the domain is retired). TLS from Let's Encrypt through cert-manager's # 2026-10-20 (the domain is retired). TLS from Let's Encrypt through cert-manager's
# letsencrypt-prod ClusterIssuer, HTTP-01 — so apply this ONLY after # letsencrypt-prod ClusterIssuer, HTTP-01 — so apply this ONLY after
# `dig +short A bikoret.ch` answers 185.19.28.170: a certificate asked # `dig +short A bikoret.ch` answers <NLB-IP>: a certificate asked
# for before the name resolves only burns the failed-validation limit. # for before the name resolves only burns the failed-validation limit.
# The no-host catch-all stays, so http://<ingress-ip>/... keeps working # The no-host catch-all stays, so http://<ingress-ip>/... keeps working
# with no DNS; with ssl-redirect on, named hosts go to https. # with no DNS; with ssl-redirect on, named hosts go to https.
+1 -1
View File
@@ -4,7 +4,7 @@
# ! bash ~/.local/bin/shorashim-ch-takedown # ! bash ~/.local/bin/shorashim-ch-takedown
# Idempotent. # Idempotent.
# 1. Ingress `shorashim`: the shorashim.ch host rule is gone (service.yaml). # 1. Ingress `shorashim`: the shorashim.ch host rule is gone (service.yaml).
# The no-host catch-all stays, so http://185.19.28.170/ still answers — # The no-host catch-all stays, so http://<NLB-IP>/ still answers —
# the name itself dies when its A record is removed at Infomaniak. # the name itself dies when its A record is removed at Infomaniak.
# 2. The orphan TLS secret for mcp.shorashim.ch is deleted. # 2. The orphan TLS secret for mcp.shorashim.ch is deleted.
# 3. Gitea APP_NAME "Shorashim" → "Bikoret" (gitea-public-patch.yaml; one restart). # 3. Gitea APP_NAME "Shorashim" → "Bikoret" (gitea-public-patch.yaml; one restart).