diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5731b0f9..6bac08dd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,7 +91,7 @@ gh api "/repos/dativo-io/talon/actions/workflows/release.yml/runs?per_page=1" -q docker pull ghcr.io/dativo-io/talon:latest ``` -Check that release notes link at least one share artifact (screenshot, GIF, or migration snippet). +Check that release notes link at least one share artifact (screenshot, GIF, or migration snippet). To refresh Mission Control screenshots, see [docs/contributor/mission-control-screenshots.md](docs/contributor/mission-control-screenshots.md). ## Weekly Repo Quality Scorecard (maintainers) diff --git a/LIMITATIONS.md b/LIMITATIONS.md index 588d70d1..c5e708dd 100644 --- a/LIMITATIONS.md +++ b/LIMITATIONS.md @@ -56,3 +56,4 @@ A valid signature proves that this evidence record was signed with the deploymen - [Evidence integrity specification](docs/reference/evidence-integrity-spec.md) — byte-exact fields, serialization, signing, and independent verification - [Threat model](docs/reference/threat-model.md) — attack surface, trust boundaries, and key-management assumptions - [Reproducible benchmarks](docs/reference/benchmarks.md) — run `make benchmarks` on your hardware; retry/fallback overhead not included until Epic #113 lands. +- [Sample auditor pack](examples/auditor-pack/README.md) — example signed export and compliance report (`make auditor-pack`) diff --git a/Makefile b/Makefile index ed70f3d9..8d5c5ce6 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ ifeq ($(UNAME_S),Darwin) GO_ENV := env -u CC CC=/usr/bin/clang CGO_ENABLED=1 endif -.PHONY: help build install test test-integration test-e2e test-smoke test-all test-ssot-gate conformance benchmarks lint fmt clean vet mod-tidy check docker-build demo-gateway demo-full demo-clean verify-flow0 nosec-count +.PHONY: help build install test test-integration test-e2e test-smoke test-all test-ssot-gate conformance benchmarks auditor-pack verify-newcomer lint fmt clean vet mod-tidy check docker-build demo-gateway demo-full demo-clean verify-flow0 nosec-count # Conformance suite: the evidence + policy paths whose passing test/subtest # count is published as Talon's honest conformance number. See @@ -66,6 +66,12 @@ conformance: ## Run the evidence + policy conformance suite and print the passin benchmarks: ## Run reproducible micro-benchmarks (gateway overhead, PII scan, evidence write) @bash scripts/run-benchmarks.sh +auditor-pack: ## Generate sample auditor pack from docker-compose demo (examples/auditor-pack/) + @bash scripts/generate-auditor-pack.sh + +verify-newcomer: ## Smoke-test cold-start path (talon init + dry-run) + @bash scripts/verify-newcomer-path.sh + lint: ## Run linter @golangci-lint run ./... diff --git a/README.md b/README.md index 08faaadf..2d406d10 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,20 @@ llm: --- +## Proof Pack (trust & verification) + +Artifacts a skeptical reviewer can grep in one session: + +- [Limitations](LIMITATIONS.md) — what Talon does and does not prove +- [Threat model](docs/reference/threat-model.md) — attack surface and trust boundaries +- [Evidence integrity specification](docs/reference/evidence-integrity-spec.md) — byte-exact signing and verification +- [Conformance suite](docs/reference/conformance.md) — `make conformance` (evidence + policy paths) +- [Benchmarks](docs/reference/benchmarks.md) — `make benchmarks` on your hardware +- [Sample auditor pack](examples/auditor-pack/README.md) — signed export + compliance report (`make auditor-pack`) +- [Roadmap & focus](ROADMAP.md) — public anti-goals and EMEA SMB direction + +--- + ## Docs - [Documentation index](docs/README.md) diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index f26ff980..8d7d0429 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -1,6 +1,6 @@ # Talon Quick Start -This page points you to the right doc for what you want to do. +This page points you to the right doc for what you want to do. For the full documentation map, see [docs/README.md](README.md). --- @@ -13,72 +13,69 @@ This page points you to the right doc for what you want to do. → [OpenAI proxy quickstart](tutorials/proxy-quickstart.md) **2.** I'm building something new and want controls from day one. -→ [Your first agent with Talon](tutorials/first-governed-agent.md) +→ [Your first governed agent](tutorials/first-governed-agent.md) -**3.** I want to understand how it works before touching anything. +**3.** I want to understand how it works before touching anything (no API key). → [60-second demo (no API key)](tutorials/quickstart-demo.md) +**4.** I need to review what an auditor handoff looks like. +→ [Sample auditor pack](../examples/auditor-pack/README.md) + +--- + +## Install (native binary) + +See the [README install matrix](../README.md#install). Summary: + +| Platform | Recommended | +|----------|-------------| +| **macOS / arm64 Linux** | `git clone … && make install` or `go install github.com/dativo-io/talon/cmd/talon@latest` | +| **linux/amd64 server** | Release tarball or `curl -sSL https://install.gettalon.dev \| sh` | +| **No install** | [Docker Compose demo](../examples/docker-compose/README.md) | + +On macOS, if linking fails with `unsupported tapi file type`, use `make install` (sets system Clang) or `CC=/usr/bin/clang CGO_ENABLED=1 go install …`. + --- ## Minimal commands (if you already know Talon) ```bash # Install (from repo) -make build # → bin/talon +make install # → $(go env GOPATH)/bin/talon + +# Secrets key (required for vault) +export TALON_SECRETS_KEY="$(openssl rand -hex 32)" + +# New project +mkdir my-agents && cd my-agents +talon init --scaffold --name my-agent -# New project (in a terminal: interactive wizard; in scripts/CI: use --scaffold or --pack) -mkdir my-agents && cd my-agents && talon init -# Non-interactive: talon init --scaffold or talon init --pack openclaw +# Policy check without LLM spend +talon run --dry-run "Your query here" -# Set key and run +# Live run (needs provider key) export OPENAI_API_KEY=sk-proj-... -talon run "Your query here" # Uses agent name from policy when --agent omitted +talon run "Your query here" # Server (API + dashboard + optional gateway/proxy) export TALON_ADMIN_KEY="replace-with-strong-admin-key" talon serve --port 8080 -# OpenAI-compatible local drop-in (dev): OPENAI_BASE_URL=http://127.0.0.1:8080/v1 -talon serve --proxy-quickstart --port 8080 -# With LLM gateway: talon serve --gateway --gateway-config examples/gateway/talon.config.gateway.yaml -# With MCP proxy: talon serve --proxy-config path/to/proxy.yaml ``` -## Verify It Works (2-minute governance loop) +Verify the cold-start path from repo root: `make verify-newcomer`. -After running your first agent, prove the governance loop is working: - -```bash -# 1. List the most recent governance evidence (audit trail) -talon audit list --limit 1 - -# 1a. Tail the live operational projection (same feed as dashboard recent activity) -talon events tail --url http://localhost:8080 - -# Expected output: -# ┌──────┬──────────────────────────────────┬─────────┬──────────┐ -# │ ID │ Timestamp │ Agent │ Decision │ -# ├──────┼──────────────────────────────────┼─────────┼──────────┤ -# │ evt_1│ 2026-04-14T22:08:00Z │ default │ approved │ -# └──────┴──────────────────────────────────┴─────────┴──────────┘ - -# 2. Verify a specific evidence entry -talon audit verify - -# Expected output: -# ✅ Evidence verified: policy=allow, reason=query matches allowlist rule #1 -``` - -This confirms Talon is logging decisions and you can inspect the full audit trail at any time. +--- -If evidence persistence degrades, `GET /v1/status` exposes: -- `evidence_ok=false` -- `last_good_write` -- `evidence_error` and `evidence_error_at` +## Trust artifacts (Proof Pack) -For full configuration and options see [Configuration and environment](reference/configuration.md). +- [LIMITATIONS.md](../LIMITATIONS.md) +- [Threat model](reference/threat-model.md) +- [Evidence integrity spec](reference/evidence-integrity-spec.md) +- [Sample auditor pack](../examples/auditor-pack/README.md) --- -## Documentation index +## More -All user-facing docs are listed by type (Tutorial, How-to, Reference, Explanation) in the **[documentation index](README.md)**. The project follows the [Diátaxis](https://diataxis.fr/) framework so you can find learning-oriented, task-oriented, or reference material quickly. +- [Documentation index](README.md) +- [Roadmap & focus](../ROADMAP.md) diff --git a/docs/README.md b/docs/README.md index ebc7e1d1..8aa436c5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,17 +18,38 @@ This documentation is organised around the [Diátaxis](https://diataxis.fr/) fra - [60-Second Demo](tutorials/quickstart-demo.md) — Docker Compose demo: `docker compose up`, send a curl request, see evidence immediately. - [QUICKSTART.md](QUICKSTART.md) — Short entry point for native Talon (requires Go). +## New here? + +Pick the path that matches your goal. For what Talon is **not** building, read [Roadmap & focus](../ROADMAP.md) early. + +### Evaluator (no API key, ~15 minutes) + +1. [60-second demo](tutorials/quickstart-demo.md) — Docker Compose, mock provider, evidence in SQLite. +2. [Evidence integrity 5-minute proof](tutorials/evidence-integrity-demo.md) — verify, export, tamper, fail verification. +3. [Sample auditor pack](../examples/auditor-pack/README.md) — browse a signed export + compliance report; regenerate with `make auditor-pack`. + +### Builder (native binary, cold start) + +1. [Install matrix](../README.md#install) — `make install` or `go install` on macOS/arm64; linux/amd64 release tarball or install script. +2. Set `TALON_SECRETS_KEY`, then `talon init --scaffold --name my-agent`, then `talon run --dry-run "hello"`. +3. [Your first governed agent](tutorials/first-governed-agent.md) — live LLM run and audit trail. + +Smoke-check the builder path: `make verify-newcomer` (from repo root). + ## Start Here (jobs-to-be-done) Choose the shortest path for your situation: -1. **"I already have an app calling OpenAI/Anthropic and want controls fast."** +1. **"I need to show an auditor or DPO what we can hand off."** + - Start: [Sample auditor pack](../examples/auditor-pack/README.md) + - Then: [How to export evidence for auditors](guides/compliance-export-runbook.md) +2. **"I already have an app calling OpenAI/Anthropic and want controls fast."** - Start: [Add Talon to your existing app](guides/add-talon-to-existing-app.md) - Then: [How to export evidence for auditors](guides/compliance-export-runbook.md) -2. **"I need to govern a third-party AI vendor."** +3. **"I need to govern a third-party AI vendor."** - Start: [Vendor integration guide](VENDOR_INTEGRATION_GUIDE.md) - Then: [Architecture: MCP proxy](ARCHITECTURE_MCP_PROXY.md) -3. **"I want to understand exactly what Talon enforces."** +4. **"I want to understand exactly what Talon enforces."** - Start: [What Talon does to your request](explanation/what-talon-does-to-your-request.md) - Then: [Why not just a PII proxy?](explanation/why-not-a-pii-proxy.md) @@ -102,6 +123,7 @@ Choose the shortest path for your situation: | [Conformance suite & count](reference/conformance.md) | Reproducible passing-test count for the evidence + policy paths (`make conformance`). | | [Reproducible benchmarks](reference/benchmarks.md) | `make benchmarks` — gateway overhead, PII scan, evidence write on your hardware. | | [Roadmap & focus](../ROADMAP.md) | Anti-goals and focus — answers "are you trying to be Portkey + AGT?" | +| [Sample auditor pack](../examples/auditor-pack/README.md) | Generated signed export + compliance report for handoff review. | | [Evidence integrity 5-minute proof](tutorials/evidence-integrity-demo.md) | Fast proof moment for auditors/operators, including offline signed-export verification. | | [Threat model](reference/threat-model.md) | Attack surface, trust boundaries, and what the HMAC signature does and does not prove. | | [Security policy](../SECURITY.md) | Vulnerability reporting process and security scope. | diff --git a/docs/contributor/mission-control-screenshots.md b/docs/contributor/mission-control-screenshots.md new file mode 100644 index 00000000..bce0f6c6 --- /dev/null +++ b/docs/contributor/mission-control-screenshots.md @@ -0,0 +1,61 @@ +# Mission Control screenshot refresh + +Maintainer workflow for updating dashboard screenshots used in README, release notes, and docs. + +## When to refresh + +- Governance or Gateway Mission Control layout changes ([`web/dashboard.html`](../../web/dashboard.html), [`web/gateway_dashboard.html`](../../web/gateway_dashboard.html)) +- New evidence verify UX, compliance preview, or gateway widgets +- Release notes that promise a visual share artifact (see [CONTRIBUTING.md](../../CONTRIBUTING.md)) + +## Seed realistic data + +```bash +cd examples/docker-compose +docker compose up -d --build +bash ../../scripts/demo-recorder.sh http://localhost:8080 +``` + +This creates ~10 evidence rows (clean requests, PII variants, multiple models) suitable for screenshots. + +## URLs to capture + +| Surface | URL | What to show | +|---------|-----|----------------| +| Governance Mission Control | `http://localhost:8080/dashboard` | Evidence tab, verify action, signature/trust block | +| Gateway Mission Control | Gateway dashboard route (see [gateway dashboard reference](../reference/gateway-dashboard.md)) | Posture, interventions, PII/cost signals | + +Open the **Evidence** tab first — that is the primary proof-bar surface for epic credibility work. + +## Suggested captures + +1. Evidence list with mixed allow/deny and PII flags visible. +2. Single record detail with **Verified** integrity state. +3. Compliance report preview panel (if enabled in your build). +4. Gateway overview with at least one blocked or PII-detected row (optional). + +## Where to store assets + +Prefer **`docs/images/mission-control/`** for README and tutorial embeds: + +``` +docs/images/mission-control/ + evidence-list.png + evidence-detail-verified.png + gateway-overview.png +``` + +Keep filenames stable; update references in docs when replacing files. + +Optional: copy highlights into `examples/auditor-pack/screenshots/` only when they illustrate auditor handoff (not required for `make auditor-pack`). + +## After capture + +1. Link new images from [README.md](../../README.md) or release notes as needed. +2. Run `bash scripts/check-claim-discipline.sh` if doc text changed. +3. Note the refresh in CHANGELOG under the release that ships the UX change. + +## Related scripts + +- [`scripts/demo-recorder.sh`](../../scripts/demo-recorder.sh) — seed evidence +- [`scripts/generate-auditor-pack.sh`](../../scripts/generate-auditor-pack.sh) — export sample auditor pack (optional companion to screenshots) diff --git a/examples/auditor-pack/README.md b/examples/auditor-pack/README.md new file mode 100644 index 00000000..627fb46e --- /dev/null +++ b/examples/auditor-pack/README.md @@ -0,0 +1,45 @@ +# Sample auditor pack + +This folder contains a **generated sample** of what you might hand to a DPO, customer security reviewer, or internal audit — produced from the no-API-key [docker-compose demo](../docker-compose/README.md). + +It is **supporting controls and evidence** for review, not a completed legal filing or certification. See [LIMITATIONS.md](../../LIMITATIONS.md) and [ROADMAP.md](../../ROADMAP.md). + +## Contents + +| File | Purpose | +|------|---------| +| [manifest.json](manifest.json) | Generation metadata, verify commands, record count | +| [evidence.signed.json](evidence.signed.json) | Full HMAC-signed evidence records (offline verification) | +| [compliance-report.html](compliance-report.html) | Framework-mapped control summary (HTML) | +| [compliance-report.json](compliance-report.json) | Same report as JSON | + +## Verify offline + +From a machine with the `talon` CLI and the same signing key context as the demo (or verify signature structure only): + +```bash +talon audit verify --file examples/auditor-pack/evidence.signed.json +``` + +For a live regeneration path, see [Evidence integrity 5-minute proof](../../docs/tutorials/evidence-integrity-demo.md). + +## Regenerate + +Requires Docker. From the repo root: + +```bash +make auditor-pack +# or: scripts/generate-auditor-pack.sh +``` + +When Docker is available, the script starts `examples/docker-compose`, runs [demo-recorder.sh](../../scripts/demo-recorder.sh) to seed ~10 requests, then exports from the running container. + +When Docker is not available, `make auditor-pack` falls back to [auditorpackgen](../../scripts/auditorpackgen/main.go) (synthetic demo records with a fixed test signing key — see `manifest.json`). + +Commit updated artifacts when the evidence schema or compliance mapping changes. + +## Related docs + +- [How to export evidence for auditors](../../docs/guides/compliance-export-runbook.md) +- [Evidence integrity specification](../../docs/reference/evidence-integrity-spec.md) +- [Conformance suite & count](../../docs/reference/conformance.md) diff --git a/examples/auditor-pack/compliance-report.html b/examples/auditor-pack/compliance-report.html new file mode 100644 index 00000000..559e3aa9 --- /dev/null +++ b/examples/auditor-pack/compliance-report.html @@ -0,0 +1,32 @@ + +Talon Compliance Report + +

Talon Compliance Report

+

Generated: 2026-06-03 13:21:11.139991 +0000 UTC | Framework: all | Tenant: default | Agent: all

+
+
Evidence Records
3
+
Policy Denials
1
+
PII Records
2
+
Total Cost (EUR)
0.0050
+
+

Control Mappings

+ + +
FrameworkArticleControlSource
gdprArt. 30Processing records via signed evidence exportinternal/evidence/store.go
gdprArt. 32PII detection and model/data-tier routinginternal/classifier/pii.go
gdprArt. 44-50EU data residency routing controlsinternal/policy/rego/routing.rego
eu-ai-actArt. 11Technical documentation through execution plans and evidenceinternal/agent/plan.go
eu-ai-actArt. 13Transparency via execution/model/cost traceabilityinternal/evidence/store.go
eu-ai-actArt. 14Human oversight via plan review gateinternal/agent/plan_review.go
nis2Art. 21Risk controls, policy enforcement, and monitoringinternal/policy/engine.go
doraArt. 11ICT incident traceability with signed audit trailinternal/evidence/store.go
iso-27001A.8.15Cryptographic integrity of logs (HMAC)internal/evidence/signature.go
iso-27001A.8.16Monitoring and governance metricsinternal/gateway/metrics.go
+

Sample Evidence IDs

+ + +
ID
req_5f880438
req_a928db90
req_b0a4da50
+ \ No newline at end of file diff --git a/examples/auditor-pack/compliance-report.json b/examples/auditor-pack/compliance-report.json new file mode 100644 index 00000000..092d0f4e --- /dev/null +++ b/examples/auditor-pack/compliance-report.json @@ -0,0 +1,76 @@ +{ + "generated_at": "2026-06-03T13:21:11.139991Z", + "framework": "", + "tenant_id": "default", + "evidence_count": 3, + "denied_count": 1, + "pii_record_count": 2, + "total_cost_eur": 0.005, + "mappings": [ + { + "framework": "gdpr", + "article": "Art. 30", + "control": "Processing records via signed evidence export", + "source": "internal/evidence/store.go" + }, + { + "framework": "gdpr", + "article": "Art. 32", + "control": "PII detection and model/data-tier routing", + "source": "internal/classifier/pii.go" + }, + { + "framework": "gdpr", + "article": "Art. 44-50", + "control": "EU data residency routing controls", + "source": "internal/policy/rego/routing.rego" + }, + { + "framework": "eu-ai-act", + "article": "Art. 11", + "control": "Technical documentation through execution plans and evidence", + "source": "internal/agent/plan.go" + }, + { + "framework": "eu-ai-act", + "article": "Art. 13", + "control": "Transparency via execution/model/cost traceability", + "source": "internal/evidence/store.go" + }, + { + "framework": "eu-ai-act", + "article": "Art. 14", + "control": "Human oversight via plan review gate", + "source": "internal/agent/plan_review.go" + }, + { + "framework": "nis2", + "article": "Art. 21", + "control": "Risk controls, policy enforcement, and monitoring", + "source": "internal/policy/engine.go" + }, + { + "framework": "dora", + "article": "Art. 11", + "control": "ICT incident traceability with signed audit trail", + "source": "internal/evidence/store.go" + }, + { + "framework": "iso-27001", + "article": "A.8.15", + "control": "Cryptographic integrity of logs (HMAC)", + "source": "internal/evidence/signature.go" + }, + { + "framework": "iso-27001", + "article": "A.8.16", + "control": "Monitoring and governance metrics", + "source": "internal/gateway/metrics.go" + } + ], + "sample_evidence_ids": [ + "req_5f880438", + "req_a928db90", + "req_b0a4da50" + ] +} \ No newline at end of file diff --git a/examples/auditor-pack/evidence.signed.json b/examples/auditor-pack/evidence.signed.json new file mode 100644 index 00000000..ccb38d25 --- /dev/null +++ b/examples/auditor-pack/evidence.signed.json @@ -0,0 +1,159 @@ +{ + "export_metadata": { + "generated_at": "2026-06-03T13:21:11.139729Z", + "talon_version": "auditorpackgen", + "filter": {}, + "total_records": 3, + "algorithm": "HMAC-SHA256", + "signed": true + }, + "records": [ + { + "id": "req_a928db90", + "correlation_id": "corr_demo_eu_summary", + "timestamp": "2026-06-03T15:21:11.137877+02:00", + "tenant_id": "default", + "agent_id": "gateway", + "invocation_type": "gateway", + "policy_decision": { + "allowed": true, + "action": "allow", + "reasons": [ + "within budget" + ], + "policy_version": "" + }, + "classification": { + "input_tier": 0, + "output_tier": 0, + "pii_redacted": false + }, + "execution": { + "model_used": "", + "cost": 0.003, + "tokens": { + "input": 0, + "output": 0 + }, + "duration_ms": 0 + }, + "audit_trail": { + "input_hash": "sha256:21b3788008cff76e874a64a267f53f846e876bc831d93f33b3529971536aa12e", + "output_hash": "sha256:75e422c843b5e225270f6cdecab7cdf80d487b332067a404ca068b796f9c0bcb" + }, + "compliance": { + "frameworks": null, + "data_location": "" + }, + "signature": "hmac-sha256:043254d4275d8968df420839b07435fd082a41da21a0b3a409924a868537e586", + "explanations": [ + { + "code": "POLICY_DENIED_COST", + "decision": "allow", + "stage": "policy_evaluation", + "reason": "Request blocked by cost policy limits.", + "trigger": "within budget", + "fix": "Reduce expected token usage or increase cost limits in policy." + } + ] + }, + { + "id": "req_b0a4da50", + "correlation_id": "corr_demo_pii_email", + "timestamp": "2026-06-03T15:21:11.138907+02:00", + "tenant_id": "default", + "agent_id": "gateway", + "invocation_type": "gateway", + "policy_decision": { + "allowed": true, + "action": "allow", + "policy_version": "" + }, + "classification": { + "input_tier": 1, + "output_tier": 0, + "pii_detected": [ + "email" + ], + "pii_redacted": false + }, + "execution": { + "model_used": "", + "cost": 0.002, + "tokens": { + "input": 0, + "output": 0 + }, + "duration_ms": 0 + }, + "audit_trail": { + "input_hash": "sha256:f0932e1c1ab84f677795b118a9350badc5890282c03c64b473149eb49573cda3", + "output_hash": "sha256:e3cf2a8b2aefa8bf452c15fe7854839f45c81c796f5431dbe7121ce884069aa7" + }, + "compliance": { + "frameworks": null, + "data_location": "" + }, + "signature": "hmac-sha256:345748e1593726870f78e87c86890404255c7084a4acb665828a3061b0f67387", + "explanations": [ + { + "code": "POLICY_ALLOWED", + "decision": "allow", + "stage": "policy_evaluation", + "reason": "Request allowed by policy." + } + ] + }, + { + "id": "req_5f880438", + "correlation_id": "corr_demo_pii_iban", + "timestamp": "2026-06-03T15:21:11.13933+02:00", + "tenant_id": "default", + "agent_id": "gateway", + "invocation_type": "gateway", + "policy_decision": { + "allowed": false, + "action": "deny", + "reasons": [ + "pii policy" + ], + "policy_version": "" + }, + "classification": { + "input_tier": 2, + "output_tier": 0, + "pii_detected": [ + "iban" + ], + "pii_redacted": false + }, + "execution": { + "model_used": "", + "cost": 0, + "tokens": { + "input": 0, + "output": 0 + }, + "duration_ms": 0 + }, + "audit_trail": { + "input_hash": "sha256:13ca9c1bc7adcbdecd82ad54428b4acdab3c5849510617f6ee10357e1d5311a5", + "output_hash": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "compliance": { + "frameworks": null, + "data_location": "" + }, + "signature": "hmac-sha256:f1b69cb81e0f97d88bd355c1f4a29fbbb0f2cc77d5d25f904a027f67a45bb614", + "explanations": [ + { + "code": "LEGACY_REASON_UNMIGRATED", + "decision": "deny", + "stage": "policy_evaluation", + "reason": "Request processed with legacy policy explanation mapping.", + "trigger": "pii policy" + } + ] + } + ] +} diff --git a/examples/auditor-pack/manifest.json b/examples/auditor-pack/manifest.json new file mode 100644 index 00000000..c6bc544f --- /dev/null +++ b/examples/auditor-pack/manifest.json @@ -0,0 +1,15 @@ +{ + "claim_note": "Supporting controls and evidence for auditor review — not a completed legal filing. See LIMITATIONS.md.", + "files": { + "compliance_report_html": "compliance-report.html", + "compliance_report_json": "compliance-report.json", + "evidence_signed": "evidence.signed.json" + }, + "generated_at": "2026-06-03T13:21:11Z", + "offline_signing_key_note": "Offline pack uses a fixed demo key; docker-compose regeneration uses the stack vault key.", + "record_count_estimate": 3, + "source": "scripts/auditorpackgen (offline; no docker-compose)", + "verify_commands": [ + "TALON_SIGNING_KEY=01234567890123456789012345678901 talon audit verify --file examples/auditor-pack/evidence.signed.json" + ] +} \ No newline at end of file diff --git a/scripts/auditorpackgen/main.go b/scripts/auditorpackgen/main.go new file mode 100644 index 00000000..4e6305e9 --- /dev/null +++ b/scripts/auditorpackgen/main.go @@ -0,0 +1,145 @@ +// Command auditorpackgen writes examples/auditor-pack artifacts without Docker. +// Invoked by scripts/generate-auditor-pack.sh when the demo stack is unavailable. +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + "time" + + "github.com/dativo-io/talon/internal/compliance" + "github.com/dativo-io/talon/internal/evidence" +) + +const testSigningKey = "01234567890123456789012345678901" + +func main() { + outDir := flag.String("out", "examples/auditor-pack", "output directory") + flag.Parse() + + if err := os.MkdirAll(*outDir, 0o755); err != nil { + fatal("mkdir out: %v", err) + } + + dir, err := os.MkdirTemp("", "talon-auditor-pack-*") + if err != nil { + fatal("temp dir: %v", err) + } + defer os.RemoveAll(dir) + + store, err := evidence.NewStore(filepath.Join(dir, "evidence.db"), testSigningKey) + if err != nil { + fatal("evidence store: %v", err) + } + defer store.Close() + + gen := evidence.NewGenerator(store) + ctx := context.Background() + + scenarios := []evidence.GenerateParams{ + { + CorrelationID: "corr_demo_eu_summary", TenantID: "default", AgentID: "gateway", + InvocationType: "gateway", + PolicyDecision: evidence.PolicyDecision{Allowed: true, Action: "allow", Reasons: []string{"within budget"}}, + InputPrompt: "What are the key trends in European AI regulation?", + OutputResponse: "Summary of EU AI Act and GDPR interplay for deployers.", + Cost: 0.003, + }, + { + CorrelationID: "corr_demo_pii_email", TenantID: "default", AgentID: "gateway", + InvocationType: "gateway", + PolicyDecision: evidence.PolicyDecision{Allowed: true, Action: "allow"}, + Classification: evidence.Classification{PIIDetected: []string{"email"}, InputTier: 1}, + InputPrompt: "My email is jan@example.com, help me reset my password", + OutputResponse: "Password reset steps (synthetic demo).", + Cost: 0.002, + }, + { + CorrelationID: "corr_demo_pii_iban", TenantID: "default", AgentID: "gateway", + InvocationType: "gateway", + PolicyDecision: evidence.PolicyDecision{Allowed: false, Action: "deny", Reasons: []string{"pii policy"}}, + Classification: evidence.Classification{PIIDetected: []string{"iban"}, InputTier: 2}, + InputPrompt: "Process payment to IBAN DE89370400440532013000", + OutputResponse: "", + Cost: 0, + }, + } + + var records []evidence.Evidence + for _, p := range scenarios { + ev, err := gen.Generate(ctx, p) + if err != nil { + fatal("generate: %v", err) + } + records = append(records, *ev) + } + + envelope := evidence.SignedExportEnvelope{ + ExportMetadata: evidence.ExportMetadata{ + GeneratedAt: time.Now().UTC(), + TalonVersion: "auditorpackgen", + TotalRecords: len(records), + Algorithm: evidence.SignedExportAlgorithm, + Signed: true, + }, + Records: records, + } + evPath := filepath.Join(*outDir, "evidence.signed.json") + f, err := os.Create(evPath) + if err != nil { + fatal("create evidence: %v", err) + } + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + if err := enc.Encode(envelope); err != nil { + fatal("encode evidence: %v", err) + } + f.Close() + + report := compliance.BuildReport("", "default", "", "", "", records) + html, err := compliance.RenderHTML(report) + if err != nil { + fatal("html report: %v", err) + } + if err := os.WriteFile(filepath.Join(*outDir, "compliance-report.html"), html, 0o600); err != nil { + fatal("write html: %v", err) + } + jsonReport, err := compliance.RenderJSON(report) + if err != nil { + fatal("json report: %v", err) + } + if err := os.WriteFile(filepath.Join(*outDir, "compliance-report.json"), jsonReport, 0o600); err != nil { + fatal("write json: %v", err) + } + + manifest := map[string]interface{}{ + "generated_at": time.Now().UTC().Format(time.RFC3339), + "source": "scripts/auditorpackgen (offline; no docker-compose)", + "record_count_estimate": len(records), + "files": map[string]string{ + "evidence_signed": "evidence.signed.json", + "compliance_report_html": "compliance-report.html", + "compliance_report_json": "compliance-report.json", + }, + "verify_commands": []string{ + "TALON_SIGNING_KEY=" + testSigningKey + " talon audit verify --file examples/auditor-pack/evidence.signed.json", + }, + "claim_note": "Supporting controls and evidence for auditor review — not a completed legal filing. See LIMITATIONS.md.", + "offline_signing_key_note": "Offline pack uses a fixed demo key; docker-compose regeneration uses the stack vault key.", + } + mb, _ := json.MarshalIndent(manifest, "", " ") + if err := os.WriteFile(filepath.Join(*outDir, "manifest.json"), mb, 0o644); err != nil { + fatal("manifest: %v", err) + } + + fmt.Printf("Wrote auditor pack to %s (%d records)\n", *outDir, len(records)) +} + +func fatal(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, "auditorpackgen: "+format+"\n", args...) + os.Exit(1) +} diff --git a/scripts/generate-auditor-pack.sh b/scripts/generate-auditor-pack.sh new file mode 100755 index 00000000..b166146a --- /dev/null +++ b/scripts/generate-auditor-pack.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash +# +# generate-auditor-pack.sh — reproducible sample auditor handoff from the docker-compose demo. +# +# Produces examples/auditor-pack/ with signed evidence export, compliance report, and manifest. +# Requires: Docker, docker compose, curl, and a running build of talon in the demo image (compose build). +# +# Usage (from repo root): +# scripts/generate-auditor-pack.sh +# scripts/generate-auditor-pack.sh --keep-up # leave compose stack running +# +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +COMPOSE_DIR="${REPO_ROOT}/examples/docker-compose" +OUTPUT_DIR="${REPO_ROOT}/examples/auditor-pack" +GATEWAY="http://localhost:8080" +KEEP_UP=false + +for arg in "$@"; do + case "$arg" in + --keep-up) KEEP_UP=true ;; + -h|--help) + echo "Usage: scripts/generate-auditor-pack.sh [--keep-up]" + exit 0 + ;; + *) + echo "Unknown argument: $arg" >&2 + exit 2 + ;; + esac +done + +generate_offline() { + echo "==> Docker unavailable; generating offline sample pack (auditorpackgen)..." + mkdir -p "$OUTPUT_DIR" + (cd "$REPO_ROOT" && go run ./scripts/auditorpackgen/main.go -out "$OUTPUT_DIR") + echo "Auditor pack written to ${OUTPUT_DIR}/ (offline fixture)" +} + +if ! command -v docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1; then + generate_offline + exit 0 +fi + +cd "$COMPOSE_DIR" + +echo "==> Building and starting docker-compose demo stack..." +if ! docker compose up -d --build; then + cd "$REPO_ROOT" + generate_offline + exit 0 +fi + +echo "==> Waiting for Talon health..." +for i in $(seq 1 60); do + if curl -fsS "${GATEWAY}/health" >/dev/null 2>&1; then + break + fi + if [ "$i" -eq 60 ]; then + echo "Error: Talon did not become healthy at ${GATEWAY}/health" >&2 + docker compose logs talon --tail 30 >&2 || true + exit 1 + fi + sleep 2 +done + +echo "==> Seeding evidence (demo-recorder)..." +bash "${REPO_ROOT}/scripts/demo-recorder.sh" "$GATEWAY" + +mkdir -p "$OUTPUT_DIR" + +echo "==> Exporting signed evidence..." +docker compose exec -T talon /usr/local/bin/talon audit export \ + --format signed-json \ + --limit 500 >"${OUTPUT_DIR}/evidence.signed.json" + +echo "==> Generating compliance report (HTML)..." +docker compose exec -T talon /usr/local/bin/talon compliance report \ + --format html >"${OUTPUT_DIR}/compliance-report.html" + +echo "==> Generating compliance report (JSON)..." +docker compose exec -T talon /usr/local/bin/talon compliance report \ + --format json >"${OUTPUT_DIR}/compliance-report.json" + +# Basic secret-leak guard (demo uses synthetic PII only). +if grep -qiE 'sk-[a-zA-Z0-9]{20,}|Bearer[[:space:]]+[a-zA-Z0-9._-]{20,}' \ + "${OUTPUT_DIR}/evidence.signed.json" "${OUTPUT_DIR}/compliance-report.html" 2>/dev/null; then + echo "Error: export may contain API key material; aborting." >&2 + exit 1 +fi + +RECORD_COUNT=0 +if command -v jq >/dev/null 2>&1; then + RECORD_COUNT=$(jq '(.records // .) | if type == "array" then length else 0 end' \ + "${OUTPUT_DIR}/evidence.signed.json" 2>/dev/null || echo 0) +fi + +TALON_VERSION=$(docker compose exec -T talon /usr/local/bin/talon version 2>/dev/null | head -1 | tr -d '\r' || echo "unknown") +GENERATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +GIT_COMMIT=$(git -C "$REPO_ROOT" rev-parse --short HEAD 2>/dev/null || echo "unknown") + +cat >"${OUTPUT_DIR}/manifest.json" < Stopping docker-compose stack..." + docker compose down +fi + +echo "" +echo "Auditor pack written to ${OUTPUT_DIR}/" +echo " evidence.signed.json" +echo " compliance-report.html" +echo " compliance-report.json" +echo " manifest.json" +echo "" +echo "Regenerate anytime: make auditor-pack" diff --git a/scripts/verify-newcomer-path.sh b/scripts/verify-newcomer-path.sh new file mode 100755 index 00000000..6b211a96 --- /dev/null +++ b/scripts/verify-newcomer-path.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# +# verify-newcomer-path.sh — smoke-test the documented cold-start builder path. +# +# Validates: talon on PATH, init --scaffold, run --dry-run (no LLM key). +# Run from repo root: make verify-newcomer +# +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +WORK_DIR=$(mktemp -d) +trap 'rm -rf "$WORK_DIR"' EXIT + +export TALON_SECRETS_KEY="${TALON_SECRETS_KEY:-$(openssl rand -hex 32)}" +export TALON_DATA_DIR="${WORK_DIR}/.talon" + +if ! command -v talon >/dev/null 2>&1; then + if [ -x "${REPO_ROOT}/bin/talon" ]; then + export PATH="${REPO_ROOT}/bin:${PATH}" + elif command -v go >/dev/null 2>&1; then + echo "==> talon not on PATH; building via make install..." + if [ "$(uname -s)" = "Darwin" ]; then + make -C "$REPO_ROOT" install + else + make -C "$REPO_ROOT" install + fi + export PATH="$(go env GOPATH)/bin:${PATH}" + else + echo "Error: talon not found and go not available to build." >&2 + exit 1 + fi +fi + +echo "==> talon version" +talon version | head -3 + +echo "==> talon init --scaffold" +cd "$WORK_DIR" +talon init --scaffold --name newcomer-smoke >/dev/null + +test -f agent.talon.yaml || { echo "missing agent.talon.yaml" >&2; exit 1; } +test -f talon.config.yaml || { echo "missing talon.config.yaml" >&2; exit 1; } + +echo "==> talon run --dry-run" +out=$(talon run --dry-run "hello newcomer smoke" 2>&1) +echo "$out" | tail -5 +echo "$out" | grep -qi "ALLOWED" || { echo "expected dry-run ALLOWED" >&2; exit 1; } + +echo "" +echo "verify-newcomer: OK (init + dry-run on clean dir)"