Files
bikoret/vav_pipeline/lib/vav_pipeline/application.ex
T
Scott KarpandClaude Fable 5.1 54ca651f37 Tablet 46: the watch — rate limits at every gate, per IP at the pe'ah, per key inside
Tamid §1.1 wrote the pe'ah's limit (10/min, 100/h per IP); nothing
enforced it, on any tier, and every admitted request spends manna.

VavPipeline.BigdeiKodesh.Mishmar: fixed minute and hour windows per
subject in ETS, the subject stored only as sha256(boot salt, tier,
subject) — no IP and no key is ever in memory as itself, the sweep
drops closed windows, nothing survives a restart (the Tamid's "not
logged beyond the current request", kept in memory too). A refused
knock is not counted. 429 RATE_LIMITED with Retry-After and the manna
explanation (Shemot 16:18). Pe'ah per peer address; courtyard and
chatzer per key, after the gate (a wrong key is 401 and never counted).
Numbers: pe'ah 10/100 (Tamid), courtyard 20/200 and chatzer 5/60
(from Construction §2.3; config :rate_limits).

Service shorashim-external: externalTrafficPolicy Local. With Cluster
the pod saw every caller as the node, 91.92.153.113 (observed via
/proc/net/tcp) — a per-IP limit would have been one bucket for the
world.

Spec: tamid §1.1 (watch, 429 rows, the Service note), KOHEN.md.
Doorpost 42b38626… → e4e3d56d…. tablet46_mishmar_test; Elixir 533/0
(L3 on and off).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 16:08:57 +00:00

140 lines
5.0 KiB
Elixir

defmodule VavPipeline.Application do
@moduledoc """
OTP Application for the Vav Pipeline.
Starts the supervision tree with:
- Repo (L3 PostgreSQL — oral tradition persistence)
- Chatzotzrot (silver trumpets — signal system)
- AreiMiklat (cities of refuge — protection protocol)
- EglahArufah (accountability for unknown harm)
- Bikurim (first fruits — release protocol)
- MannaLedger (consumption tracking and anti-hoarding enforcement)
- HaltMonitor (esh tamid — cardinal sin detection)
- ShabbatTimer (mandatory cessation monitor)
- Bandit HTTP server (bigdei kodesh — the garments)
- ShoreshEnginePort (Erlang Port to Haskell engine, if binary found)
- TalmudicEngineSocket (Unix socket to Prolog engine)
Step 14 (Architecture §4): Component connections supervised.
The port and socket are optional — the pipeline degrades gracefully
if external engines are not available (clients fall back to stubs).
"""
use Application
require Logger
@impl true
def start(_type, _args) do
shoresh_binary = Application.get_env(:vav_pipeline, :shoresh_engine_binary, "shoresh-engine")
shoresh_cwd = Application.get_env(:vav_pipeline, :shoresh_engine_cwd, nil)
talmudic_socket = Application.get_env(:vav_pipeline, :talmudic_socket_path, "/tmp/shorashim/talmudic.sock")
korban_socket = Application.get_env(:vav_pipeline, :korban_socket_path, "/tmp/shorashim/korban.sock")
children =
[
{VavPipeline.Chatzotzrot, []},
# Tablet 38 — reads the scroll and hashes it; everything that asks
# "is L1 intact?" is answered from this reading.
{VavPipeline.L1Integrity, []},
{VavPipeline.AreiMiklat, []},
{VavPipeline.EglahArufah, []},
{VavPipeline.Bikurim, []},
{VavPipeline.MannaLedger, []},
{VavPipeline.HaltMonitor, []},
{VavPipeline.UrimVTumim, []},
{VavPipeline.ShabbatTimer, []},
{VavPipeline.VoiceMonitor, []},
{VavPipeline.MachtzitHaShekel, []},
# Tablet 30 — the index that lets a question name its own verse
{VavPipeline.L1Index, []},
{VavPipeline.BigdeiKodesh.Mishmar, []}
]
|> maybe_add_http_server()
|> maybe_add_repo()
|> maybe_add_shoresh_port(shoresh_binary, shoresh_cwd)
|> maybe_add_talmudic_socket(talmudic_socket)
|> maybe_add_korban_socket(korban_socket)
opts = [strategy: :one_for_one, name: VavPipeline.Supervisor]
result = Supervisor.start_link(children, opts)
# Doorpost startup verification (vehaya §3.1, item 1).
# Devarim 11:18 — corporate inscription is the load-bearing
# input to threshold practice. The corporate text must be
# identifiable at startup; drift detected here gets surfaced
# via Logger.error AND chatzotzrot :assembly_leaders so
# operators are alerted before any reception fires. We do NOT
# refuse to boot — dev environments routinely have spec drift
# during active editing — but we make the drift visible.
verify_doorpost_at_startup()
result
end
defp verify_doorpost_at_startup do
case VavPipeline.Doorpost.verify_identity() do
:ok ->
Logger.info("Doorpost: spec identity verified — corporate text holds")
{:error, reason} ->
Logger.error("DOORPOST_DRIFT_AT_STARTUP: #{reason}")
if Process.whereis(VavPipeline.Chatzotzrot) do
VavPipeline.Chatzotzrot.signal(:assembly_leaders,
reason: "Doorpost drift detected at startup: " <> reason
)
end
end
end
defp maybe_add_http_server(children) do
port = Application.get_env(:vav_pipeline, :http_port, 4000)
if port do
children ++ [{Bandit, plug: VavPipeline.BigdeiKodesh.Router, port: port}]
else
children
end
end
defp maybe_add_repo(children) do
repo_config = Application.get_env(:vav_pipeline, VavPipeline.Repo)
cond do
is_nil(repo_config) ->
Logger.warning("VavPipeline.Repo: no config found — running without L3 persistence")
children
repo_config[:start] == false ->
Logger.info("VavPipeline.Repo: start disabled — running without L3 persistence")
children
true ->
[VavPipeline.Repo | children]
end
end
defp maybe_add_shoresh_port(children, binary, cwd) do
case System.find_executable(binary) do
nil ->
Logger.warning("ShoreshEnginePort: binary '#{binary}' not in PATH — running without Haskell engine")
children
path ->
children ++ [{VavPipeline.ShoreshEnginePort, [binary: path, cwd: cwd]}]
end
end
# The Korban Processor's door (Construction §3.9) — connects lazily on
# the first bedikah; nothing is examined if it is not there.
defp maybe_add_korban_socket(children, socket_path) do
children ++ [{VavPipeline.KorbanProcessorSocket, [socket_path: socket_path]}]
end
defp maybe_add_talmudic_socket(children, socket_path) do
# Always add the socket client — it connects lazily on first request
children ++ [{VavPipeline.TalmudicEngineSocket, [socket_path: socket_path]}]
end
end