prism-verify 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. prism_verify-0.4.0/.github/workflows/ci.yml +35 -0
  2. prism_verify-0.4.0/.github/workflows/release.yml +176 -0
  3. prism_verify-0.4.0/.gitignore +21 -0
  4. prism_verify-0.4.0/CHANGELOG.md +209 -0
  5. prism_verify-0.4.0/LICENSE +21 -0
  6. prism_verify-0.4.0/PKG-INFO +191 -0
  7. prism_verify-0.4.0/README.es.md +137 -0
  8. prism_verify-0.4.0/README.fr.md +116 -0
  9. prism_verify-0.4.0/README.hi.md +116 -0
  10. prism_verify-0.4.0/README.it.md +137 -0
  11. prism_verify-0.4.0/README.ja.md +116 -0
  12. prism_verify-0.4.0/README.md +141 -0
  13. prism_verify-0.4.0/README.pt-BR.md +137 -0
  14. prism_verify-0.4.0/README.zh.md +116 -0
  15. prism_verify-0.4.0/SCORECARD.md +30 -0
  16. prism_verify-0.4.0/SECURITY.md +59 -0
  17. prism_verify-0.4.0/SHIP_GATE.md +67 -0
  18. prism_verify-0.4.0/assets/prism-verify-logo.png +0 -0
  19. prism_verify-0.4.0/design/01-research-grounding.md +237 -0
  20. prism_verify-0.4.0/design/02-standards-compliance.md +79 -0
  21. prism_verify-0.4.0/design/03-compensators.md +56 -0
  22. prism_verify-0.4.0/design/04-citation-verification.md +237 -0
  23. prism_verify-0.4.0/design/05-http-and-receipts.md +329 -0
  24. prism_verify-0.4.0/npm/LICENSE +21 -0
  25. prism_verify-0.4.0/npm/README.md +27 -0
  26. prism_verify-0.4.0/npm/bin/prism.js +18 -0
  27. prism_verify-0.4.0/npm/package.json +39 -0
  28. prism_verify-0.4.0/pyproject.toml +84 -0
  29. prism_verify-0.4.0/scripts/verify.py +37 -0
  30. prism_verify-0.4.0/src/prism/__init__.py +3 -0
  31. prism_verify-0.4.0/src/prism/__main__.py +6 -0
  32. prism_verify-0.4.0/src/prism/cli/__init__.py +0 -0
  33. prism_verify-0.4.0/src/prism/cli/main.py +393 -0
  34. prism_verify-0.4.0/src/prism/core/__init__.py +0 -0
  35. prism_verify-0.4.0/src/prism/core/citations.py +139 -0
  36. prism_verify-0.4.0/src/prism/core/engine.py +640 -0
  37. prism_verify-0.4.0/src/prism/core/routing.py +203 -0
  38. prism_verify-0.4.0/src/prism/core/setup.py +61 -0
  39. prism_verify-0.4.0/src/prism/core/stripping.py +127 -0
  40. prism_verify-0.4.0/src/prism/core/submodularity.py +97 -0
  41. prism_verify-0.4.0/src/prism/core/types.py +212 -0
  42. prism_verify-0.4.0/src/prism/http/__init__.py +20 -0
  43. prism_verify-0.4.0/src/prism/http/app.py +344 -0
  44. prism_verify-0.4.0/src/prism/http/auth.py +173 -0
  45. prism_verify-0.4.0/src/prism/http/errors.py +101 -0
  46. prism_verify-0.4.0/src/prism/http/webhook.py +300 -0
  47. prism_verify-0.4.0/src/prism/lenses/__init__.py +0 -0
  48. prism_verify-0.4.0/src/prism/lenses/base.py +225 -0
  49. prism_verify-0.4.0/src/prism/lenses/boundary.py +96 -0
  50. prism_verify-0.4.0/src/prism/lenses/contract.py +92 -0
  51. prism_verify-0.4.0/src/prism/lenses/groundedness.py +97 -0
  52. prism_verify-0.4.0/src/prism/lenses/invariant.py +95 -0
  53. prism_verify-0.4.0/src/prism/lenses/registry.py +56 -0
  54. prism_verify-0.4.0/src/prism/mcp/__init__.py +0 -0
  55. prism_verify-0.4.0/src/prism/mcp/server.py +194 -0
  56. prism_verify-0.4.0/src/prism/providers/__init__.py +0 -0
  57. prism_verify-0.4.0/src/prism/providers/anthropic.py +112 -0
  58. prism_verify-0.4.0/src/prism/providers/base.py +68 -0
  59. prism_verify-0.4.0/src/prism/providers/google.py +114 -0
  60. prism_verify-0.4.0/src/prism/providers/ollama.py +111 -0
  61. prism_verify-0.4.0/src/prism/providers/openai.py +131 -0
  62. prism_verify-0.4.0/src/prism/receipts/__init__.py +0 -0
  63. prism_verify-0.4.0/src/prism/receipts/signing.py +221 -0
  64. prism_verify-0.4.0/src/prism/receipts/store.py +481 -0
  65. prism_verify-0.4.0/src/prism/retrieval/__init__.py +1 -0
  66. prism_verify-0.4.0/src/prism/retrieval/oracle.py +241 -0
  67. prism_verify-0.4.0/tests/__init__.py +0 -0
  68. prism_verify-0.4.0/tests/conftest.py +19 -0
  69. prism_verify-0.4.0/tests/integration/__init__.py +0 -0
  70. prism_verify-0.4.0/tests/integration/test_citations.py +215 -0
  71. prism_verify-0.4.0/tests/integration/test_compensate_after_verify.py +102 -0
  72. prism_verify-0.4.0/tests/integration/test_http.py +330 -0
  73. prism_verify-0.4.0/tests/integration/test_meta_citations.py +197 -0
  74. prism_verify-0.4.0/tests/integration/test_verify_pipeline.py +151 -0
  75. prism_verify-0.4.0/tests/unit/__init__.py +0 -0
  76. prism_verify-0.4.0/tests/unit/test_citations.py +84 -0
  77. prism_verify-0.4.0/tests/unit/test_cli.py +141 -0
  78. prism_verify-0.4.0/tests/unit/test_engine.py +72 -0
  79. prism_verify-0.4.0/tests/unit/test_lenses.py +111 -0
  80. prism_verify-0.4.0/tests/unit/test_providers.py +105 -0
  81. prism_verify-0.4.0/tests/unit/test_receipts.py +445 -0
  82. prism_verify-0.4.0/tests/unit/test_retrieval.py +216 -0
  83. prism_verify-0.4.0/tests/unit/test_routing.py +161 -0
  84. prism_verify-0.4.0/tests/unit/test_signing.py +243 -0
  85. prism_verify-0.4.0/tests/unit/test_stripping.py +101 -0
  86. prism_verify-0.4.0/tests/unit/test_submodularity.py +120 -0
  87. prism_verify-0.4.0/tests/unit/test_webhook.py +265 -0
  88. prism_verify-0.4.0/uv.lock +1504 -0
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ paths:
6
+ - "src/**"
7
+ - "tests/**"
8
+ - "pyproject.toml"
9
+ - ".github/workflows/**"
10
+ pull_request:
11
+ paths:
12
+ - "src/**"
13
+ - "tests/**"
14
+ - "pyproject.toml"
15
+ - ".github/workflows/**"
16
+ workflow_dispatch:
17
+
18
+ concurrency:
19
+ group: ${{ github.workflow }}-${{ github.ref }}
20
+ cancel-in-progress: true
21
+
22
+ jobs:
23
+ test:
24
+ runs-on: ubuntu-latest
25
+ strategy:
26
+ matrix:
27
+ python-version: ["3.11", "3.12", "3.13"]
28
+ steps:
29
+ - uses: actions/checkout@v5
30
+ - uses: astral-sh/setup-uv@v6
31
+ - run: uv python install ${{ matrix.python-version }}
32
+ - run: uv sync --all-extras
33
+ - run: uv run ruff check src/ tests/
34
+ - run: uv run mypy src/
35
+ - run: uv run pytest --tb=short
@@ -0,0 +1,176 @@
1
+ name: Release
2
+
3
+ # On a published GitHub Release this:
4
+ # 1. publishes the Python package to PyPI via Trusted Publishing (OIDC, no token),
5
+ # 2. builds PyInstaller binaries for each platform + uploads them (+ checksums) to the release,
6
+ # 3. publishes the @mcptoolshop/prism-verify npm wrapper (the npm-launcher) via Trusted Publishing.
7
+ #
8
+ # Org rule: publish workflows fire on `release: published` only. This is the repo's 2nd (and last)
9
+ # workflow file. The cross-OS binary matrix (macos/windows) is the explicit-request exception to the
10
+ # "1 OS / no macos" rule — the npm launcher distributes platform binaries, which requires it.
11
+ #
12
+ # First PyPI publish needs a PyPI *pending publisher* (project prism-verify, workflow release.yml;
13
+ # environment left "(Any)" — these jobs declare no GH environment). First npm publish needs a
14
+ # placeholder publish + a Trusted Publisher on npmjs.com for @mcptoolshop/prism-verify
15
+ # (workflow release.yml).
16
+
17
+ on:
18
+ release:
19
+ types: [published]
20
+ workflow_dispatch:
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ concurrency:
26
+ group: ${{ github.workflow }}-${{ github.ref }}
27
+ cancel-in-progress: false # never cancel an in-flight publish
28
+
29
+ jobs:
30
+ pypi:
31
+ name: Publish to PyPI (Trusted Publishing)
32
+ runs-on: ubuntu-latest
33
+ permissions:
34
+ id-token: write # OIDC handshake for PyPI Trusted Publishing — the only auth needed
35
+ timeout-minutes: 15
36
+ steps:
37
+ - uses: actions/checkout@v5
38
+
39
+ - name: Verify the tag matches pyproject version
40
+ run: |
41
+ TAG="${GITHUB_REF_NAME#v}"
42
+ PKG=$(grep -m1 '^version = ' pyproject.toml | sed -E 's/version = "(.*)"/\1/')
43
+ echo "tag=${TAG} pyproject=${PKG}"
44
+ if [ "${TAG}" != "${PKG}" ]; then
45
+ echo "::error::release tag ${TAG} does not match pyproject version ${PKG}"
46
+ exit 1
47
+ fi
48
+
49
+ - uses: astral-sh/setup-uv@v6
50
+
51
+ - name: Build sdist + wheel
52
+ run: uv build
53
+
54
+ - name: Check distribution metadata
55
+ run: uvx twine check dist/*
56
+
57
+ - name: Publish to PyPI
58
+ # Trusted Publishing: no token. PEP 740 attestations are on by default (action >= v1.11.0).
59
+ uses: pypa/gh-action-pypi-publish@release/v1
60
+
61
+ build-binaries:
62
+ name: Build PyInstaller binary (${{ matrix.target }})
63
+ strategy:
64
+ matrix:
65
+ include:
66
+ - os: ubuntu-latest
67
+ target: linux-x64
68
+ ext: ""
69
+ - os: macos-latest
70
+ target: darwin-arm64
71
+ ext: ""
72
+ # darwin-x64 omitted: macos-13 is deprecated; the arm64 binary runs via Rosetta.
73
+ - os: windows-latest
74
+ target: win-x64
75
+ ext: ".exe"
76
+ runs-on: ${{ matrix.os }}
77
+ timeout-minutes: 20
78
+ steps:
79
+ - uses: actions/checkout@v5
80
+ - uses: astral-sh/setup-uv@v6
81
+ - run: uv python install 3.12
82
+ - run: uv venv
83
+
84
+ - name: Install prism + PyInstaller
85
+ # Bundle the [http] extra so the binary covers the CLI + local (Ollama) verification + the
86
+ # HTTP service + citation verification. Hosted-provider SDKs come with the PyPI install.
87
+ run: uv pip install ".[http]" "pyinstaller>=6.9.0"
88
+
89
+ - name: Build binary
90
+ shell: bash
91
+ run: |
92
+ VERSION=${GITHUB_REF_NAME#v}
93
+ uv run pyinstaller --onefile --name prism --console \
94
+ --collect-submodules prism \
95
+ --collect-submodules pydantic \
96
+ src/prism/__main__.py
97
+ OUTNAME="prism-${VERSION}-${{ matrix.target }}${{ matrix.ext }}"
98
+ mv "dist/prism${{ matrix.ext }}" "dist/${OUTNAME}"
99
+ echo "ASSET_NAME=${OUTNAME}" >> "$GITHUB_ENV"
100
+
101
+ - name: Smoke-test the binary
102
+ shell: bash
103
+ run: dist/${{ env.ASSET_NAME }} --version
104
+
105
+ - uses: actions/upload-artifact@v4
106
+ with:
107
+ name: binary-${{ matrix.target }}
108
+ path: dist/${{ env.ASSET_NAME }}
109
+
110
+ release-binaries:
111
+ name: Upload binaries + checksums to the release
112
+ needs: build-binaries
113
+ runs-on: ubuntu-latest
114
+ permissions:
115
+ contents: write # upload assets to the release
116
+ steps:
117
+ - uses: actions/download-artifact@v4
118
+ with:
119
+ path: artifacts
120
+ merge-multiple: true
121
+
122
+ - name: Generate checksums
123
+ shell: bash
124
+ run: |
125
+ VERSION=${GITHUB_REF_NAME#v}
126
+ cd artifacts
127
+ sha256sum * > "checksums-${VERSION}.txt"
128
+ cat "checksums-${VERSION}.txt"
129
+
130
+ - uses: softprops/action-gh-release@v2
131
+ with:
132
+ files: artifacts/*
133
+
134
+ npm:
135
+ name: Publish npm wrapper (Trusted Publishing)
136
+ needs: release-binaries # the wrapper is only useful once the binaries are on the release
137
+ runs-on: ubuntu-latest
138
+ permissions:
139
+ id-token: write # npm provenance via Sigstore OIDC
140
+ timeout-minutes: 15
141
+ steps:
142
+ - uses: actions/checkout@v5
143
+
144
+ - name: Verify the npm wrapper version matches the tag
145
+ run: |
146
+ TAG="${GITHUB_REF_NAME#v}"
147
+ PKG=$(node -p "require('./npm/package.json').version")
148
+ echo "tag=${TAG} npm=${PKG}"
149
+ if [ "${TAG}" != "${PKG}" ]; then
150
+ echo "::error::release tag ${TAG} does not match npm/package.json version ${PKG}"
151
+ exit 1
152
+ fi
153
+
154
+ - uses: actions/setup-node@v4
155
+ with:
156
+ node-version: "22"
157
+ registry-url: "https://registry.npmjs.org"
158
+
159
+ - name: Install npm >= 11.5 for OIDC trusted-publishing auth
160
+ run: |
161
+ # Node 22's bundled npm 10.9 races on an in-place `npm install -g npm@latest`
162
+ # (MODULE_NOT_FOUND: promise-retry). Install npm@latest into a sandbox and shadow it.
163
+ SANDBOX="$HOME/.npm-cli-sandbox"
164
+ mkdir -p "$SANDBOX"
165
+ pushd "$SANDBOX" >/dev/null
166
+ echo '{"name":"npm-cli-sandbox","version":"0.0.0","private":true}' > package.json
167
+ npm install --no-save --no-audit --no-fund --silent npm@latest
168
+ popd >/dev/null
169
+ echo "$SANDBOX/node_modules/.bin" >> "$GITHUB_PATH"
170
+ "$SANDBOX/node_modules/.bin/npm" --version
171
+
172
+ - name: Publish wrapper with provenance (OIDC trusted publisher)
173
+ working-directory: npm
174
+ run: |
175
+ npm install --no-save --no-audit --no-fund
176
+ npm publish --provenance --access public
@@ -0,0 +1,21 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ *.egg
9
+ .eggs/
10
+ .venv/
11
+ venv/
12
+ .env
13
+ .mypy_cache/
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ *.db
17
+ *.sqlite
18
+ *.sqlite3
19
+ .coverage
20
+ htmlcov/
21
+ .polyglot-cache.json
@@ -0,0 +1,209 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.4.0] - 2026-06-02
11
+
12
+ prism becomes an **installable, HTTP-callable, independently-verifiable runtime service**.
13
+ Research-grounded by a 5-agent study-swarm (`wf_f0f8b9c8-e2c`, 40 cited findings) and
14
+ adversarially verified (3 decorrelated lenses) — design in `design/05-http-and-receipts.md`.
15
+
16
+ ### Added
17
+ - **Ed25519 receipts (production default), version-aware.** Receipt **schema v4** adds `alg` +
18
+ `kid`; new receipts are signed with **Ed25519 (RFC 8032)** so a *different tool* verifies a
19
+ prism receipt with prism's **public key — no shared secret** (closes the cross-tool trust gap
20
+ role-os flagged). Legacy v1/v2/v3 **HMAC receipts still verify**, and the verifier **whitelists
21
+ `alg` per receipt** (downgrade / algorithm-confusion refused). HMAC stays for legacy + explicit
22
+ `PRISM_SIGNING_SECRET`; `PRISM_DEV=1` now mints a dev Ed25519 key.
23
+ - `prism verify-receipt <receipt.json> [--public-key <pem>]` — verify a standalone receipt
24
+ (the cross-tool path; Ed25519 needs only the public key). Also `POST /verify-receipt`.
25
+ - `prism keygen` (generate an Ed25519 keypair) and `prism pubkey` (publish the public key + kid).
26
+ - **HTTP/FastAPI runtime surface** (`prism serve`, the `[http]` extra) — the same guarantees as
27
+ CLI/MCP over HTTP: `POST /verify` (sync; `Prefer: respond-async` → `202` + webhook delivery),
28
+ `GET /replay/{receipt_id}`, `POST /verify-receipt`, `GET /healthz`, OpenAPI 3.1 at `/docs`.
29
+ Bearer **API-key auth** (hashed at rest, constant-time, fail-closed), **RFC 9457
30
+ `application/problem+json`** errors, `Idempotency-Key` (replay / `409` in-flight / `422`
31
+ mismatch), and denial-of-wallet back-pressure (artifact size cap + per-key rate limit +
32
+ stricter failed-auth limiter).
33
+ - **Signed-webhook escalate channel** — Standard-Webhooks HMAC over `id.timestamp.payload`
34
+ (300s tolerance, multi-signature rotation), an **SSRF guard** (https-only; rejects
35
+ loopback/RFC1918/link-local/metadata, v4+v6), bounded retry → dead-letter, and the
36
+ **`send_cancel_event()` compensator** (the named undo for the irreversible verdict POST).
37
+ - **PyPI Trusted Publishing** (`.github/workflows/release.yml`) — OIDC, no long-lived token;
38
+ builds with `uv build` and publishes via `pypa/gh-action-pypi-publish` (PEP 740 attestations on
39
+ by default) on `release: published`. First publish uses a PyPI *pending publisher*.
40
+ - **npm launcher** (`@mcptoolshop/prism-verify`) — zero-Python `npx @mcptoolshop/prism-verify` via
41
+ [`@mcptoolshop/npm-launcher`](https://github.com/mcp-tool-shop-org/npm-launcher): the release
42
+ builds PyInstaller binaries (linux-x64 / darwin-arm64 / win-x64) + a `checksums-<version>.txt`,
43
+ and the wrapper downloads + **SHA256-verifies** the platform binary at first run. Published via
44
+ npm Trusted Publishing (OIDC provenance). PyPI remains the primary, full-featured distribution
45
+ (hosted-provider SDKs + all extras); the npm binary bundles the CLI + local Ollama + HTTP + citations.
46
+
47
+ ### Changed
48
+ - MCP + HTTP now share one engine factory (`prism.core.setup.build_default_engine`) so the
49
+ transports cannot drift.
50
+ - `cryptography` added as a core dependency (Ed25519 signing/verification).
51
+
52
+ ### Migration notes
53
+ - A v0.3 `~/.prism/receipts.db` migrates to schema v4 in place (`alg` / `kid` columns added);
54
+ legacy rows backfill `alg=HMAC-SHA256` and keep their original signatures. To verify legacy
55
+ HMAC receipts after moving to Ed25519, keep `PRISM_SIGNING_SECRET` set alongside the new key.
56
+ - The honest ceiling is disclosed: an on-disk private key is forgeable by a local-root attacker
57
+ (same as the HMAC secret) — Ed25519 buys **third-party verifiability**, not stronger
58
+ anti-forgery. HSM + a transparency log is the named hardening path.
59
+
60
+ ### Standards
61
+ - workflow-standards stays **15/15**; each new surface (HTTP / webhook / receipt-signing) carries
62
+ its own compliance section and a NO-SKIP compensators table (`design/05`, `design/03`).
63
+
64
+ ## [0.3.2] - 2026-06-01
65
+
66
+ ### Added
67
+ - **`prism verify --gate`** — opt-in verdict-coded exit status for shell gating (`0` accept,
68
+ `10` revise, `20` refuse, `30` escalate). The default (no `--gate`) stays exit `0` on any
69
+ successful verification, preserving the CLI contract. Lets a shell/CI step — e.g. the role-os
70
+ citation-verification gate — branch on the verdict without parsing JSON.
71
+
72
+ ### Fixed
73
+ - **Citation retrieval oracle hardening** (surfaced by the role-os citation-gate dogfood). The
74
+ arXiv existence check now uses `https://export.arxiv.org` directly (the `http://` endpoint
75
+ 301-redirects, which broke the lookup), sends a descriptive `User-Agent`, follows redirects, and
76
+ **retries transient `429`/`5xx` with backoff** — arXiv rate-limits anonymous bursts, so a burst
77
+ of citations was previously escalating instead of resolving (the gap the v0.3 study-swarm's Q1
78
+ predicted). `CitationOracle(retry_delays=...)` is injectable; tests pass `()` for no sleeps.
79
+
80
+ ## [0.3.1] - 2026-06-01
81
+
82
+ A post-ship adversarial verification pass (3 decorrelated lenses over the v0.3.0 diff) found and
83
+ fixed real defects. v0.3.0 stays immutable; these land as a patch.
84
+
85
+ ### Fixed
86
+ - **Citation oracle cache (was a silent wrong verdict).** The per-identifier cache held the full
87
+ existence result, but RESOLVED-vs-METADATA_MISMATCH is title-dependent; a repeated arXiv-id / DOI
88
+ with a different claimed title inherited the first citation's verdict (and the cache is
89
+ engine-lifetime, so it leaked across requests). The cache now holds only the title-independent
90
+ retrieved record and recomputes the title match per citation.
91
+ - **DOI request hardening + pin integrity.** An unvalidated DOI suffix was interpolated into the
92
+ Crossref path (path-traversal / query-param injection), and the signed retrieval pin recorded a
93
+ URL different from the one httpx actually fetched. The DOI pattern now forbids `?`/`#`/whitespace,
94
+ `..` segments are rejected, `mailto` travels as a real query param, and the pin records the URL
95
+ actually issued.
96
+ - **MCP `verify` tool now advertises `citations`.** The CLI was migrated in v0.3.0 but the MCP
97
+ inputSchema still enumerated only `code`/`tool_call`, making the headline feature unreachable via MCP.
98
+ - **Groundedness prompt-injection defense-in-depth.** The claim and retrieved source are wrapped in
99
+ `<<<...>>>` untrusted-data markers; groundedness stays advisory over the sound existence floor.
100
+
101
+ ### Tests
102
+ - Crossref-transient (5xx / 429 / timeout / unparseable) → UNRESOLVABLE (not FABRICATED); the cache
103
+ title-recompute regression; DOI path-traversal rejection; pin-equals-request-URL; a v2→v3 receipt
104
+ migration; and the MR1 metamorphic test now pins its verdict value.
105
+
106
+ ### Standards
107
+ - workflow-standards remains 15/15; this is a correctness / hardening patch with no scope change.
108
+
109
+ ## [0.3.0] - 2026-06-01
110
+
111
+ ### Added
112
+ - **Citation verification (headline).** A new `ArtifactType.CITATIONS` artifact (a JSON array of
113
+ citations) is adjudicated through a two-stage pipeline kept deliberately distinct:
114
+ - a **deterministic retrieval existence floor** (`prism.retrieval`) — arXiv-ID-first, then
115
+ Crossref DOI — that ANDON-refuses a non-resolving citation (`FABRICATED`) and distinguishes an
116
+ oracle-down/blocked retrieval (`UNRESOLVABLE` → escalate) from a genuine non-resolution (never
117
+ read as fabrication);
118
+ - a **deterministic numeric guard** that catches the "95.8% vs 89%" class an NLI/LLM lens is
119
+ structurally blind to (Naik 2018); and
120
+ - a **RAG-fed Groundedness (L4) lens** given the *retrieved* title+abstract, on a family-different
121
+ verifier.
122
+ Two-axis verdict mapping with per-citation action verbs (DROP / FIX METADATA / FIX TO MATCH
123
+ SOURCE / RETRIEVE MANUALLY); the protocol's `CANNOT_CONFIRM` maps to ESCALATE. Research grounding
124
+ + design: `design/04-citation-verification.md`.
125
+ - **prism-on-prism meta-test (EXTERNAL_VERIFIER 1→3).** Verifies prism's own design-doc citations
126
+ through a different family + retrieval-backed existence, asserting metamorphic relations (corrupt
127
+ id → existence flips to refuse; numeric swap → off accept; author reorder → invariant). Non-
128
+ circular: it checks retrieval, not recall. Closes the launch bootstrap skip.
129
+ - **Compensate-after-verify flow (NAMED_COMPENSATORS 2→3).** verify → signed receipt → delete /
130
+ prune → asserts the undo, proving the named compensator end-to-end.
131
+ - Receipt **schema v3**: signs `artifact_type` and a hash of the per-citation `retrieval_pins` (the
132
+ retrieval query + retrieved-source SHA-256), so a citation verdict is replayable.
133
+
134
+ ### Changed
135
+ - **Router walks configured families.** `select_verifier()` skips candidate families with no
136
+ configured provider instead of dead-ending on `VERIFIER_UNAVAILABLE` — fixes the out-of-box
137
+ `prism verify` trap (an Anthropic caller with only a local provider was refused).
138
+ - **`BUDGET_EXCEEDED` is now enforced** as a hard latency-budget timeout on the lens fan-out (was
139
+ declared in `RefusalReason` but never raised).
140
+ - Hosted verifier model IDs reconciled with provider lineups (`gpt-5.4-mini`, `claude-sonnet-4-6`,
141
+ `claude-haiku-4-5-20251001`), guarded by a test; local verifier pinned to the non-thinking
142
+ `mistral-small:24b` with `format=json` + `think=false`.
143
+
144
+ ### Fixed
145
+ - **Security:** the Google provider sends its API key via the `x-goog-api-key` header instead of the
146
+ URL query string, so the credential no longer leaks into error reprs / tracebacks / logs.
147
+ - OpenAI provider uses `max_completion_tokens` (GPT-5 / o-series reject `max_tokens`) and omits
148
+ `temperature` for reasoning models.
149
+ - The SQLite receipt store is safe across threads (`check_same_thread=False` + a reentrant lock) and
150
+ supports the context-manager protocol for deterministic close.
151
+
152
+ ### Migration notes
153
+ - An existing `~/.prism/receipts.db` is migrated to schema v3 in place on first open (`artifact_type`
154
+ + `retrieval_pins` columns added); legacy v1/v2 rows keep their original signatures and still verify.
155
+
156
+ ### Standards
157
+ - workflow-standards **12/15 → 15/15**: EXTERNAL_VERIFIER 1→3, NAMED_COMPENSATORS 2→3, and
158
+ BUDGET_EXCEEDED enforced. See `design/02-standards-compliance.md`.
159
+
160
+ ## [0.2.0] - 2026-06-01
161
+
162
+ ### Added
163
+ - **Replayable prompt pinning (PIN_PER_STEP).** Receipts now record `lens_prompt_hashes`
164
+ — a SHA-256 of each lens call's `(system_prompt, user_prompt)` — stamped by the engine
165
+ and signed into the receipt, so a verification is byte-for-byte replayable.
166
+ - **Named compensators.** `prism receipt delete <id>` and
167
+ `prism receipt prune --older-than <duration> --yes` (plus the `store.delete_receipt` /
168
+ `store.prune` APIs) undo the receipt store's only irreversible write.
169
+ - `PRISM_SIGNING_SECRET` / `PRISM_DEV` configuration for the receipt signing secret,
170
+ documented in the README and SECURITY.md.
171
+ - First end-to-end integration test of `engine.verify()` against mock providers (respx).
172
+ - SECURITY.md with a real threat model and an honest tamper-evident (not tamper-proof)
173
+ integrity-ceiling disclosure.
174
+
175
+ ### Changed
176
+ - **Expanded the signed-receipt scope.** The HMAC now covers `reasoning_visibility_mode`,
177
+ `confidence`, `retryable`, a hash of the lens results, and the lens prompt hashes — not
178
+ just the original 7 fields. Receipts carry a `schema_version`; legacy v1 receipts still
179
+ verify after the automatic in-place schema migration.
180
+ - The receipt store **requires** an explicit signing secret (`PRISM_SIGNING_SECRET`, an
181
+ explicit argument, or `PRISM_DEV=1`) and refuses the built-in dev key otherwise.
182
+ - CI now runs when workflow files change (`.github/workflows/**` added to the path filter).
183
+
184
+ ### Fixed
185
+ - Out-of-enum verifier output (e.g. `severity: "high"`, `outcome: "accept"`) or
186
+ markdown-fenced JSON no longer crashes `verify()` with a raw stack; lens responses
187
+ degrade gracefully to an `errored` UNCERTAIN result.
188
+ - Verdict aggregation: a FAIL with only minor (or no) findings no longer collapses to
189
+ ACCEPT, and a mixed PASS + UNCERTAIN now ESCALATEs instead of silently ACCEPTing.
190
+ - A provider outage now refuses with `VERIFIER_UNAVAILABLE` (retryable) instead of being
191
+ misclassified as a non-retryable `LENS_COLLAPSE`.
192
+ - Ollama / OpenAI providers guard malformed response bodies (raise `ProviderError`
193
+ instead of a bare `KeyError`).
194
+ - Routing map's local-tier model id corrected (`qwen3-32b` → `qwen3:32b`) to match the
195
+ Ollama provider.
196
+ - `prism replay` no longer leaks its SQLite connection on the receipt-not-found path.
197
+
198
+ ### Migration notes
199
+ - Receipts written by v0.1.0 under the built-in dev key report `signature_valid: false`
200
+ once a real `PRISM_SIGNING_SECRET` is set — expected (different key), not tampering.
201
+ - An existing `~/.prism/receipts.db` is migrated in place on first open (new columns
202
+ added; legacy rows keep their original signatures).
203
+
204
+ ## [0.1.0] - 2026-06-01
205
+
206
+ ### Added
207
+ - Initial release: family-different routing, reasoning-stripping, multi-lens (≥3)
208
+ verification with submodularity-aware lens-collapse refusal, SQLite HMAC-signed
209
+ receipts, a CLI (`verify` / `replay`), and an MCP server. 41 tests; CI on 3.11–3.13.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mcp-tool-shop
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: prism-verify
3
+ Version: 0.4.0
4
+ Summary: Runtime adjudication service for agent workflows. Family-different, reasoning-stripped, multi-lens verification with replayable receipts.
5
+ Project-URL: Homepage, https://github.com/mcp-tool-shop-org/prism-verify
6
+ Project-URL: Repository, https://github.com/mcp-tool-shop-org/prism-verify
7
+ Project-URL: Issues, https://github.com/mcp-tool-shop-org/prism-verify/issues
8
+ Author-email: mcp-tool-shop <64996768+mcp-tool-shop@users.noreply.github.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: adjudication,agent,llm,mcp,verification
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Quality Assurance
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: click>=8.0
21
+ Requires-Dist: cryptography>=42.0
22
+ Requires-Dist: httpx>=0.27
23
+ Requires-Dist: pydantic>=2.0
24
+ Requires-Dist: ulid-py>=1.1
25
+ Provides-Extra: all
26
+ Requires-Dist: anthropic>=0.40; extra == 'all'
27
+ Requires-Dist: fastapi>=0.115; extra == 'all'
28
+ Requires-Dist: google-genai>=1.0; extra == 'all'
29
+ Requires-Dist: mcp>=1.0; extra == 'all'
30
+ Requires-Dist: openai>=1.50; extra == 'all'
31
+ Requires-Dist: uvicorn>=0.30; extra == 'all'
32
+ Provides-Extra: anthropic
33
+ Requires-Dist: anthropic>=0.40; extra == 'anthropic'
34
+ Provides-Extra: dev
35
+ Requires-Dist: mypy>=1.11; extra == 'dev'
36
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
37
+ Requires-Dist: pytest>=8.0; extra == 'dev'
38
+ Requires-Dist: respx>=0.21; extra == 'dev'
39
+ Requires-Dist: ruff>=0.6; extra == 'dev'
40
+ Provides-Extra: google
41
+ Requires-Dist: google-genai>=1.0; extra == 'google'
42
+ Provides-Extra: http
43
+ Requires-Dist: fastapi>=0.115; extra == 'http'
44
+ Requires-Dist: uvicorn>=0.30; extra == 'http'
45
+ Provides-Extra: mcp
46
+ Requires-Dist: mcp>=1.0; extra == 'mcp'
47
+ Provides-Extra: openai
48
+ Requires-Dist: openai>=1.50; extra == 'openai'
49
+ Description-Content-Type: text/markdown
50
+
51
+ <p align="center">
52
+ <a href="README.ja.md">日本語</a> | <a href="README.zh.md">中文</a> | <a href="README.es.md">Español</a> | <a href="README.fr.md">Français</a> | <a href="README.hi.md">हिन्दी</a> | <a href="README.it.md">Italiano</a> | <a href="README.pt-BR.md">Português (BR)</a>
53
+ </p>
54
+
55
+ <p align="center">
56
+ <img src="https://raw.githubusercontent.com/mcp-tool-shop-org/prism-verify/main/assets/prism-verify-logo.png" alt="prism-verify logo" width="500">
57
+ </p>
58
+
59
+ # prism-verify
60
+
61
+ Runtime adjudication service for agent workflows. Family-different, reasoning-stripped, multi-lens verification with replayable receipts.
62
+
63
+ ## Install
64
+
65
+ Install the `prism` CLI (and the HTTP service) on your PATH:
66
+
67
+ ```bash
68
+ uv tool install prism-verify # or: pipx install prism-verify
69
+ ```
70
+
71
+ Zero Python? Use the npm launcher (downloads + SHA256-verifies a prebuilt binary):
72
+
73
+ ```bash
74
+ npx @mcptoolshop/prism-verify verify --artifact @file.py --intent "..." --caller-family openai
75
+ ```
76
+
77
+ Or add it as a library — extras: `[anthropic]` `[openai]` `[google]` `[mcp]` `[http]` `[all]`:
78
+
79
+ ```bash
80
+ uv add prism-verify
81
+ # or
82
+ pip install "prism-verify[all]"
83
+ ```
84
+
85
+ ## Quick start
86
+
87
+ Prism always verifies with a model family **different** from the caller's (Lock 1), so
88
+ configure at least one alternate-family provider. Set a signing secret (or `PRISM_DEV=1`
89
+ for local play) so receipts can be written:
90
+
91
+ ```bash
92
+ export PRISM_SIGNING_SECRET="$(openssl rand -hex 32)"
93
+ export ANTHROPIC_API_KEY="sk-ant-..." # alt-family verifier for an OpenAI-family caller
94
+
95
+ prism verify \
96
+ --artifact @myfile.py \
97
+ --intent "Sort a list in O(n log n)" \
98
+ --caller-family openai \
99
+ --provider anthropic
100
+ ```
101
+
102
+ ## Architecture
103
+
104
+ Prism enforces four architectural locks at the API contract:
105
+
106
+ 1. **Family-different** — caller's model family is always excluded from verification
107
+ 2. **Reasoning-stripped** — producer CoT is stripped before crossing the family boundary
108
+ 3. **Multi-lens** — at least 3 independent lenses run in parallel
109
+ 4. **Submodularity-aware** — refuses if lenses agree too much (collapsed signal)
110
+
111
+ ## HTTP service
112
+
113
+ Run prism as an HTTP service (needs the `[http]` extra):
114
+
115
+ ```bash
116
+ prism serve --host 127.0.0.1 --port 8000 # OpenAPI docs at /docs
117
+ ```
118
+
119
+ | Endpoint | What it does |
120
+ |---|---|
121
+ | `POST /verify` | Verify an artifact (same contract as the CLI). Blocks within the budget; `Prefer: respond-async` + a `webhook` URL → `202`, verdict delivered to the (signed) webhook. |
122
+ | `GET /replay/{receipt_id}` | The signed receipt + `signature_valid`. |
123
+ | `POST /verify-receipt` | Verify a standalone receipt (cross-tool). |
124
+ | `GET /healthz` | Liveness + configured verifier families (no auth). |
125
+
126
+ Set API keys (hashed at rest) — prism is **fail-closed**, so `/verify` is refused until keys
127
+ are configured or you opt into local no-auth:
128
+
129
+ ```bash
130
+ export PRISM_API_KEYS="<sha256(key1)>,<sha256(key2)>" # callers send: Authorization: Bearer <key>
131
+ export PRISM_WEBHOOK_SECRET="<random>" # to sign async/escalate webhook deliveries
132
+ # local dev only:
133
+ export PRISM_HTTP_ALLOW_NO_AUTH=1
134
+ ```
135
+
136
+ Errors are RFC 9457 `application/problem+json`; `POST /verify` honours an `Idempotency-Key`
137
+ header and a per-key rate limit (`429` + `Retry-After`). Async/escalate webhooks are
138
+ Standard-Webhooks-signed, SSRF-guarded (no internal/metadata targets), retried, and carry a
139
+ named cancel-event compensator.
140
+
141
+ ## Receipts & signing (Ed25519, verifiable by anyone)
142
+
143
+ Every verification produces a signed, replayable receipt in `~/.prism/receipts.db`. v0.4 signs
144
+ new receipts with **Ed25519 (RFC 8032)** by default, so **a different tool can verify a prism
145
+ receipt with prism's public key — no shared secret**:
146
+
147
+ ```bash
148
+ prism keygen --out ~/.prism/signing_key.pem # generate an Ed25519 keypair
149
+ export PRISM_SIGNING_KEY=~/.prism/signing_key.pem
150
+ prism pubkey # publish this public key + kid to consumers
151
+
152
+ # a consumer (e.g. role-os) verifies a receipt with ONLY the public key:
153
+ prism verify-receipt receipt.json --public-key prism-pub.pem
154
+ ```
155
+
156
+ The signature covers the verdict, the pre/post-strip artifact hashes, the verifier model, the
157
+ submodularity matrix, the per-lens prompt hashes (byte-for-byte replayable), the citation
158
+ retrieval pins, and the signing `alg`/`kid`. Legacy **HMAC** receipts still verify (set
159
+ `PRISM_SIGNING_SECRET`); `PRISM_DEV=1` mints a dev key for local play. Prism **refuses to start**
160
+ the verify / replay / serve / MCP paths if no key is configured, rather than silently signing
161
+ with a publicly known key.
162
+
163
+ Manage stored receipts with the compensator commands:
164
+
165
+ ```bash
166
+ prism receipt delete <receipt_id>
167
+ prism receipt prune --older-than 90d --yes
168
+ ```
169
+
170
+ ## Security & privacy
171
+
172
+ - **Threat model.** Prism reads the artifact + intent you pass and the verifier models'
173
+ responses, and writes signed receipts to a local SQLite DB. It does **not** read your
174
+ source tree, environment, or credentials beyond the provider API keys you supply via
175
+ environment variables. Receipt signatures give **third-party verifiability** (Ed25519: a
176
+ consumer verifies with the public key, no shared secret) but are not tamper-*proof* against a
177
+ local-root attacker who can read the on-disk private key — that's the same ceiling as the HMAC
178
+ secret. For genuine tamper-resistance, hold the key in an HSM and anchor receipts in a
179
+ transparency log (the named hardening path).
180
+ - **HTTP surface.** `prism serve` binds loopback by default, is **fail-closed** (no `/verify`
181
+ without API keys), hashes keys at rest, and **SSRF-guards** caller-supplied webhook URLs
182
+ (no internal/link-local/metadata targets). It runs caller-supplied artifacts through a model;
183
+ an artifact may *attempt* prompt injection but cannot change the verdict schema or exfiltrate
184
+ prism's provider keys.
185
+ - **No telemetry.** Prism sends requests only to the model providers you configure
186
+ (Anthropic / OpenAI / Google / local Ollama). Nothing else.
187
+ - Full policy: [SECURITY.md](SECURITY.md).
188
+
189
+ ## License
190
+
191
+ MIT