diff --git a/.github/workflows/model-plane-receipts.yml b/.github/workflows/model-plane-receipts.yml new file mode 100644 index 0000000..b1cb851 --- /dev/null +++ b/.github/workflows/model-plane-receipts.yml @@ -0,0 +1,48 @@ +name: model-plane-receipts + +# Teeth for the Model Plane (Tranche 7) receipt emitter shim: proves source-os emits +# spec-conformant, hash-chained InferenceReceipts and that tampered / local-only receipts +# are rejected (SEAM-011). CI needs no model — the emitter path is what is under test. + +on: + pull_request: + paths: + - 'modules/model-plane/**' + - '.github/workflows/model-plane-receipts.yml' + push: + branches: + - main + paths: + - 'modules/model-plane/**' + - '.github/workflows/model-plane-receipts.yml' + +jobs: + inferenced-receipt-teeth: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install jsonschema + run: python -m pip install --quiet 'jsonschema>=4.22,<5' + + - name: Assert emitter is vendored byte-verbatim (consume-not-fork) + run: | + set -euo pipefail + expected=6881246b8e41a515fb1b29df645faeb9be17d8195d22a7e0d5ce15f8477d8e6a + actual=$(sha256sum modules/model-plane/tools/inference_receipt_emitter.py | awk '{print $1}') + if [ "$actual" != "$expected" ]; then + echo "ERR: vendored emitter diverged from canonical (expected $expected, got $actual)." + echo " Refresh from prophet-platform apps/receipt-gateway; do not fork (SEAM-011)." + exit 1 + fi + echo "OK: emitter matches canonical sha256:$expected" + + - name: inferenced emit-shim teeth + run: python modules/model-plane/inferenced_shim.py --selftest + + - name: Canonical emitter self-test + run: python modules/model-plane/tools/inference_receipt_emitter.py --selftest diff --git a/docs/model-plane/architecture.md b/docs/model-plane/architecture.md new file mode 100644 index 0000000..a2d63d7 --- /dev/null +++ b/docs/model-plane/architecture.md @@ -0,0 +1,47 @@ +# Model Plane on source-os (Tranche 7) + +The Model Plane is source-os's tiered, on-device inference substrate: placement tiers +**T0–T4** where a tier boundary *is* a data-residency boundary, with provenance receipts, +consent-gated escalation, and governed distillation. The canonical contracts are owned by +`SourceOS-Linux/sourceos-spec` (Tranche 7); source-os is a downstream **emitter**. + +## Status + +This directory currently ships the **emitter shim** for the serving path — the smallest +real slice that proves source-os can emit a spec-conformant, hash-chained +`InferenceReceipt` natively. The full daemon set is not yet built. + +| Slice | Item | State | +|-------|------|-------| +| T7-9 | `docs/model-plane/architecture.md` | this stub | +| T7-10 | `profiles/model-plane/{constrained,standard,workstation,cluster-node}.nix` | not built | +| T7-11 | `modules/modelplaned/` (catalog + residency) | not built | +| T7-12 | `modules/inferenced/` (serving) | **emitter shim only** (`modules/model-plane/inferenced_shim.py`) | +| T7-13 | `modules/embeddingd/` | not built | +| T7-14 | `modules/visiond/` (no network namespace) | not built | +| T7-15 | `modules/distilld/` (governed distillation) | not built | +| T7-20 | `docs/seam-registry.md` — SEAM-014..017 | not built | + +Everything not built here is tracked in the T7 follow-up issue. + +## Receipt spine (SEAM-011) + +Every completion a serving daemon finishes must leave an `InferenceReceipt` in the estate's +single **hash-chained** ledger — a local-only ledger is not permitted (SEAM-011). source-os +**consumes** the canonical emitter (`prophet-platform apps/receipt-gateway`, +vendored byte-verbatim under `modules/model-plane/tools/`); it does not re-implement the +chain. `modules/model-plane/inferenced_shim.py` is the serving-path caller: given the +content-addressed base-model digest and the input/output of a finished completion, it +appends an on-device receipt via the vendored `emit_receipt()`. + +Teeth (`.github/workflows/model-plane-receipts.yml`): a produced receipt validates against +`InferenceReceipt.schema.json` and chains (prevHash continuity); a tampered entry and a +local-only (unchained) entry are both rejected; and the vendored emitter is asserted to +match the canonical sha256 (consume-not-fork guard). + +## Off-device escalation + +An on-device (`on_device_only`) completion carries no lease and no escalation. A completion +served off-device (`sovereign_cluster` / `external_permitted`) crosses a data boundary and +must carry an authorizing capability lease and a non-empty escalation chain (SEAM-015); +the schema enforces this. Off-device serving is out of scope for the emitter shim. diff --git a/modules/model-plane/__pycache__/inferenced_shim.cpython-312.pyc b/modules/model-plane/__pycache__/inferenced_shim.cpython-312.pyc new file mode 100644 index 0000000..c496b81 Binary files /dev/null and b/modules/model-plane/__pycache__/inferenced_shim.cpython-312.pyc differ diff --git a/modules/model-plane/inferenced_shim.py b/modules/model-plane/inferenced_shim.py new file mode 100755 index 0000000..9cf7e28 --- /dev/null +++ b/modules/model-plane/inferenced_shim.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +"""`inferenced` receipt-emission shim (Model Plane, Tranche 7 — emitter half of T7-12). + +The full `inferenced` serving daemon (T7-12) is not built here (it is blocker-laden: +HellGraph ledger service, base-model licensing, credentials). This shim is the SMALLEST +real slice that proves source-os can emit a spec-conformant, hash-chained +`InferenceReceipt` NATIVELY when a completion finishes: it takes the facts a real serving +daemon has at completion time (the content-addressed base-model digest, the task, and the +input/output) and appends an on-device receipt to the estate's hash-chained ledger +(SEAM-011: no local-only ledger). + +Consume-not-fork: all chaining/canonicalisation/verification is delegated to the vendored +canonical emitter (tools/inference_receipt_emitter.py, byte-verbatim from +prophet-platform apps/receipt-gateway). This file adds no chain logic of its own. + +CLI: + inferenced_shim.py --ledger --base-model-digest sha256:<64hex> \ + --task