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.
- prism_verify-0.4.0/.github/workflows/ci.yml +35 -0
- prism_verify-0.4.0/.github/workflows/release.yml +176 -0
- prism_verify-0.4.0/.gitignore +21 -0
- prism_verify-0.4.0/CHANGELOG.md +209 -0
- prism_verify-0.4.0/LICENSE +21 -0
- prism_verify-0.4.0/PKG-INFO +191 -0
- prism_verify-0.4.0/README.es.md +137 -0
- prism_verify-0.4.0/README.fr.md +116 -0
- prism_verify-0.4.0/README.hi.md +116 -0
- prism_verify-0.4.0/README.it.md +137 -0
- prism_verify-0.4.0/README.ja.md +116 -0
- prism_verify-0.4.0/README.md +141 -0
- prism_verify-0.4.0/README.pt-BR.md +137 -0
- prism_verify-0.4.0/README.zh.md +116 -0
- prism_verify-0.4.0/SCORECARD.md +30 -0
- prism_verify-0.4.0/SECURITY.md +59 -0
- prism_verify-0.4.0/SHIP_GATE.md +67 -0
- prism_verify-0.4.0/assets/prism-verify-logo.png +0 -0
- prism_verify-0.4.0/design/01-research-grounding.md +237 -0
- prism_verify-0.4.0/design/02-standards-compliance.md +79 -0
- prism_verify-0.4.0/design/03-compensators.md +56 -0
- prism_verify-0.4.0/design/04-citation-verification.md +237 -0
- prism_verify-0.4.0/design/05-http-and-receipts.md +329 -0
- prism_verify-0.4.0/npm/LICENSE +21 -0
- prism_verify-0.4.0/npm/README.md +27 -0
- prism_verify-0.4.0/npm/bin/prism.js +18 -0
- prism_verify-0.4.0/npm/package.json +39 -0
- prism_verify-0.4.0/pyproject.toml +84 -0
- prism_verify-0.4.0/scripts/verify.py +37 -0
- prism_verify-0.4.0/src/prism/__init__.py +3 -0
- prism_verify-0.4.0/src/prism/__main__.py +6 -0
- prism_verify-0.4.0/src/prism/cli/__init__.py +0 -0
- prism_verify-0.4.0/src/prism/cli/main.py +393 -0
- prism_verify-0.4.0/src/prism/core/__init__.py +0 -0
- prism_verify-0.4.0/src/prism/core/citations.py +139 -0
- prism_verify-0.4.0/src/prism/core/engine.py +640 -0
- prism_verify-0.4.0/src/prism/core/routing.py +203 -0
- prism_verify-0.4.0/src/prism/core/setup.py +61 -0
- prism_verify-0.4.0/src/prism/core/stripping.py +127 -0
- prism_verify-0.4.0/src/prism/core/submodularity.py +97 -0
- prism_verify-0.4.0/src/prism/core/types.py +212 -0
- prism_verify-0.4.0/src/prism/http/__init__.py +20 -0
- prism_verify-0.4.0/src/prism/http/app.py +344 -0
- prism_verify-0.4.0/src/prism/http/auth.py +173 -0
- prism_verify-0.4.0/src/prism/http/errors.py +101 -0
- prism_verify-0.4.0/src/prism/http/webhook.py +300 -0
- prism_verify-0.4.0/src/prism/lenses/__init__.py +0 -0
- prism_verify-0.4.0/src/prism/lenses/base.py +225 -0
- prism_verify-0.4.0/src/prism/lenses/boundary.py +96 -0
- prism_verify-0.4.0/src/prism/lenses/contract.py +92 -0
- prism_verify-0.4.0/src/prism/lenses/groundedness.py +97 -0
- prism_verify-0.4.0/src/prism/lenses/invariant.py +95 -0
- prism_verify-0.4.0/src/prism/lenses/registry.py +56 -0
- prism_verify-0.4.0/src/prism/mcp/__init__.py +0 -0
- prism_verify-0.4.0/src/prism/mcp/server.py +194 -0
- prism_verify-0.4.0/src/prism/providers/__init__.py +0 -0
- prism_verify-0.4.0/src/prism/providers/anthropic.py +112 -0
- prism_verify-0.4.0/src/prism/providers/base.py +68 -0
- prism_verify-0.4.0/src/prism/providers/google.py +114 -0
- prism_verify-0.4.0/src/prism/providers/ollama.py +111 -0
- prism_verify-0.4.0/src/prism/providers/openai.py +131 -0
- prism_verify-0.4.0/src/prism/receipts/__init__.py +0 -0
- prism_verify-0.4.0/src/prism/receipts/signing.py +221 -0
- prism_verify-0.4.0/src/prism/receipts/store.py +481 -0
- prism_verify-0.4.0/src/prism/retrieval/__init__.py +1 -0
- prism_verify-0.4.0/src/prism/retrieval/oracle.py +241 -0
- prism_verify-0.4.0/tests/__init__.py +0 -0
- prism_verify-0.4.0/tests/conftest.py +19 -0
- prism_verify-0.4.0/tests/integration/__init__.py +0 -0
- prism_verify-0.4.0/tests/integration/test_citations.py +215 -0
- prism_verify-0.4.0/tests/integration/test_compensate_after_verify.py +102 -0
- prism_verify-0.4.0/tests/integration/test_http.py +330 -0
- prism_verify-0.4.0/tests/integration/test_meta_citations.py +197 -0
- prism_verify-0.4.0/tests/integration/test_verify_pipeline.py +151 -0
- prism_verify-0.4.0/tests/unit/__init__.py +0 -0
- prism_verify-0.4.0/tests/unit/test_citations.py +84 -0
- prism_verify-0.4.0/tests/unit/test_cli.py +141 -0
- prism_verify-0.4.0/tests/unit/test_engine.py +72 -0
- prism_verify-0.4.0/tests/unit/test_lenses.py +111 -0
- prism_verify-0.4.0/tests/unit/test_providers.py +105 -0
- prism_verify-0.4.0/tests/unit/test_receipts.py +445 -0
- prism_verify-0.4.0/tests/unit/test_retrieval.py +216 -0
- prism_verify-0.4.0/tests/unit/test_routing.py +161 -0
- prism_verify-0.4.0/tests/unit/test_signing.py +243 -0
- prism_verify-0.4.0/tests/unit/test_stripping.py +101 -0
- prism_verify-0.4.0/tests/unit/test_submodularity.py +120 -0
- prism_verify-0.4.0/tests/unit/test_webhook.py +265 -0
- 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
|