diff --git a/.env.example b/.env.example index 33bc0f6..762acd3 100644 --- a/.env.example +++ b/.env.example @@ -47,6 +47,19 @@ WOODPECKER_DB_USER=woodpecker # [CONFIG] Postgres user WOODPECKER_DB_HOST=127.0.0.1 # [CONFIG] Postgres host WOODPECKER_DB_NAME=woodpecker # [CONFIG] Postgres database name +# ── Vault-only secrets (DO NOT put these in .env) ──────────────────────── +# These tokens grant access to external systems (GitHub, ClawHub, deploy targets). +# They live ONLY in .env.vault.enc and are injected into the ephemeral vault-runner +# container at fire time (#745). lib/env.sh explicitly unsets them so agents +# can never hold them directly — all external actions go through vault dispatch. +# +# GITHUB_TOKEN — GitHub API access (publish, deploy, post) +# CLAWHUB_TOKEN — ClawHub registry credentials (publish) +# (deploy keys) — SSH keys for deployment targets +# +# To manage vault secrets: disinto secrets edit-vault +# See also: vault/vault-run-action.sh, vault/vault-fire.sh + # ── Project-specific secrets ────────────────────────────────────────────── # Store all project secrets here so formulas reference env vars, never hardcode. BASE_RPC_URL= # [SECRET] on-chain RPC endpoint diff --git a/AGENTS.md b/AGENTS.md index 00cb56e..3e3b43d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -152,6 +152,7 @@ Humans write these. Agents read and enforce them. | AD-003 | The runtime creates and destroys, the formula preserves. | Runtime manages worktrees/sessions/temp. Formulas commit knowledge to git before signaling done. | | AD-004 | Event-driven > polling > fixed delays. | Never `waitForTimeout` or hardcoded sleep. Use phase files, webhooks, or poll loops with backoff. | | AD-005 | Secrets via env var indirection, never in issue bodies. | Issue bodies become code. Agent secrets go in `.env.enc`, vault secrets in `.env.vault.enc` (both SOPS-encrypted). Referenced as `$VAR_NAME`. Vault-runner gets only vault secrets; agents get only agent secrets. | +| AD-006 | External actions go through vault dispatch, never direct. | Agents build addressables; only the vault exercises them (publishes, deploys, posts). Tokens for external systems (`GITHUB_TOKEN`, `CLAWHUB_TOKEN`, deploy keys) live only in `.env.vault.enc` and are injected into the ephemeral vault-runner container. `lib/env.sh` unsets them so agents never hold them. PRs with direct external actions without vault dispatch get REQUEST_CHANGES. | **Who enforces what:** - **Gardener** checks open backlog issues against ADs during grooming; closes violations with a comment referencing the AD number. diff --git a/bin/disinto b/bin/disinto index 7c7b1d3..18d7f5d 100755 --- a/bin/disinto +++ b/bin/disinto @@ -233,6 +233,10 @@ services: DISINTO_CONTAINER: "1" env_file: - .env + # IMPORTANT: agents get .env only (forge tokens, CI tokens, config). + # Vault-only secrets (GITHUB_TOKEN, CLAWHUB_TOKEN, deploy keys) live in + # .env.vault.enc and are NEVER injected here — only the vault-runner + # container receives them at fire time (AD-006, #745). depends_on: - forgejo - woodpecker diff --git a/formulas/review-pr.toml b/formulas/review-pr.toml index eaec1bc..47427bb 100644 --- a/formulas/review-pr.toml +++ b/formulas/review-pr.toml @@ -107,7 +107,40 @@ already contains a similar item (same resource, same ask). List the vault directories to inspect existing items. If a duplicate or near-duplicate exists, REQUEST_CHANGES and reference the existing item. -## 5. Re-review (if previous review is provided) +## 5. External action detection (token separation) + +Agents must NEVER execute external actions directly. Any action that touches +an external system (publish, deploy, post, push to external registry, API +calls to third-party services) MUST go through vault dispatch — i.e., the +agent files a vault item (`vault/pending/*.json`) and the vault-runner +container executes it with injected secrets. + +Scan the diff for these patterns: + +- **Direct publish commands**: `clawhub publish`, `npm publish`, + `cargo publish`, `docker push`, `gh release create`, or similar +- **Direct deploy commands**: `ssh ... deploy`, `rsync` to external hosts, + `kubectl apply`, `helm install`, cloud CLI deploy commands +- **Direct external API calls**: `curl`/`wget` to external services with + auth tokens, posting to social media APIs, sending emails +- **Token usage**: Direct references to `GITHUB_TOKEN`, `CLAWHUB_TOKEN`, + or other vault-only secrets in agent code (outside of vault/ directory) + +If ANY of these patterns appear in agent code (scripts in `dev/`, `action/`, +`planner/`, `gardener/`, `supervisor/`, `predictor/`, `review/`, `formulas/`, +`lib/`) WITHOUT routing through vault dispatch (`vault/pending/`, `vault-fire.sh`, +`vault-run-action.sh`), **REQUEST_CHANGES**. + +Explain that external actions must use vault dispatch per AD-006. The agent +should file a vault item instead of executing directly. + +**Exceptions** (do NOT flag these): +- Code inside `vault/` — the vault system itself is allowed to handle secrets +- References in comments or documentation explaining the architecture +- `bin/disinto` setup commands that manage `.env.vault.enc` +- Local operations (git push to forge, forge API calls with `FORGE_TOKEN`) + +## 6. Re-review (if previous review is provided) If the orchestrator injected a previous review and incremental diff: 1. For each finding in the previous review, check if it was addressed @@ -118,7 +151,7 @@ If the orchestrator injected a previous review and incremental diff: Focus on the incremental diff for finding-by-finding status, but use the full diff to check overall correctness. -## 6. Follow-up issues (pre-existing tech debt) +## 7. Follow-up issues (pre-existing tech debt) If you discover pre-existing issues (NOT introduced by this PR), create tech-debt issues via API so they are tracked separately: @@ -148,7 +181,7 @@ tech-debt issues via API so they are tracked separately: Only create follow-ups for clear, actionable tech debt. Do not create issues for minor style nits or speculative improvements. -## 7. Verdict +## 8. Verdict Choose one: - **APPROVE**: Change is correct, complete, and follows conventions @@ -159,7 +192,7 @@ Bias toward APPROVE for small, correct changes. Use REQUEST_CHANGES only for actual problems (bugs, security issues, broken functionality, missing required behavior). Use DISCUSS sparingly. -## 8. Output +## 9. Output Write a single JSON object to the file path from REVIEW_OUTPUT_FILE. Use jq to ensure proper JSON escaping of the markdown content: diff --git a/lib/env.sh b/lib/env.sh index 947c1af..ef602dd 100755 --- a/lib/env.sh +++ b/lib/env.sh @@ -90,6 +90,13 @@ export WOODPECKER_REPO_ID="${WOODPECKER_REPO_ID:-}" export WOODPECKER_SERVER="${WOODPECKER_SERVER:-http://localhost:8000}" export CLAUDE_TIMEOUT="${CLAUDE_TIMEOUT:-7200}" +# Vault-only token guard (#745): external-action tokens (GITHUB_TOKEN, CLAWHUB_TOKEN) +# must NEVER be available to agents. They live in .env.vault.enc and are injected +# only into the ephemeral vault-runner container at fire time. Unset them here so +# even an accidental .env inclusion cannot leak them into agent sessions. +unset GITHUB_TOKEN 2>/dev/null || true +unset CLAWHUB_TOKEN 2>/dev/null || true + # Disable Claude Code auto-updater, telemetry, error reporting in factory sessions. # Factory processes must never phone home or auto-update mid-session (#725). export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1