The examination is the product; the record of it is what a caller can carry away. Every bedikah was already engraved in L3 — output hash and length, source digests, coupling trace, claims, violations, verdict, never the text — and no caller could get theirs back. - bedikot.key_id (migration): a SHA-256 digest of the courtyard key that brought the offering, never the key; threaded as :key_id through Bedikah.examine from every site that goes through the gate (courtyard bedikah, pair, the gate's pass and stream). Unique index on bedikah_ref. - VavPipeline.Ledger: entry (the examination's fields, none of the database's, not the owner's), canonical JSON (keys sorted at every level, no whitespace, UTF-8), Ed25519 sign/verify, public key, render. Seed from BIKORET_LEDGER_SIGNING_KEY (base64 of 32 bytes) in shorashim-secrets; absent → unsigned and says so. - GET /courtyard/ledger?limit&before — this key's bedikot, newest first, each rendered and signed; GET /courtyard/bedikah/<ref> — one, 404 for another key's or none alike; GET /peah/ledger_key — the public key. - Tamid §1.1 "The ledger" (Doorpost pin moved → 02ddf313…), KOHEN "Issuing a key" (the seed, once), secrets.yaml template. Tests: ledger_test (digest, entry, canonical, sign/verify, unsigned rendering, signed rendering); ledger_l3_test (own records listed newest first and only own; 404 alike for another's and none; an examination engraved under the key reads back signed and verifies against /peah/ledger_key). Elixir 642/0; L3 13/0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
110 lines
5.1 KiB
Elixir
110 lines
5.1 KiB
Elixir
defmodule VavPipeline.LedgerL3Test do
|
|
@moduledoc "The ledger over L3: a key reads its own bedikot back, signed, and no one else's (2026-10-21)."
|
|
use ExUnit.Case, async: false
|
|
|
|
import Plug.Test
|
|
import Plug.Conn
|
|
|
|
alias VavPipeline.BigdeiKodesh.Router
|
|
alias VavPipeline.L3.Store
|
|
|
|
@opts Router.init([])
|
|
@moduletag :l3
|
|
|
|
setup do
|
|
:ok = Ecto.Adapters.SQL.Sandbox.checkout(VavPipeline.Repo)
|
|
Ecto.Adapters.SQL.Sandbox.mode(VavPipeline.Repo, {:shared, self()})
|
|
on_exit(fn -> Ecto.Adapters.SQL.Sandbox.mode(VavPipeline.Repo, :manual) end)
|
|
write_provision()
|
|
:ok
|
|
end
|
|
|
|
defp write_provision do
|
|
VavPipeline.MannaLedger.reset()
|
|
on_exit(fn -> VavPipeline.MannaLedger.reset() end)
|
|
today = Date.utc_today() |> Date.to_iso8601()
|
|
expires = DateTime.utc_now() |> DateTime.add(6 * 3600) |> DateTime.to_iso8601()
|
|
provision = %{"date" => today, "provision" => %{"compute_units" => 50, "memory_mb" => 512, "api_calls_external" => 10, "storage_delta_mb" => 2, "sanhedrin_panels_max" => 2},
|
|
"type" => "regular", "expires_at" => expires, "written_by" => "ledger_l3_test", "written_at" => DateTime.utc_now() |> DateTime.to_iso8601()}
|
|
path = VavPipeline.Manna.provision_file()
|
|
File.mkdir_p!(Path.dirname(path))
|
|
File.write!(path, Jason.encode!(provision))
|
|
on_exit(fn -> File.rm(path) end)
|
|
end
|
|
|
|
defp attrs(ref, key_id) do
|
|
%{
|
|
bedikah_ref: ref, provenance: "claude", query: "q", output_sha256: String.duplicate("cd", 32), output_length: 5,
|
|
vital_content: "", verdict: "empty", examined_at: DateTime.utc_now() |> DateTime.truncate(:second), key_id: key_id
|
|
}
|
|
end
|
|
|
|
defp get(path, token) do
|
|
conn(:get, path) |> put_req_header("authorization", "Bearer #{token}") |> Router.call(@opts)
|
|
end
|
|
|
|
# An examination the offering protocol finds nothing in: the :empty branch, no engine needed.
|
|
defp empty_examination do
|
|
%{"provenance" => "claude", "query" => "status?", "vital_content" => "", "propositions" => [], "discarded" => ["Rolled out."],
|
|
"references" => [], "hebrew" => [], "roots" => [], "authority_claims" => [], "actions" => [],
|
|
"self_validation" => [], "self_promotion" => [], "excluded_adversarial" => [], "frames" => [], "sources" => [], "claims" => []}
|
|
end
|
|
|
|
test "the record keeps the key's digest, and the ledger lists only that key's records, newest first" do
|
|
mine = VavPipeline.Ledger.key_id("test-token")
|
|
other = VavPipeline.Ledger.key_id("someone-else")
|
|
{:ok, _} = Store.record_bedikah(attrs("bedikah-l3aaaa0000000001", mine))
|
|
{:ok, _} = Store.record_bedikah(attrs("bedikah-l3aaaa0000000002", mine))
|
|
{:ok, _} = Store.record_bedikah(attrs("bedikah-l3bbbb0000000003", other))
|
|
{:ok, _} = Store.record_bedikah(attrs("bedikah-l3cccc0000000004", nil))
|
|
|
|
refs = Store.list_bedikot(mine) |> Enum.map(& &1.bedikah_ref)
|
|
assert refs == ["bedikah-l3aaaa0000000002", "bedikah-l3aaaa0000000001"]
|
|
|
|
conn = get("/courtyard/ledger?limit=10", "test-token")
|
|
assert conn.status == 200
|
|
body = Jason.decode!(conn.resp_body)
|
|
assert body["key_id"] == mine
|
|
assert body["count"] == 2
|
|
assert Enum.map(body["entries"], & &1["entry"]["bedikah_ref"]) == ["bedikah-l3aaaa0000000002", "bedikah-l3aaaa0000000001"]
|
|
# never the text, never the key, never the owner column
|
|
e = hd(body["entries"])["entry"]
|
|
refute Map.has_key?(e, "key_id")
|
|
assert e["output_sha256"] == String.duplicate("cd", 32)
|
|
assert hd(body["entries"])["signed"] == false
|
|
end
|
|
|
|
test "one record by ref: mine answers, another key's and a missing one answer 404 alike" do
|
|
mine = VavPipeline.Ledger.key_id("test-token")
|
|
{:ok, _} = Store.record_bedikah(attrs("bedikah-l3dddd0000000005", mine))
|
|
{:ok, _} = Store.record_bedikah(attrs("bedikah-l3eeee0000000006", VavPipeline.Ledger.key_id("someone-else")))
|
|
|
|
assert get("/courtyard/bedikah/bedikah-l3dddd0000000005", "test-token").status == 200
|
|
assert get("/courtyard/bedikah/bedikah-l3eeee0000000006", "test-token").status == 404
|
|
assert get("/courtyard/bedikah/bedikah-l3ffff0000000007", "test-token").status == 404
|
|
end
|
|
|
|
test "an examination is engraved under the caller's key and reads back signed and verifiable" do
|
|
Application.put_env(:vav_pipeline, :ledger_signing_key, Base.encode64(:crypto.strong_rand_bytes(32)))
|
|
mine = VavPipeline.Ledger.key_id("test-token")
|
|
|
|
try do
|
|
examiner = fn _o, _p, _q, _s -> {:ok, empty_examination()} end
|
|
assert {:ok, r} = VavPipeline.Bedikah.examine("Rolled out.", "claude", "status?", examiner: examiner, key_id: mine)
|
|
assert r.verdict == :empty
|
|
assert Store.get_bedikah(r.bedikah_ref).key_id == mine
|
|
|
|
back = get("/courtyard/bedikah/#{r.bedikah_ref}", "test-token")
|
|
assert back.status == 200
|
|
body = Jason.decode!(back.resp_body)
|
|
assert body["signed"] == true
|
|
pub = Jason.decode!(get("/peah/ledger_key", "x").resp_body)["public_key"]
|
|
canonical = VavPipeline.Ledger.canonical(body["entry"])
|
|
assert :crypto.hash(:sha256, canonical) |> Base.encode16(case: :lower) == body["canonical_sha256"]
|
|
assert VavPipeline.Ledger.verify(canonical, body["signature"], pub)
|
|
after
|
|
Application.delete_env(:vav_pipeline, :ledger_signing_key)
|
|
end
|
|
end
|
|
end
|