The seismograph for the agent ecosystem. Sign, govern, and debug the skills your agents run — before they slide.
Tiltmeter gives agent skills (SKILL.md files, plugins, MCP servers) what software packages got decades ago: cryptographic signatures, a governed registry, auditable approvals, and runtime debugging. It answers three questions about every skill:
- What is this? — signed, attributed, and tamper-evident (Ed25519 + content hashing)
- Is it allowed? — registry with namespaces, policies, and human approvals
- What did it do? — capture sessions, decision trees, deadlock detection, context drift
Web Console — dashboard & verification
CLI — sign & verify (tamper detection)
Server & REST API
- Ed25519 key-pair signing of skill content (SHA-256 content hash)
- Sigstore keyless signing (
sign --keyless): OIDC → Fulcio cert → Rekor transparency log, verified withverify --keyless - Provenance statement generation — in-toto Statement/v1 + SLSA v1.0 provenance
- SBOM generation for skill dependencies
- OCI artifact packaging: bundle signed skills as OCI images, push/pull any registry (
oci bundle/extract/push/pull) - Tamper detection: any byte change breaks verification
- Skill registry with namespaces (public/team/private)
- Policies per namespace:
auto_allow/auto_deny/require_approval/opa(real Rego via Open Policy Agent) - Human approval workflow: pending → approve/reject with reviewer + reason
- Audit log with JSON export (who approved what, when, why)
- Default-safe: unknown namespaces default to
require_approval
- Debug sessions capture events (LLM calls, tool calls, state changes)
- Decision tree reconstruction, deadlock detection, context drift scoring
- Timeline replay of any captured run
- Single-page console (dashboard, registry, approvals, policies, audit, debug)
- Full REST API for automation and CI integration
- SQLite storage (zero-config), FastAPI server
# install
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# start the server (REST + web console on :8765)
python3 -m uvicorn tiltmeter.server:app --host 0.0.0.0 --port 8765
# sign a skill file (Ed25519 keypair)
python3 -m tiltmeter.cli sign skill.md --author "Your Name" --output sig.json
# ...or sigstore keyless (OIDC → Fulcio → Rekor)
python3 -m tiltmeter.cli sign skill.md --keyless --output keyless.json
# verify it (tamper → INVALID)
python3 -m tiltmeter.cli verify skill.md --signature <sig> --public-key <key>
python3 -m tiltmeter.cli verify skill.md --keyless --bundle keyless.json
# OCI artifact packaging
python3 -m tiltmeter.cli oci bundle skill.md sig.json -o layout/
python3 -m tiltmeter.cli oci push layout/ myorg/skills --tag 1.0.0 --registry https://ghcr.io -u user -p $TOKEN
python3 -m tiltmeter.cli oci pull myorg/skills --tag 1.0.0 -o pulled/ --registry https://ghcr.io
# publish to the registry & manage
python3 -m tiltmeter.cli publish skill.md --name my-skill --namespace public --version 0.1.0
python3 -m tiltmeter.cli search my-skill
python3 -m tiltmeter.cli approve <skill_id> --action approve --reviewer you
# policies: built-in three-state, or real Rego via the OPA binary
python3 -m tiltmeter.cli policy-set public --type auto_allow
python3 -m tiltmeter.cli policy-set ops --type opa --rego-file policy.rego
# debug & replay
python3 -m tiltmeter.cli replay <session_id>
python3 -m tiltmeter.cli list --namespace publicOpen http://localhost:8765 for the web console.
| command | description |
|---|---|
sign <file> [--author] [--output] [--keyless] |
Sign a skill file (Ed25519 or sigstore keyless) |
verify <file> --signature --public-key | --keyless --bundle |
Verify signature + content hash |
oci bundle/extract/push/pull |
Package signed skills as OCI artifacts |
publish <file> --name [--namespace] [--version] |
Publish skill to registry |
search <query> [--namespace] |
Search the registry |
approve <skill_id> --action approve|reject [--reviewer] |
Approve/reject a skill |
policy-set <namespace> --type auto_allow|auto_deny|require_approval|opa [--rego/--rego-file] |
Set namespace policy (built-in or Rego) |
replay <session_id> |
Replay a debug session |
list [--namespace] [--status] |
List registry skills |
| method | path | description |
|---|---|---|
| GET | /api/stats |
Dashboard statistics |
| POST | /api/skills/sign · /api/skills/verify |
Sign / verify |
| GET/POST | /api/skills · /api/skills/{id} |
Registry CRUD + details |
| GET | /api/skills/search |
Search |
| POST | /api/skills/{id}/approve |
Approval decision |
| GET | /api/approvals |
Approval queue |
| GET/POST | /api/policies |
Policy management |
| GET | /api/audit-logs · /api/audit-logs/export |
Audit + export |
| POST | /api/debug/sessions · /api/debug/sessions/{id}/events |
Capture events |
| GET | /api/debug/sessions/{id}/tree · /deadlocks · /drift · /timeline |
Debug views |
| GET | /health |
Health check |
Full API reference: see AGENTS.md or the server source (tiltmeter/server.py).
GitHub Actions: every push/PR runs the full pytest suite on Python 3.11/3.12, plus a signature-gate job that verifies a signed example skill and proves tampering is caught.
The gate is a reusable composite action — drop it into any pipeline that consumes signed skills:
- uses: LandslideLab/Tiltmeter/.github/actions/tiltmeter-verify@main
with:
skill-file: path/to/skill.md
signature-file: path/to/skill.sig.json # output of `tiltmeter sign`It only needs cryptography (not the full Tiltmeter install) and fails the job on
any mismatch (content hash or Ed25519 signature). See .github/actions/tiltmeter-verify/
and examples/signed-skill/ for a working example.
pip install -r requirements.txt pytest
python -m pytest # 133 tests, ~95% coverage
python -m pytest --cov=tiltmeter --cov-report=term-missingThe suite isolates every test behind a fresh temporary SQLite database — it never
touches your real data/tiltmeter.db — and covers:
- signing — hash determinism, keypairs, sign/verify round-trips, tamper detection, SLSA v1 provenance & SBOM
- keyless — sigstore sign/verify with mocked OIDC boundaries (real E2E runs in CI via GitHub OIDC)
- oci — bundle/extract round-trip, tamper rejection, push/pull against an in-process fake registry
- policy — allow/deny/require-approval, Rego-like rules (eq/neq/contains/regex), real OPA/Rego evaluation (needs
opabinary; fails closed otherwise) - database — schema creation, immutability constraint
- CLI — exit codes, sign→verify round-trip, publish/approve/policy/search/list flows
- API — all 24 routes: stats, sign/verify, registry, approvals, policies, audit, debug, namespaces
- replay — decision trees, circular-wait deadlocks, context drift, timeline
├── tiltmeter/
│ ├── cli.py # Typer CLI (8 commands)
│ ├── server.py # FastAPI app (REST + web console)
│ ├── signing.py # Ed25519 sign/verify, provenance, SBOM
│ ├── policy.py # Policy engine (allow / deny / require-approval)
│ ├── replay.py # Debug sessions: tree, deadlock, drift, timeline
│ └── database.py # SQLite (aiosqlite)
├── tests/ # pytest suite (isolated temp DB per test)
├── .github/
│ ├── actions/tiltmeter-verify/ # reusable signature-gate action
│ └── workflows/ci.yml # tests + signature gate on push/PR
├── examples/signed-skill/ # signed example + demo signature
├── static/ # Web console (native HTML/CSS/JS, no build step)
├── run_server.py # Server entry point
├── docs/screenshots/ # This README's screenshots
└── requirements.txt
Python 3.12 · FastAPI · Typer + Rich · cryptography (Ed25519) · sigstore (keyless) · aiosqlite · vanilla HTML/CSS/JS
Apache-2.0 · Built for the LANDSLIDE human-machine collaboration initiative.


