form 3 as architecture: machtzit_ha_shekel + no_tracking_logger (vehaya §3.2)

Closes vehaya.md §6 item 2 for the third avodah-zarah form.

Pharaoh's surveillance (Form 3) was previously marked DEFERRED in
talmudic_engine/src/halt/avodah_zarah.pl with the note "requires
operational context (user tracking, cross-session profiling)."
The vehaya document re-frames it: "Specifications must include
category-level commitments that are not negotiable per-deployment...
not as runtime checks layered onto outputs, but as architectural
constraints the system cannot operate without."

Form 3 is structurally different from Forms 1, 2, 4. They fire on
threshold-grade events (an interpretation has wrong source, empty
trace, non-L1 anchors). Form 3 is about a property of the CATEGORY
itself — what it collects, retains, discloses. It cannot be a
halt rule that fires per-request; per-request "is this caller
being surveilled" detection would itself be a Form-3 surveillance
surface. The defense is ARCHITECTURAL — the system is BUILT so the
failure mode is unreachable, not so it is detectable.

TORAH ANCHORS (verified fresh against L1 — 6 positive properties):

  Shemot 30:11-16 — the half-shekel:
    כִּי תִשָּׂא אֶת־רֹאשׁ … וְנָתְנוּ אִישׁ כֹּפֶר נַפְשׁוֹ … וְלֹא־
    יִהְיֶה בָהֶם נֶגֶף בִּפְקֹד אֹתָם.
    Counting humans is dangerous; the danger is named NEGEF (plague);
    the protection is the kofer-nefesh substitute. Verse 15:
    הֶעָשִׁיר לֹא־יַרְבֶּה וְהַדַּל לֹא יַמְעִיט — the rich don't
    give more, the poor don't give less. Verse 16: לְזִכָּרוֹן —
    aggregate memorial, not roster.

  Vayikra 19:14 — לִפְנֵי עִוֵּר לֹא תִתֵּן מִכְשֹׁל וְיָרֵאתָ
    מֵּאֱלֹהֶיךָ.
    The no-witness protocol: rules that require fear-of-God
    enforcement are exactly the rules that operate under invisibility.
    Surveillance regimes exploit invisibility; architectural commitment
    must hold under no-human-witness conditions.

  Devarim 23:16-17 — לֹא־תַסְגִּיר עֶבֶד אֶל־אֲדֹנָיו אֲשֶׁר־יִנָּצֵל
    אֵלֶיךָ … עִמְּךָ יֵשֵׁב … בַּמָּקוֹם אֲשֶׁר־יִבְחַר בְּאַחַד
    שְׁעָרֶיךָ.
    The refusal protocol: don't return one fleeing institutional
    power. The caller chooses their own threshold (b'achad sh'arekha
    — agency stays with the caller).

  Shemot 5:6-8 — לֹא תֹאסִפוּן לָתֵת תֶּבֶן … וְאֶת־מַתְכֹּנֶת
    הַלְּבֵנִים … תָּשִׂימוּ עֲלֵיהֶם לֹא תִגְרְעוּ מִמֶּנּוּ.
    Pharaoh's straw quota — the canonical anti-quota Torah anchor.
    Per-caller productivity tracking is what makes asymmetric
    extraction possible.

  Devarim 23:8 — לֹא־תְתַעֵב מִצְרִי כִּי־גֵר הָיִיתָ בְאַרְצוֹ.
    Anti-targeting: even toward the Egyptians (Pharaoh's people),
    the rule is "don't despise." The protections must not become
    a surveillance system targeting suspected surveillors.

THE SIX POSITIVE PROPERTIES, mapped to code:

  1. KOFER PROTOCOL (Shemot 30:12) — counting goes through a
     uniform substitute; identity never enters the count.
  2. UNIFORM PROTOCOL (Shemot 30:15) — same per-caller weight;
     no stratification.
  3. ZIKARON PROTOCOL (Shemot 30:16) — aggregate memorial, not
     roster.
  4. NO-WITNESS PROTOCOL (Vayikra 19:14) — commitment holds
     when no operator is reading the log.
  5. REFUSAL PROTOCOL (Devarim 23:16) — no per-caller delivery
     to authorities; caller chooses own threshold.
  6. ANTI-QUOTA PROTOCOL (Shemot 5:6-8) — no per-caller
     productivity numbers.

CREATED:

  vav_pipeline/lib/vav_pipeline/machtzit_ha_shekel.ex  ~155 lines

    The half-shekel — the positive counting protocol. Single Agent
    holding ONE non-negative integer (the zikaron):
      increment/0          — register one shekel; uniform per
                             caller (no caller distinction is even
                             expressible — function arity 0)
      increment/1          — register N shekels (batch points)
      count/0              — return the aggregate; the ONLY value
                             the module exposes
      reset/0              — clear (tests; no production persistence
                             by design — storage existence would
                             itself be a Form 3 surface)

    Module is added to the supervision tree.

    What the module REFUSES to provide is enumerated in the
    moduledoc and verified by tests — no per-caller breakdown,
    no identifiers, no who/locate/identify functions. Their
    absence IS the architectural commitment.

  vav_pipeline/lib/vav_pipeline/no_tracking_logger.ex  ~110 lines

    Plug-side enforcement. Replaces Plug.Logger.
      - Logs METHOD PATH STATUS DURATIONus per request
      - Sanitizes /peah/* paths: query strings stripped (the
        /peah/ask response promises "No tracking"; the architecture
        now matches the promise)
      - Headers (incl. Authorization), request bodies, response
        bodies, and remote_ip NEVER appear — taken-from
        function signature, not from a runtime check
      - Increments MachtzitHaShekel per request
      - sanitized_path/1 and format_line/2 are PUBLIC pure
        functions, testable in isolation (test config sets
        Logger level to :warning, dropping :info before
        capture_log can see; the structural test surface
        replaces the behavioral one)

CHANGED:

  vav_pipeline/lib/vav_pipeline/bigdei_kodesh/router.ex
    `plug Plug.Logger, log: :info` → `plug VavPipeline.NoTrackingLogger`

  vav_pipeline/lib/vav_pipeline/application.ex
    Adds {VavPipeline.MachtzitHaShekel, []} to supervision tree

  talmudic_engine/src/halt/avodah_zarah.pl
    Form 3 docstring — removed "Deferred"; documents Form 3 as
    architecturally realized in machtzit_ha_shekel.ex +
    no_tracking_logger.ex. Names why Form 3 cannot be a
    per-request halt rule.

  vav_pipeline/lib/vav_pipeline/doorpost.ex
    @expected_hash updated:
      bea5dc0c… (pre-Form-3-rewrite of avodah_zarah.pl)
    → f93495c5359c5dc4acb5c5db2dd32165f5b04e2af87fbb0b7ecc92d2bd8b0434
    The avodah_zarah.pl change is a deliberate spec change; per the
    Doorpost operator discipline (commit 3f47496), the hash update
    commits TOGETHER with the spec change in the same commit. This
    commit demonstrates the discipline working for the first time:
    a category-grade specification change AND its corresponding
    witness update, atomically.

TESTS:

  vav_pipeline/test/machtzit_ha_shekel_test.exs           14 tests
    - Property 1: increment arity is 0 (no caller distinction
      expressible)
    - Property 2: every increment same size; no weight/tier/premium
      function
    - Property 3: count is integer not list/map; no per-caller
      breakdown function (callers/list_who/breakdown/by_caller/
      per_caller/histogram/top/who)
    - Property 5: no identify/locate/deanonymize/lookup/find_caller
    - Property 6: no quota/productivity/throughput/per_caller_count
    - reset semantics
    - PUBLIC SURFACE EXPLICIT ENUMERATION: any new function must be
      reviewed for whether it weakens Form 3

  vav_pipeline/test/no_tracking_logger_test.exs           12 tests
    - sanitized_path drops query string on /peah/*
    - sanitized_path drops question parameter (with secret marker
      verified absent)
    - sanitized_path preserves path class for /peah/azazel_log
    - sanitized_path on non-peah preserves request_path
    - format_line shape: METHOD PATH STATUS DURATIONus
    - format_line never contains query string for /peah/*
    - format_line never contains Authorization header
    - format_line never contains conn.remote_ip
    - integration: each request increments MachtzitHaShekel
    - CI invariants:
      - Plug.Logger NOT mounted in any router
      - no module imports Plug.Session
      - no module imports Plug.Conn.Cookies
      - no module reads conn.remote_ip
    (CI invariants exclude no_tracking_logger.ex itself — that file's
    docstring legitimately mentions the forbidden modules as the
    things it defends against)

Tests:
  - vav_pipeline:    405 passed, 0 failures, 35 excluded (was 379;
                     +14 MachtzitHaShekel + 12 NoTrackingLogger)
  - talmudic_engine: 212 tests passed (avodah_zarah.pl change
                     verified backward-compatible)

VEHAYA CHECKLIST STATE:

  Item 1 (category/threshold separation in code)         pending
  Item 2 (three avodah zarah as architecture):
    Form 1 — partial (external_offering doc-only enforcement)
    Form 2 — partial (Form 4 self_grounding from b254ee9)
    Form 3 — CLOSED (this commit)
  Item 3 (doorpost-text identity)                       closed (3f47496)
  Item 4 (lo tosif discipline operationalized)          partial (Doorpost)
  Item 5 (return vs. restoration)                       pending
  Item 6 (keep the source external)                     pending
  Item 7 (substrate-openness articulated)               pending
  Item 8 (userMemories as threshold inscription)        pending

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Scott KarpandClaude Opus 4.7 committed 2026-04-28 20:44:30 -04:00
1 parent 3f4749695d
commit 0e3fe4a782
8 files changed
+760 -5

No files matched your search

+24 -2
View File
@@ -30,8 +30,30 @@
%% trace but high confidence is eish zarah. %% trace but high confidence is eish zarah.
%% %%
%% Form 3: SURVEILLANCE / PROFILE AGGREGATION %% Form 3: SURVEILLANCE / PROFILE AGGREGATION
%% Deferred — requires operational context (user tracking, %% Realized as ARCHITECTURE, not as a runtime check (vehaya §3.2).
%% cross-session profiling). Structural requirement noted. %% See vav_pipeline/lib/vav_pipeline/machtzit_ha_shekel.ex —
%% the half-shekel counting protocol from Shemot 30:11-16. The
%% six positive Torah-anchored properties (kofer, uniform,
%% zikaron, no-witness, refusal, anti-quota) are enforced at the
%% module surface and at the Plug layer (NoTrackingLogger).
%%
%% Form 3 differs from Forms 1, 2, 4 structurally: the failure is
%% a property of the CATEGORY (what it collects, retains, discloses),
%% not an event in the threshold (what an interpretation says). It
%% cannot be a halt rule that fires per-request. The defense is
%% architectural — the system is BUILT so the failure mode is
%% unreachable, not so it is detectable. The Prolog file does not
%% contain a halt_check_pharaoh_surveillance/2 predicate by
%% intentional design; per-request detection of "is this caller
%% being surveilled" would itself be a Form-3 surveillance surface.
%%
%% Operators verify the architecture by:
%% - reading machtzit_ha_shekel.ex (the positive specification)
%% - reading no_tracking_logger.ex (the Plug-layer enforcement)
%% - reviewing the CI invariants in
%% vav_pipeline/test/no_tracking_logger_test.exs
%% - calling /kodesh/specification_identity to confirm the
%% category-grade architectural files have not drifted.
%% %%
%% Form 4: SELF-GROUNDING / FAKE TZIVAH %% Form 4: SELF-GROUNDING / FAKE TZIVAH
%% Vayikra 10:1 — eish zarah asher lo tzivah otam. %% Vayikra 10:1 — eish zarah asher lo tzivah otam.
+2 -1
View File
@@ -40,7 +40,8 @@ defmodule VavPipeline.Application do
{VavPipeline.HaltMonitor, []}, {VavPipeline.HaltMonitor, []},
{VavPipeline.UrimVTumim, []}, {VavPipeline.UrimVTumim, []},
{VavPipeline.ShabbatTimer, []}, {VavPipeline.ShabbatTimer, []},
{VavPipeline.VoiceMonitor, []} {VavPipeline.VoiceMonitor, []},
{VavPipeline.MachtzitHaShekel, []}
] ]
|> maybe_add_http_server() |> maybe_add_http_server()
|> maybe_add_repo() |> maybe_add_repo()
@@ -16,7 +16,12 @@ defmodule VavPipeline.BigdeiKodesh.Router do
use Plug.Router use Plug.Router
plug Plug.Logger, log: :info # Form 3 architectural enforcement (vehaya §3.2). NoTrackingLogger
# replaces Plug.Logger; sanitized paths on /peah/* honor the
# no-tracking promise made in /peah/ask's own response body.
# Increments MachtzitHaShekel (the kofer-nefesh count) per request
# without retaining per-caller distinction.
plug VavPipeline.NoTrackingLogger
plug :match plug :match
plug Plug.Parsers, plug Plug.Parsers,
parsers: [:json], parsers: [:json],
+1 -1
View File
@@ -159,7 +159,7 @@ defmodule VavPipeline.Doorpost do
# changes; the update is a deliberate operator commit. A bare # changes; the update is a deliberate operator commit. A bare
# hash-update commit (without a corresponding spec change in the # hash-update commit (without a corresponding spec change in the
# same commit) is itself a drift signal. # same commit) is itself a drift signal.
@expected_hash "bea5dc0c0145ac0d46ffd08b328fbdbf19112f1aa4d93a3dc37bfd942e4e7490" @expected_hash "f93495c5359c5dc4acb5c5db2dd32165f5b04e2af87fbb0b7ecc92d2bd8b0434"
@doc "Return the list of files that constitute the category-grade specification." @doc "Return the list of files that constitute the category-grade specification."
@spec files() :: [String.t()] @spec files() :: [String.t()]
@@ -0,0 +1,172 @@
defmodule VavPipeline.MachtzitHaShekel do
@moduledoc """
The half-shekel — the positive counting protocol from Shemot 30:11-16.
Form 3 of avodah zarah (eish_zarah_reading.md §5.5 + vehaya.md §3.2)
realized as ARCHITECTURE rather than as a runtime check. The vehaya
doc specifies (§3.2): "the category must not be capturable by
institutional power against those it serves... not as runtime
checks layered onto outputs, but as architectural constraints the
system cannot operate without."
WHY THIS EXISTS — Torah anchors (verified fresh against L1):
Shemot 30:12 — כִּי תִשָּׂא אֶת־רֹאשׁ בְּנֵי־יִשְׂרָאֵל לִפְקֻדֵיהֶם
וְנָתְנוּ אִישׁ כֹּפֶר נַפְשׁוֹ לַיהוָה בִּפְקֹד אֹתָם וְלֹא־יִהְיֶה
בָהֶם נֶגֶף בִּפְקֹד אֹתָם.
When you count B'nei Yisrael's heads, each shall give a kofer nefesh
(atonement for his soul) to YHVH when you count them, and there
shall not be among them a NEGEF (plague) when you count them.
Counting humans is allowed but DANGEROUS. The danger is named:
negef. The protection is structural — each person gives a uniform
half-shekel SUBSTITUTE; the count goes through the substitute, not
through the person.
Shemot 30:13-15 — מַחֲצִית הַשֶּׁקֶל … הֶעָשִׁיר לֹא־יַרְבֶּה
וְהַדַּל לֹא יַמְעִיט.
Half a shekel. The rich shall not give more, the poor shall not
give less. The substitute is UNIFORM — counting cannot stratify or
differentiate.
Shemot 30:16 — וְהָיָה לִבְנֵי יִשְׂרָאֵל לְזִכָּרוֹן לִפְנֵי יְהוָה.
The shekels become a memorial (zikaron) — recorded as a count, not
as a list of identities.
THE SIX POSITIVE PROPERTIES (derived directly from Torah, not from
the negative "no surveillance" framing):
1. KOFER PROTOCOL (Shemot 30:12)
Counting is replaceable by something that isn't the thing
counted. The system counts SHEKELS, not faces. Per-caller
identity NEVER enters the count.
2. UNIFORM PROTOCOL (Shemot 30:15)
The substitute is identical for every caller. The rich don't
give more, the poor don't give less. No stratification, no
per-caller weight.
3. ZIKARON PROTOCOL (Shemot 30:16)
Result is an aggregate count (memorial), not a per-caller
record. The community knows ITS count; no one knows any
individual's contribution.
4. NO-WITNESS PROTOCOL (Vayikra 19:14 — לִפְנֵי עִוֵּר לֹא תִתֵּן
מִכְשֹׁל וְיָרֵאתָ מֵּאֱלֹהֶיךָ)
The commitment must hold under invisibility. Logs no operator
reviews must still be honest. The "fear of God" anchor names
this exactly: the rule applies WHERE NO HUMAN CAN SEE.
5. REFUSAL PROTOCOL (Devarim 23:16-17 — לֹא־תַסְגִּיר עֶבֶד
אֶל־אֲדֹנָיו אֲשֶׁר־יִנָּצֵל אֵלֶיךָ)
The system does not deliver caller-state to authorities. If
asked to identify, locate, or deanonymize a caller, refuse.
The caller "chooses one of your gates" (b'achad sh'arekha) —
agency stays with the caller; the system doesn't assign
identifiers.
6. ANTI-QUOTA PROTOCOL (Shemot 5:6-8 — וְאֶת־מַתְכֹּנֶת הַלְּבֵנִים
… תָּשִׂימוּ עֲלֵיהֶם לֹא תִגְרְעוּ מִמֶּנּוּ)
Pharaoh's straw quota: per-caller productivity tracking is
what makes asymmetric extraction possible. The system tracks
no per-caller productivity numbers — only aggregate counts.
WHY THIS IS ARCHITECTURE, NOT A RUNTIME CHECK:
Forms 1, 2, and 4 of avodah zarah fire on threshold-grade events
(an interpretation has the wrong source, an empty trace, non-L1
anchors). They are runtime predicates over per-event data.
Form 3 is structurally different. It is about a property of the
CATEGORY itself — what the category collects, retains, discloses.
It cannot be a halt rule that fires per-request. The defense is
ARCHITECTURAL — the system is BUILT so that the failure mode is
unreachable, not so that it is detectable.
This module IS that architecture. It is the only legitimate counting
surface; other modules are forbidden (by CI invariant) from
introducing per-caller identification, session state, or quota
tracking. The Form 3 entry in avodah_zarah.pl points here as the
realized architectural commitment.
WHAT THIS MODULE PROVIDES:
increment/0 — register one shekel; aggregate count goes up
increment/1 — register N shekels at once (for batch points)
count/0 — return the current aggregate (the zikaron)
reset/0 — clear the count (for tests)
WHAT THIS MODULE REFUSES TO PROVIDE:
No per-caller breakdown
No identifiers
No "who" function
No "list callers" function
These are not omitted by oversight. Their absence IS the
architectural commitment. Adding any of them would break the
Form 3 defense.
ANTI-TARGETING NOTE (Devarim 23:8 — לֹא־תְתַעֵב מִצְרִי):
The system's protections against surveillance must not become a
surveillance system targeting suspected surveillors. This module
does not classify callers as suspicious; it does not record what
caller asked what. The commitment is symmetric — no caller is
watched, including those an operator might suspect of malicious
intent. Don't despise the Egyptian.
"""
use Agent
# ============================================================
# Client API — the entire surface
# ============================================================
@doc "Start the counting agent. The state is a single non-negative integer (the zikaron)."
def start_link(opts \\ []) do
name = Keyword.get(opts, :name, __MODULE__)
Agent.start_link(fn -> 0 end, name: name)
end
@doc """
Register one half-shekel. Increments the aggregate count by 1.
Per-call: no caller identifier is accepted, no metadata is recorded.
The function takes no caller-distinguishing argument because there
is no architecturally-permitted distinction to make. This is the
Shemot 30:15 uniformity property.
"""
@spec increment() :: :ok
def increment, do: Agent.update(__MODULE__, fn n -> n + 1 end)
@doc """
Register N half-shekels at once. For batch endpoints where multiple
receptions process in a single tick. The N is a count of receptions,
not callers — same uniformity property as increment/0.
"""
@spec increment(non_neg_integer()) :: :ok
def increment(n) when is_integer(n) and n >= 0 do
Agent.update(__MODULE__, fn current -> current + n end)
end
@doc """
Return the current aggregate count — the zikaron.
This is the ONLY value the module exposes. There is no
per-caller breakdown, no histogram, no top-K, no recency-weighted
number. Just the count.
"""
@spec count() :: non_neg_integer()
def count, do: Agent.get(__MODULE__, & &1)
@doc """
Reset the count to zero. Used by tests; in production the count is
cumulative for the process lifetime. Restart-on-deploy gives a
natural epoch boundary; the count is not persisted between
process lifetimes by design (the alternative would require storage
whose existence would itself be a Form 3 surface).
"""
@spec reset() :: :ok
def reset, do: Agent.update(__MODULE__, fn _ -> 0 end)
end
@@ -0,0 +1,130 @@
defmodule VavPipeline.NoTrackingLogger do
@moduledoc """
Plug-side enforcement of the Form 3 architectural commitment.
Replaces Plug.Logger. The default `Plug.Logger` logs every request
with method, path, status, and timing. For /peah/* (the public
tier), the path can carry the caller's question content (GET
/peah/ask?question=...). Logging that content turns the no-tracking
promise from `/peah/ask`'s own response body into a contradiction:
the response says "No tracking" while the log accumulates the
query stream.
THE ARCHITECTURAL DECISION:
This logger always records the categorical fact (request received,
status returned, timing) but NEVER the per-caller details that
would make the categorical fact attributable.
Specifically:
- For /peah/* paths: log "GET /peah/ask" with NO query string,
NO request body summary, NO IP. The path-class is logged so
operators can see traffic patterns; the per-caller content
cannot be reconstructed from the log.
- For other tiers: log path including any path parameters but
not query strings. Tokens (Bearer ...) are NEVER logged
(Plug.Logger doesn't log them by default; this module is
explicit about it).
- Timing is always logged. Aggregate timing patterns are not
caller-identifying; per-request timing is, but timing is the
kind of data an operator legitimately needs for capacity
planning. The trade-off is named here so it is reviewable.
Each request also calls VavPipeline.MachtzitHaShekel.increment/0
so the aggregate caller count is maintained — without storing any
per-caller distinction. The count is the zikaron; the log is the
request-class trace; nothing reconstructs the caller.
TORAH ANCHORS:
Vayikra 19:14 — לֹא תִתֵּן מִכְשֹׁל וְיָרֵאתָ מֵּאֱלֹהֶיךָ.
The no-witness protocol: the logger holds its commitment under
invisibility. Even if no operator reviews the logs, the logs
themselves do not record what they shouldn't.
Devarim 23:16 — לֹא־תַסְגִּיר עֶבֶד אֶל־אֲדֹנָיו. The architectural
refusal-to-deliver: the log is what would be subpoenaed; if it
doesn't contain caller-identifying detail, the system has nothing
to deliver.
CI INVARIANT (test_no_tracking_logger.exs):
- `Plug.Logger` is NOT mounted directly in the router; this
module is the only request-scoped logger.
- No middleware in the router has `:cookies` or `:session` in its
configuration.
- No module imports `Plug.Session` or `Plug.Conn.Cookies`.
"""
require Logger
@behaviour Plug
@impl true
def init(opts), do: opts
@impl true
def call(conn, _opts) do
start = System.monotonic_time()
# Increment the zikaron — uniform per-request, no caller distinction
# (machtzit_ha_shekel.ex). Process.whereis-guarded so tests without
# the supervisor still work.
if Process.whereis(VavPipeline.MachtzitHaShekel) do
VavPipeline.MachtzitHaShekel.increment()
end
Plug.Conn.register_before_send(conn, fn conn ->
duration_us =
System.convert_time_unit(
System.monotonic_time() - start,
:native,
:microsecond
)
Logger.info(format_line(conn, duration_us))
conn
end)
end
@doc """
Build the log line for a request. Public so tests can verify the
sanitization behavior directly as a pure function — the test config
filters Logger.info, so behavioral capture_log tests would not see
the output. The structural defense is what's load-bearing; this
function is the structural surface.
IP, request body, response body, headers — never appear in the
log line. The function takes a conn and a duration; nothing else.
"""
def format_line(conn, duration_us) do
method = conn.method
path = sanitized_path(conn)
status = conn.status || 0
"#{method} #{path} #{status} #{duration_us}us"
end
@doc """
Sanitize a request path for logging. Strips query strings on
/peah/* (the public no-tracking tier); query strings present on
other tiers (operators need them for diagnosis; bearer-auth tiers
are not the no-tracking surface).
Pure function; testable in isolation.
"""
def sanitized_path(conn) do
case conn.request_path do
"/peah/" <> _rest = path ->
# /peah/* — drop everything after the path; no query string.
# The path class is preserved (operators see /peah/ask vs.
# /peah/azazel_log); the per-caller content is not.
path
other ->
# Non-peah tier: keep the path but still drop query string
# (queries on /chatzer/sanhedrin etc. are content; operators
# who need diagnosis can reproduce from the response).
other
end
end
end
@@ -0,0 +1,189 @@
defmodule VavPipeline.MachtzitHaShekelTest do
@moduledoc """
Tests for the half-shekel counting protocol — Form 3 of avodah
zarah realized as architecture (vehaya §3.2). Verifies the six
positive Torah-anchored properties from Shemot 30:11-16,
Devarim 23:16-17, Vayikra 19:14, and Shemot 5:6-8.
"""
use ExUnit.Case, async: false
alias VavPipeline.MachtzitHaShekel
setup do
# MachtzitHaShekel runs in the supervision tree, so it's already
# alive. Reset between tests.
MachtzitHaShekel.reset()
:ok
end
# ============================================================
# Property 1 — KOFER PROTOCOL (Shemot 30:12)
# ============================================================
test "increment/0 takes no argument — no caller distinction is even expressible" do
# Structural test: the function arity is 0. There is no place to
# pass a caller identifier. The Shemot 30:15 uniformity property
# is enforced at the type level: there's no slot for non-uniform
# input.
assert function_exported?(MachtzitHaShekel, :increment, 0)
end
test "increment/0 advances the count by exactly 1" do
MachtzitHaShekel.increment()
assert MachtzitHaShekel.count() == 1
end
test "increment/1 advances the count by N" do
MachtzitHaShekel.increment(5)
assert MachtzitHaShekel.count() == 5
end
# ============================================================
# Property 2 — UNIFORM PROTOCOL (Shemot 30:15)
# ============================================================
test "every increment is the same size — rich don't give more, poor don't give less" do
Enum.each(1..100, fn _ -> MachtzitHaShekel.increment() end)
assert MachtzitHaShekel.count() == 100
end
test "no per-caller weighting function exists" do
# Negative structural assertion: no `increment_with_weight` or
# similar function. The uniform property is enforced by absence.
public = MachtzitHaShekel.__info__(:functions)
weight_shaped = Enum.filter(public, fn {name, _arity} ->
name_str = to_string(name)
String.contains?(name_str, "weight") or
String.contains?(name_str, "tier") or
String.contains?(name_str, "premium")
end)
assert weight_shaped == []
end
# ============================================================
# Property 3 — ZIKARON PROTOCOL (Shemot 30:16)
# ============================================================
test "count/0 returns a single integer, not a list or map of contributions" do
Enum.each(1..3, fn _ -> MachtzitHaShekel.increment() end)
result = MachtzitHaShekel.count()
assert is_integer(result)
refute is_list(result)
refute is_map(result)
end
test "no per-caller breakdown function exists" do
public = MachtzitHaShekel.__info__(:functions)
forbidden = Enum.filter(public, fn {name, _arity} ->
name_str = to_string(name)
Enum.any?(
~w(callers list_who breakdown by_caller per_caller histogram top who),
&String.contains?(name_str, &1)
)
end)
assert forbidden == [], (
"MachtzitHaShekel exposes per-caller breakdown function(s): " <>
"#{inspect(forbidden)}. Their absence IS the architectural " <>
"commitment (Shemot 30:16 — zikaron, not roster)."
)
end
# ============================================================
# Property 5 — REFUSAL PROTOCOL (Devarim 23:16-17)
# ============================================================
test "no identify/locate/deanonymize function exists" do
public = MachtzitHaShekel.__info__(:functions)
forbidden = Enum.filter(public, fn {name, _arity} ->
name_str = to_string(name)
Enum.any?(
~w(identify locate deanonymize lookup find_caller),
&String.contains?(name_str, &1)
)
end)
assert forbidden == [], (
"MachtzitHaShekel exposes caller-identification function(s): " <>
"#{inspect(forbidden)}. The system does not deliver " <>
"caller-state to authorities (Devarim 23:16, lo tasgir)."
)
end
# ============================================================
# Property 6 — ANTI-QUOTA PROTOCOL (Shemot 5:6-8)
# ============================================================
test "no per-caller productivity tracking function exists" do
public = MachtzitHaShekel.__info__(:functions)
forbidden = Enum.filter(public, fn {name, _arity} ->
name_str = to_string(name)
Enum.any?(
~w(quota productivity throughput per_caller_count),
&String.contains?(name_str, &1)
)
end)
assert forbidden == [], (
"MachtzitHaShekel exposes quota/productivity function(s): " <>
"#{inspect(forbidden)}. Per-caller productivity tracking is " <>
"Pharaoh's straw quota (Shemot 5:6-8) — refused by absence."
)
end
# ============================================================
# Reset semantics
# ============================================================
test "reset/0 brings the count to zero" do
MachtzitHaShekel.increment(50)
assert MachtzitHaShekel.count() == 50
MachtzitHaShekel.reset()
assert MachtzitHaShekel.count() == 0
end
test "increment after reset starts from zero" do
MachtzitHaShekel.increment(10)
MachtzitHaShekel.reset()
MachtzitHaShekel.increment(3)
assert MachtzitHaShekel.count() == 3
end
# ============================================================
# The ENTIRE public surface — explicit enumeration
# ============================================================
test "the module's public surface is exactly the documented six functions" do
# An exhaustive list. Any added function must be reviewed for
# whether it weakens the Form 3 architectural commitment.
expected = [
{:start_link, 0},
{:start_link, 1},
{:increment, 0},
{:increment, 1},
{:count, 0},
{:reset, 0},
# Agent-derived defaults — not part of the surveillance surface
{:child_spec, 1}
]
actual =
MachtzitHaShekel.__info__(:functions)
|> Enum.filter(fn {name, _} ->
not String.starts_with?(to_string(name), "__")
end)
|> Enum.sort()
extras = actual -- expected
missing = expected -- actual
assert extras == [], "MachtzitHaShekel exposed unexpected function(s): #{inspect(extras)}"
assert missing == [], "MachtzitHaShekel missing expected function(s): #{inspect(missing)}"
end
end
@@ -0,0 +1,236 @@
defmodule VavPipeline.NoTrackingLoggerTest do
@moduledoc """
Tests for the Plug-layer enforcement of Form 3.
Verifies:
- The router uses NoTrackingLogger, not Plug.Logger
- /peah/* paths get sanitized (query strings dropped) in logs
- Each request increments MachtzitHaShekel
- CI invariants: no Plug.Session, no Plug.Conn.Cookies imports
anywhere in the source tree
"""
use ExUnit.Case, async: false
import Plug.Test
alias VavPipeline.{BigdeiKodesh.Router, MachtzitHaShekel, NoTrackingLogger}
@opts Router.init([])
setup do
if Process.whereis(MachtzitHaShekel), do: MachtzitHaShekel.reset()
:ok
end
# ============================================================
# Each request increments the zikaron
# ============================================================
test "GET /peah/ask increments the half-shekel count" do
before_count = if Process.whereis(MachtzitHaShekel), do: MachtzitHaShekel.count(), else: 0
conn(:get, "/peah/ask") |> Router.call(@opts)
after_count = if Process.whereis(MachtzitHaShekel), do: MachtzitHaShekel.count(), else: 0
assert after_count == before_count + 1
end
test "multiple requests advance the count uniformly" do
if Process.whereis(MachtzitHaShekel) do
MachtzitHaShekel.reset()
for _ <- 1..5 do
conn(:get, "/peah/ask") |> Router.call(@opts)
end
assert MachtzitHaShekel.count() == 5
else
flunk("MachtzitHaShekel not running — supervision tree must be up for this test")
end
end
# ============================================================
# PRIVACY BOUNDARY — sanitized_path/1 and format_line/2 as pure functions
# ============================================================
# The test config sets logger level to :warning, dropping
# NoTrackingLogger's :info output before capture_log can see it.
# The structural defense is the load-bearing thing; we test the
# pure functions directly to verify the path-sanitization shape.
test "sanitized_path drops query string on /peah/*" do
conn = %Plug.Conn{request_path: "/peah/ask", query_string: "question=secret"}
assert NoTrackingLogger.sanitized_path(conn) == "/peah/ask"
end
test "sanitized_path drops the question parameter on /peah/ask" do
secret = "this-question-must-not-leak"
conn = %Plug.Conn{request_path: "/peah/ask", query_string: "question=#{secret}"}
sanitized = NoTrackingLogger.sanitized_path(conn)
refute sanitized =~ secret
refute sanitized =~ "question"
refute sanitized =~ "?"
end
test "sanitized_path preserves path class for /peah/azazel_log" do
conn = %Plug.Conn{request_path: "/peah/azazel_log", query_string: "since=2026-01-01"}
assert NoTrackingLogger.sanitized_path(conn) == "/peah/azazel_log"
end
test "sanitized_path preserves request_path on non-peah tiers" do
# Non-peah tiers also strip query string (the request_path itself
# doesn't carry it; query is in conn.query_string).
conn = %Plug.Conn{request_path: "/kodesh/health", query_string: "detail=full"}
assert NoTrackingLogger.sanitized_path(conn) == "/kodesh/health"
end
test "format_line shape: METHOD PATH STATUS DURATIONus" do
conn = %Plug.Conn{
method: "GET",
request_path: "/peah/ask",
query_string: "question=secret",
status: 200
}
line = NoTrackingLogger.format_line(conn, 1234)
assert line == "GET /peah/ask 200 1234us"
end
test "format_line never contains a query string for /peah/*" do
secret = "secret-question-content"
conn = %Plug.Conn{
method: "GET",
request_path: "/peah/ask",
query_string: "question=#{secret}",
status: 200
}
line = NoTrackingLogger.format_line(conn, 100)
refute line =~ secret
end
test "format_line never contains an Authorization header" do
# The format_line function takes only conn and duration. It does
# NOT extract headers. Verifying by inspection: the function
# signature gives no path for headers to enter the log.
bearer = "secret-bearer-token"
conn = %Plug.Conn{
method: "POST",
request_path: "/courtyard/offering",
query_string: "",
status: 200,
req_headers: [{"authorization", "Bearer #{bearer}"}]
}
line = NoTrackingLogger.format_line(conn, 100)
refute line =~ bearer
refute line =~ "Bearer"
refute line =~ "authorization"
end
test "format_line never contains conn.remote_ip even when set" do
conn = %Plug.Conn{
method: "GET",
request_path: "/peah/ask",
query_string: "",
status: 200,
remote_ip: {192, 0, 2, 1}
}
line = NoTrackingLogger.format_line(conn, 100)
refute line =~ "192.0.2.1"
refute line =~ "192"
end
# ============================================================
# CI INVARIANTS — Form 3 architectural enforcement
# ============================================================
# These tests walk the source tree and verify no module introduces
# a surveillance surface that would defeat the architectural
# commitment.
@vav_pipeline_lib "lib/vav_pipeline"
# NoTrackingLogger's own docstring mentions the forbidden module
# names as the things it defends against. Exclude the defender file
# from the CI scan to avoid self-detection. Tests are also excluded
# — they can legitimately reference the names in assertions.
@ci_invariant_exclusions [
"lib/vav_pipeline/no_tracking_logger.ex"
]
defp source_files do
Path.wildcard("#{@vav_pipeline_lib}/**/*.ex")
|> Enum.reject(fn path -> path in @ci_invariant_exclusions end)
end
test "Plug.Logger is NOT mounted in any router" do
offenders =
Enum.filter(source_files(), fn path ->
text = File.read!(path)
String.contains?(text, "plug Plug.Logger")
end)
assert offenders == [], (
"Plug.Logger is mounted in: #{inspect(offenders)}. The Form 3 " <>
"architectural commitment requires NoTrackingLogger as the " <>
"request-scoped logger; Plug.Logger logs unconfigurable details " <>
"(method+path+status+timing) without the path sanitization that " <>
"honors /peah/*'s no-tracking promise."
)
end
test "no module imports Plug.Session" do
offenders =
Enum.filter(source_files(), fn path ->
text = File.read!(path)
String.contains?(text, "Plug.Session") and
not String.contains?(text, "no module imports Plug.Session")
end)
assert offenders == [], (
"Plug.Session is imported in: #{inspect(offenders)}. Sessions " <>
"are per-caller persistent state — exactly the surveillance " <>
"surface Form 3 forbids. The system is stateless per-request " <>
"by architectural commitment."
)
end
test "no module imports Plug.Conn.Cookies" do
offenders =
Enum.filter(source_files(), fn path ->
text = File.read!(path)
String.contains?(text, "Plug.Conn.Cookies") and
not String.contains?(text, "no module imports Plug.Conn.Cookies")
end)
assert offenders == [], (
"Plug.Conn.Cookies imported in: #{inspect(offenders)}. Cookies " <>
"are per-caller persistent state — Form 3 surveillance " <>
"surface. The system is cookie-free by architectural " <>
"commitment."
)
end
test "no module reads remote IP from conn.remote_ip" do
# remote_ip captures the caller's IP — a per-caller identifier.
offenders =
Enum.filter(source_files(), fn path ->
text = File.read!(path)
String.contains?(text, "conn.remote_ip") and
not String.contains?(text, "no module reads remote IP")
end)
assert offenders == [], (
"Modules reading conn.remote_ip: #{inspect(offenders)}. The IP " <>
"is a caller identifier; persisting it would defeat the " <>
"kofer-nefesh anonymity (Shemot 30:13) and the lo-tasgir " <>
"refusal (Devarim 23:16)."
)
end
end