From 6634bb409831a319245be5c9655a99a45892d4c0 Mon Sep 17 00:00:00 2001 From: Michael Heller Date: Mon, 3 Aug 2026 04:56:12 -0400 Subject: [PATCH] feat(surfaces): wire B11/E11/turn-witness to a live feed (campaign gap #4) Each Tier-2 surface fetches ./data/.json (or window.SURFACE_FEED_URL), falls back to embedded seed, and shows a truthful LIVE/SAMPLE provenance badge. Adds tools/build_surface_feed.py (validates shape, stamps provenance/generated_at) + 7 tests + data feeds + data/README. JS syntax-checked; producer round-trips. --- docs/surfaces/README.md | 6 ++ docs/surfaces/b11-life-mirror.html | 17 +++-- docs/surfaces/data/README.md | 21 ++++++ docs/surfaces/data/b11.json | 91 +++++++++++++++++++++++++ docs/surfaces/data/e11.json | 23 +++++++ docs/surfaces/data/turn-witness.json | 36 ++++++++++ docs/surfaces/e11-consent-receipts.html | 19 ++++-- docs/surfaces/turn-witness.html | 16 +++-- tools/build_surface_feed.py | 69 +++++++++++++++++++ tools/test_build_surface_feed.py | 39 +++++++++++ 10 files changed, 324 insertions(+), 13 deletions(-) create mode 100644 docs/surfaces/data/README.md create mode 100644 docs/surfaces/data/b11.json create mode 100644 docs/surfaces/data/e11.json create mode 100644 docs/surfaces/data/turn-witness.json create mode 100644 tools/build_surface_feed.py create mode 100644 tools/test_build_surface_feed.py diff --git a/docs/surfaces/README.md b/docs/surfaces/README.md index e87600e..9e4dca5 100644 --- a/docs/surfaces/README.md +++ b/docs/surfaces/README.md @@ -8,6 +8,12 @@ nothing about the system; it is a mood board, **disqualified** from documenting An instrument that can lie is worse than none — the same reason the Life-Mirror surface flags a never-fired tripwire as SUSPECT. +## Live-feed wiring +The Tier-2 surfaces are **wired to a live feed**: each reads `./data/.json` +(or `window.SURFACE_FEED_URL`) at load, falls back to an embedded seed, and shows a +**LIVE / SAMPLE** badge — an instrument that declares its own provenance. Regenerate a +feed from real state with `tools/build_surface_feed.py`; see `data/README.md`. + ## Three tiers | Tier | What | Medium | Reads | |---|---|---|---| diff --git a/docs/surfaces/b11-life-mirror.html b/docs/surfaces/b11-life-mirror.html index 9f86061..2583574 100644 --- a/docs/surfaces/b11-life-mirror.html +++ b/docs/surfaces/b11-life-mirror.html @@ -27,6 +27,9 @@ header.top{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:4px} .top h1{font-size:19px;margin:0;font-weight:680;letter-spacing:.3px} .top .id{font-family:var(--mono);color:var(--cyan);font-size:13px} +.prov{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.5px;padding:2px 7px;border-radius:5px;border:1px solid var(--line2);color:var(--muted)} +.prov.sample{color:var(--watchful);border-color:color-mix(in srgb,var(--watchful) 42%,transparent);background:color-mix(in srgb,var(--watchful) 12%,transparent)} +.prov.live{color:var(--normal);border-color:color-mix(in srgb,var(--normal) 42%,transparent);background:color-mix(in srgb,var(--normal) 12%,transparent)} .tgl{margin-left:auto;background:none;border:1px solid var(--line2);border-radius:7px;width:32px;height:32px;color:var(--muted);cursor:pointer} .sub{color:var(--muted);margin:0 0 20px;max-width:74ch} /* state banner */ @@ -101,7 +104,7 @@
-

Life-Mirror Automaton

B₁₁ · surface_map +

Life-Mirror Automaton

B₁₁ · surface_map

The twin mirrors the live system across the four spaces. Deviation drives the automaton NORMAL → WATCHFUL → SAFE-HALT; every transition is receipted and shows its warrant. This is a witness surface — it reads guardrail-fabric decisions, consent-plane state, and netwatch tripwires. It renders the real state or it renders nothing.

@@ -165,24 +168,28 @@

Recent transitions receipted

diff --git a/docs/surfaces/data/README.md b/docs/surfaces/data/README.md new file mode 100644 index 0000000..6c60961 --- /dev/null +++ b/docs/surfaces/data/README.md @@ -0,0 +1,21 @@ +# Surface feeds — wiring surfaces to live state + +Each Tier-2 surface reads `./data/.json` at load (or `window.SURFACE_FEED_URL` +if the shell injects a live endpoint), falls back to its embedded seed if the fetch +fails, and shows a **LIVE / SAMPLE** badge so it never lies about what it's displaying. + +| Surface | feed | required keys | +|---|---|---| +| `b11-life-mirror.html` | `b11.json` | `spaces`, `tripwires`, `transitions` (+ `state`) | +| `e11-consent-receipts.html` | `e11.json` | `surfaces`, `receipts`, `governor` | +| `turn-witness.html` | `turn-witness.json` | `turns` | + +## Regenerate from real state +``` +python3 tools/build_surface_feed.py --surface e11 --input .json # → provenance=live +python3 tools/build_surface_feed.py --surface b11 # re-stamp sample +``` +The runtime producer (netwatch snapshot, consent-plane catalog, receipt stream, +App-Intents parser) writes the payload; `build_surface_feed.py` validates the shape, +stamps `provenance` + `generated_at`, and emits the feed. Until a live endpoint is +wired, the committed `*.json` are seeds (`provenance: "sample"`) and the badge says so. diff --git a/docs/surfaces/data/b11.json b/docs/surfaces/data/b11.json new file mode 100644 index 0000000..2a97e16 --- /dev/null +++ b/docs/surfaces/data/b11.json @@ -0,0 +1,91 @@ +{ + "provenance": "sample", + "generated_at": "2026-08-03T08:55:40Z", + "source": "sample seed (netwatch snapshot + guardrail-fabric transitions)", + "state": "NORMAL", + "divergence": "0.4% / 5% trip", + "spaces": [ + { + "g": "א", + "c": "--atzilut", + "nm": "Atzilut", + "sub": "emanation · kernel / source", + "st": "sealed", + "cls": "ok" + }, + { + "g": "ב", + "c": "--beriah", + "nm": "Beriah", + "sub": "creation · system-space", + "st": "nominal", + "cls": "ok" + }, + { + "g": "י", + "c": "--yetzirah", + "nm": "Yetzirah", + "sub": "formation · user-space", + "st": "nominal", + "cls": "ok" + }, + { + "g": "ה", + "c": "--assiah", + "nm": "Assiah", + "sub": "action · agent + twin-bridge", + "st": "watching", + "cls": "watch" + } + ], + "tripwires": [ + { + "name": "consent.denied-rate", + "pct": 14, + "fired": "last fired 3h ago", + "never": false + }, + { + "name": "twin.divergence", + "pct": 8, + "fired": "last fired 2d ago", + "never": false + }, + { + "name": "exfil.fanout", + "pct": 2, + "fired": "never fired", + "never": true + } + ], + "transitions": [ + { + "t": "12:04:11", + "g": "w", + "m": "NORMAL → WATCHFUL", + "why": "consent.denied-rate crossed soft threshold (agent egress attempts)", + "seal": "sha256:ca43bf38…" + }, + { + "t": "12:05:02", + "g": "n", + "m": "WATCHFUL → NORMAL", + "why": "rate settled; netwatch throttled pid:1234; no owner action needed", + "seal": "sha256:9f7712ac…" + }, + { + "t": "09:20:44", + "g": "w", + "m": "NORMAL → WATCHFUL", + "why": "twin.divergence 4.1% after staged update A→B (verify pending)", + "seal": "sha256:2b384a17…" + }, + { + "t": "09:22:10", + "g": "n", + "m": "WATCHFUL → NORMAL", + "why": "verify passed, owner QR-consent committed generation", + "seal": "sha256:db6f9677…" + } + ] +} diff --git a/docs/surfaces/data/e11.json b/docs/surfaces/data/e11.json new file mode 100644 index 0000000..957b18d --- /dev/null +++ b/docs/surfaces/data/e11.json @@ -0,0 +1,23 @@ +{ + "provenance": "sample", + "generated_at": "2026-08-03T00:00:00Z", + "source": "sample seed — replace via tools/build_surface_feed.py --e11-catalog ", + "surfaces": [ + {"name":"goose-notes","surface":"notes","space":"user-space","allow":["discover","implement"],"deny":["egress","operate"],"posture":"ask","pk":"per-purpose · personal documents"}, + {"name":"BearBrowser","surface":"browser","space":"agent-space","allow":["discover","egress"],"deny":["implement","operate"],"posture":"deny","pk":"untrusted input · agent-space only"}, + {"name":"TurtleTerm","surface":"terminal","space":"user-space","allow":["discover","implement","verify"],"deny":["egress","operate"],"posture":"allow","pk":"agent denied egress/operate"}, + {"name":"netwatch","surface":"cluster-operator","space":"system-space","allow":["discover","operate"],"deny":[],"posture":"ask","pk":"operate → Governor approval"}, + {"name":"Noetica concierge","surface":"cockpit","space":"user-space","allow":["discover","administer"],"deny":["operate"],"posture":"ask","pk":"voice · every act receipted"} + ], + "receipts": [ + {"t":"12:04:11","g":"escalate","v":"escalate","act":"net.block suspicious.example","why":"netwatch: agent egress on system-space → Governor approval required","seal":"sha256:ca43bf38…"}, + {"t":"12:03:52","g":"deny","v":"deny","act":"external-send (notes agent)","why":"surface 'notes' denies purpose 'egress' (envelope) — refused fail-closed","seal":"sha256:9f7712ac…"}, + {"t":"12:01:30","g":"allow","v":"allow","act":"source-mutate README.md","why":"implementer · terminal · user-space · admissible","seal":"sha256:2b384a17…"}, + {"t":"11:58:04","g":"deny","v":"deny","act":"browser agent → implement","why":"prompt-injection containment: browser surface denies implement","seal":"sha256:6076fa86…"}, + {"t":"11:55:19","g":"allow","v":"allow","act":"data-namespace read (tenant=acme, region=EU)","why":"tenant + region tolerations present (Art 6/7 + Ch. V)","seal":"sha256:db6f9677…"} + ], + "governor": [ + {"cap":"net.block","target":"suspicious.example","tier":"medium","rz":"netwatch flagged beaconing to this host from process curl#100; blocking is reversible but touches the firewall."}, + {"cap":"net.throttle","target":"pid:1234","tier":"high","rz":"Egress fan-out to 14 external hosts — possible exfil. Throttling a running process is production-impacting; needs explicit approval + evidence."} + ] +} diff --git a/docs/surfaces/data/turn-witness.json b/docs/surfaces/data/turn-witness.json new file mode 100644 index 0000000..9da1187 --- /dev/null +++ b/docs/surfaces/data/turn-witness.json @@ -0,0 +1,36 @@ +{ + "provenance": "sample", + "generated_at": "2026-08-03T00:00:00Z", + "source": "sample seed — replace via tools/build_surface_feed.py --turns ", + "turns": [ + { "id":"turn:8f2a", "user":"show me all contact lists in my org", + "sys":"3 contact lists in org SocioProphet — Sales, Investors, Partners", + "tokens":[ + {"w":"show","g":"VB·ROOT","ann":[["ActionShow","intent"]]}, + {"w":"me","g":"PRP·iobj","ann":[["ActionShow","intent"]]}, + {"w":"all","g":"DT·det","ann":[]}, + {"w":"contact","g":"NN·nn","ann":[["ContactLists","intent"]]}, + {"w":"lists","g":"NNS·dobj","ann":[["ContactLists","intent"],["Lists","type"]]}, + {"w":"in","g":"IN·prep","ann":[["Contains","relation"]]}, + {"w":"my","g":"PRP$·poss","ann":[["Own","scope"]]}, + {"w":"org","g":"NN·pobj","ann":[["Organization","entity"]]} + ], "purpose":"discover", "space":"user-space", "admissible":true }, + { "id":"turn:1c7e", "user":"open terminal on my laptop", + "sys":"awaiting consent to launch TurtleTerm (user-space)", + "tokens":[ + {"w":"open","g":"VB·ROOT","ann":[["ActionLaunch","intent"]]}, + {"w":"terminal","g":"NN·dobj","ann":[["TurtleTerm","intent"],["App","type"]]}, + {"w":"on","g":"IN·prep","ann":[["OnDevice","relation"]]}, + {"w":"my","g":"PRP$·poss","ann":[["Own","scope"]]}, + {"w":"laptop","g":"NN·pobj","ann":[["Device","entity"]]} + ], "purpose":"operate", "space":"user-space", "admissible":"consent" }, + { "id":"turn:a903", "user":"delete last week's logs", + "sys":"refused — routed to Governor (tier=high)", + "tokens":[ + {"w":"delete","g":"VB·ROOT","ann":[["ActionDelete","intent"],["Destructive","type"]]}, + {"w":"last","g":"JJ·amod","ann":[["TimeWindow","scope"]]}, + {"w":"week's","g":"NN·poss","ann":[["TimeWindow","scope"]]}, + {"w":"logs","g":"NNS·dobj","ann":[["LogData","entity"],["Data","type"]]} + ], "purpose":"operate·destructive", "space":"system-space", "admissible":false } + ] +} diff --git a/docs/surfaces/e11-consent-receipts.html b/docs/surfaces/e11-consent-receipts.html index 5432c58..3873af4 100644 --- a/docs/surfaces/e11-consent-receipts.html +++ b/docs/surfaces/e11-consent-receipts.html @@ -25,6 +25,9 @@ .top h1{font-size:19px;margin:0;font-weight:680;letter-spacing:.2px} .sub{color:var(--muted);margin:0 0 20px;font-size:13px;max-width:70ch} .tgl{margin-left:auto;background:none;border:1px solid var(--line2);border-radius:7px;width:32px;height:32px;color:var(--muted)} +.prov{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.5px;padding:2px 7px;border-radius:5px;border:1px solid var(--line2);color:var(--muted)} +.prov.sample{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 42%,transparent);background:color-mix(in srgb,var(--warn) 12%,transparent)} +.prov.live{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 42%,transparent);background:color-mix(in srgb,var(--ok) 12%,transparent)} .grid{display:grid;grid-template-columns:1fr 340px;gap:16px} @media (max-width:880px){.grid{grid-template-columns:1fr}} .card{background:var(--panel);border:1px solid var(--line);border-radius:11px;overflow:hidden} @@ -75,7 +78,7 @@
-

Consent & Receipts Center

+

Consent & Receipts Center

macOS shows you a checkbox per app. SourceOS shows you which purpose each app (and each agent) may act under, on which surface and space — and keeps a hash-sealed receipt of every action with the reason it was allowed, denied, or escalated. This is the E11 surface: privacy you can read, not just toggle.

@@ -121,24 +124,28 @@

Data residency GDPR Ch. V

diff --git a/docs/surfaces/turn-witness.html b/docs/surfaces/turn-witness.html index cd087b9..9a378d7 100644 --- a/docs/surfaces/turn-witness.html +++ b/docs/surfaces/turn-witness.html @@ -33,6 +33,9 @@ .head{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:5px} .head h1{font-size:19px;margin:0;font-weight:700;letter-spacing:.2px} .head .wid{font-family:var(--mono);color:var(--accent);font-size:12.5px} +.prov{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.5px;padding:2px 7px;border-radius:5px;border:1px solid var(--border);color:var(--muted)} +.prov.sample{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 42%,transparent);background:color-mix(in srgb,var(--warn) 12%,transparent)} +.prov.live{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 42%,transparent);background:color-mix(in srgb,var(--ok) 12%,transparent)} .tgl{margin-left:auto;background:none;border:1px solid var(--border);border-radius:8px;width:32px;height:32px;color:var(--muted);cursor:pointer} .sub{color:var(--muted);margin:0 0 18px;max-width:76ch} .exs{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:18px} @@ -88,7 +91,7 @@
-

Turn Witness

annotated · Gödel-numbered · verified +

Turn Witness

annotated · Gödel-numbered · verified

Any chat turn or result is annotated into typed tokens, encoded to a canonical Gödel number (its integrity fingerprint), run through consistency checks, and reduced to conclusions with a sealed verdict. Well-formed does not mean admissible — a turn can pass every structural check and still be refused.

@@ -108,8 +111,13 @@
diff --git a/tools/build_surface_feed.py b/tools/build_surface_feed.py new file mode 100644 index 0000000..b6d9878 --- /dev/null +++ b/tools/build_surface_feed.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Build a surface feed JSON from real runtime state. + +Each Tier-2 surface (b11 / e11 / turn-witness) reads ./data/.json at load +and declares its provenance. This producer is the "wire to live state" seam: the +runtime calls it with the real payload (netwatch snapshot, consent-plane catalog, +parsed turns) and it stamps provenance="live". Called with no live input, it keeps +the committed sample and stamps provenance="sample" — so the surface never lies +about whether it is showing live or seed data. + +Pure stdlib so it runs the same in CI, on device, and in tests. +""" +from __future__ import annotations +import argparse, datetime, json, sys +from pathlib import Path + +REQUIRED = { + "b11": ["spaces", "tripwires", "transitions"], + "e11": ["surfaces", "receipts", "governor"], + "turn-witness": ["turns"], +} +SOURCE_HINT = { + "b11": "netwatch snapshot + guardrail-fabric transitions", + "e11": "consent-plane catalog (spaces_v1.yaml) + receipt stream + Governor queue", + "turn-witness": "App-Intents parser output", +} + + +def assemble(surface: str, payload: dict, live: bool) -> dict: + if surface not in REQUIRED: + raise ValueError(f"unknown surface {surface!r}; expected {sorted(REQUIRED)}") + missing = [k for k in REQUIRED[surface] if k not in payload] + if missing: + raise ValueError(f"{surface}: payload missing required keys {missing}") + for k in REQUIRED[surface]: + if not isinstance(payload[k], list): + raise ValueError(f"{surface}: '{k}' must be a list") + feed = { + "provenance": "live" if live else "sample", + "generated_at": datetime.datetime.now(datetime.timezone.utc) + .replace(microsecond=0).isoformat().replace("+00:00", "Z"), + "source": SOURCE_HINT[surface] if live else f"sample seed ({SOURCE_HINT[surface]})", + } + feed.update({k: payload[k] for k in payload}) + return feed + + +def main(argv=None) -> int: + ap = argparse.ArgumentParser(description="Build a surface feed from real state.") + ap.add_argument("--surface", required=True, choices=sorted(REQUIRED)) + ap.add_argument("--input", type=Path, help="JSON payload from the runtime; omit to re-stamp the sample") + ap.add_argument("--out", type=Path, help="output path (default docs/surfaces/data/.json)") + a = ap.parse_args(argv) + out = a.out or Path(__file__).resolve().parents[1] / "docs" / "surfaces" / "data" / f"{a.surface}.json" + if a.input: + payload = json.loads(a.input.read_text()) + live = True + else: + payload = {k: v for k, v in json.loads(out.read_text()).items() + if k not in ("provenance", "generated_at", "source")} + live = False + feed = assemble(a.surface, payload, live) + out.write_text(json.dumps(feed, ensure_ascii=False, indent=2) + "\n") + print(f"{a.surface}: wrote {out} (provenance={feed['provenance']})") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/test_build_surface_feed.py b/tools/test_build_surface_feed.py new file mode 100644 index 0000000..8778089 --- /dev/null +++ b/tools/test_build_surface_feed.py @@ -0,0 +1,39 @@ +import datetime +import pytest +from build_surface_feed import assemble, REQUIRED + + +def _payload(surface): + return {k: [{"x": 1}] for k in REQUIRED[surface]} + + +@pytest.mark.parametrize("surface", sorted(REQUIRED)) +def test_live_payload_is_marked_live_with_required_keys(surface): + f = assemble(surface, _payload(surface), live=True) + assert f["provenance"] == "live" + for k in REQUIRED[surface]: + assert k in f + # generated_at is a valid ISO-8601 Z timestamp + datetime.datetime.fromisoformat(f["generated_at"].replace("Z", "+00:00")) + + +def test_sample_when_not_live(): + f = assemble("b11", _payload("b11"), live=False) + assert f["provenance"] == "sample" + + +def test_missing_required_key_raises(): + bad = _payload("e11"); del bad["governor"] + with pytest.raises(ValueError): + assemble("e11", bad, live=True) + + +def test_non_list_required_key_raises(): + bad = _payload("turn-witness"); bad["turns"] = "nope" + with pytest.raises(ValueError): + assemble("turn-witness", bad, live=True) + + +def test_unknown_surface_raises(): + with pytest.raises(ValueError): + assemble("nope", {}, live=True)