riskrouter-recorder
README.md
# riskrouter-recorder
Drop-in recorders for Python and Node, with no dependencies, that turn the decision logs you already keep into
evidence. Each record and its salt stay in your own journal; only the record's salted SHA-256 and a short kind
are sent to an evidence log, so that later anyone holding the record can check it was never changed. The log can
be RiskRouter's (`https://api.riskrouter.eu`), your own instance, or none at all: with no key, nothing is sent and
the journal keeps everything.
| | Install | Read |
| --- | --- | --- |
| Python 3.8+ | `pip install riskrouter-recorder` | [`python/README.md`](python/README.md) |
| Node 20+ | `npm install riskrouter-recorder` | [`js/README.md`](js/README.md) |
What is in them:
- a recorder with a local journal, batching, retries and completeness chains;
- a Python `logging` handler, a decorator (Python) or function wrapper (Node), and OpenTelemetry span processors;
- agent adapters for LangChain and LangGraph and for the OpenAI Agents SDK: one record per run, with content
digests, never content;
- a local MCP server (Node, `riskrouter-recorder-mcp`) that gives any MCP client a `record_decision` tool. It
records the agent's own statement of what it decided and marks every record so; it never returns a record or a
salt to the agent;
- `riskrouter-logseal`, which seals a log stream segment by segment, and `riskrouter-otlp-sealer`, which seals
spans and log records at the OpenTelemetry Collector.
```sh
npx -y -p riskrouter-recorder riskrouter-recorder-mcp # the local MCP server, over stdio
```
In an MCP client's settings (setup steps for an assistant: [`llms-install.md`](llms-install.md)):
```json
{ "mcpServers": { "riskrouter-recorder": { "command": "npx",
"args": ["-y", "-p", "riskrouter-recorder", "riskrouter-recorder-mcp"],
"env": { "RISKROUTER_JOURNAL": "/absolute/path/to/evidence-journal.jsonl" } } } }
```
Its tools are `record_decision`, `evidence_lookup` and `evidence_status`; only `record_decision` writes, and
nothing deletes. With `RISKROUTER_API_KEY` set, each record's salted digest goes to the log; without it, nothing
leaves the machine.
RiskRouter also runs a hosted MCP endpoint for reading the log and its proofs, at `https://api.riskrouter.eu/mcp`
(`eu.riskrouter/evidence-log` in the official MCP Registry). That is the service, not this package.
## Seal a release from GitHub Actions
Seal the SHA-256 of each file of a release, so anyone can later show the artefact they hold is the one that
shipped, on the day it shipped. Only each record's salted digest leaves the runner; the files stay where they are.
With an SBOM, each record is a `cra.release` of the Cyber Resilience Act pack.
```yaml
on:
release:
types: [published]
jobs:
seal:
runs-on: ubuntu-latest
permissions:
contents: write # to attach the receipts to the release
steps:
- uses: actions/checkout@v4
- run: gh release download "$GITHUB_REF_NAME" --dir dist
env: { GH_TOKEN: "${{ github.token }}" }
- id: seal
uses: aniljangrabe-dev/riskrouter-recorder/github-action@main
with:
api-key: ${{ secrets.RISKROUTER_API_KEY }} # free sandbox key: https://riskrouter.eu/integrate
files: dist/*
# sbom: dist/sbom.cdx.json
- run: gh release upload "$GITHUB_REF_NAME" ${{ steps.seal.outputs.receipts }}/*.riskrouter.json
env: { GH_TOKEN: "${{ github.token }}" }
```
Each `<file>.riskrouter.json` is a receipt the published verifiers check offline. Attaching it to the release makes
the record and its salt public, which is fine for a public release; for a private one, keep the receipts privately
instead. Without the receipt, nobody, RiskRouter included, can tie the log entry to the file. The same sealer runs
outside GitHub: `RISKROUTER_API_KEY=… npx -p riskrouter-recorder riskrouter-seal-release --product acme/app --version 1.2.0 dist/*`.
## Checking what was recorded
A record's bundle is checked offline, without RiskRouter, by either of the open-source verifiers at
https://github.com/aniljangrabe-dev/riskrouter-verify. The record format, the salted digest and the log's proofs
are specified at https://riskrouter.eu/spec.
## About this repository
This repository is an exact copy of `recorders/` in RiskRouter's own repository, which is private; a workflow
copies every change here when it lands there. Issues are welcome here, and a pull request is read and, if accepted,
applied at the source and copied back. Report a security problem as https://riskrouter.eu/security says, not in
an issue.
Licence: Apache-2.0 ([`LICENSE`](LICENSE)), like the verifiers, the conformance suite and the integration kit. The
RiskRouter service itself is not licensed.