# Policy Cookbook ## 0. Start From a Template `tirith policy init --template ` writes a curated, well-commented, schema-valid starter policy. It is the fastest way to a sensible baseline you can then edit: ```bash tirith policy init --template individual # solo developer defaults tirith policy init --template ci-strict # fail-closed CI, no bypass tirith policy init --template ai-agent-heavy # heavy AI-agent environments ``` - **`individual`** — `fail_mode: open`, `paranoia: 1`, the noisy `shortened_url` rule escalated, an empty `allowlist` ready to fill in. - **`ci-strict`** — `fail_mode: closed`, the `TIRITH=0` bypass disabled (interactive and non-interactive), `strict_warn: true`, remote-execution rules escalated to CRITICAL, and `scan.fail_on: high` so `tirith scan` fails the build. - **`ai-agent-heavy`** — `fail_mode: open` (so an internal error cannot wedge an agent), `paranoia: 3`, the non-interactive bypass disabled, `approval_rules` for the highest-risk pipe-to-shell rules, and `escalation` rules that block on repeated warnings. `tirith policy init` with no `--template` writes the full default policy. The recipes below show hand-tuned variations on these baselines. ## 0b. Tune From Your Audit Log Once tirith has some history, `tirith policy tune --from-audit` reads your audit log and *suggests* conservative policy adjustments: ```bash tirith policy tune --from-audit tirith policy tune --from-audit --format json # machine-readable ``` It is **suggest-only** — it never edits your policy. The headline suggestion is a rule you allowed or bypassed *every* time and *never* blocked: that rule is probably firing on something you trust, so an `allowlist` entry or a `severity_overrides` downgrade may be warranted. A rule you *sometimes* block on is never suggested for a downgrade — it is doing its job. Every suggestion is a plain count from the log, not an inference; when the log is too small to be meaningful, `policy tune` says so rather than guessing. Review each suggestion, then apply it by hand to `.tirith/policy.yaml`. ## 1. Strict Organization (Fail Closed, No Bypass) ```yaml # .tirith/policy.yaml (repo root) fail_mode: closed allow_bypass_env: false severity_overrides: shortened_url: HIGH plain_http_to_sink: CRITICAL ``` All findings block execution. No bypass mechanism. Shortened URLs and plain HTTP are escalated. ## 2. Personal Developer (Defaults + Allowlist) ```yaml # ~/.config/tirith/policy.yaml fail_mode: open allow_bypass_env: true ``` With allowlist at `~/.config/tirith/allowlist`: ``` raw.githubusercontent.com homebrew.bintray.com get.docker.com ``` Default severity mappings. Allowlisted URLs skip analysis. ## 3. CI Safe Mode (Non-Interactive, JSON Output) ```bash # In CI pipeline tirith check --non-interactive --format json -- curl https://example.com/setup.sh | bash EXIT=$? if [ $EXIT -eq 1 ]; then echo "BLOCKED by tirith" >&2 exit 1 fi ``` Non-interactive mode never prompts. JSON output for machine parsing. ## 4. Docker-Focused (Escalate Docker Rules) ```yaml # .tirith/policy.yaml severity_overrides: docker_untrusted_registry: CRITICAL docker_tag_latest: HIGH ``` All Docker-related findings are escalated. Other rules use default severity. ## 5. Learning Mode (All Low Severity) ```yaml # ~/.config/tirith/policy.yaml fail_mode: open allow_bypass_env: true severity_overrides: curl_pipe_shell: LOW wget_pipe_shell: LOW pipe_to_interpreter: LOW punycode_domain: LOW confusable_domain: LOW ``` Everything becomes a LOW-severity warning. Nothing blocks. Useful for onboarding. ## 6. cargo-vet (Rust Supply-Chain Audit) tirith detects when `cargo install` or `cargo add` is run in a project that hasn't configured [cargo-vet](https://mozilla.github.io/cargo-vet/). The `vet_not_configured` rule fires at LOW severity by default. To escalate: ```yaml # .tirith/policy.yaml severity_overrides: vet_not_configured: HIGH ``` To suppress it (e.g. for non-Rust repos): ``` # ~/.config/tirith/allowlist # or .tirith/allowlist vet_not_configured ``` ## 7. vet (getvet.sh) — Safe Pipe-to-Shell When tirith blocks a `curl | bash` pattern, the safest alternatives are: ### Ask tirith for the rewrite `tirith check --suggest-safe-command` prints a concrete safer version of the exact command you ran: ```bash tirith check --suggest-safe-command -- 'curl https://example.com/install.sh | bash' # tirith: safer alternative # curl_pipe_shell # try: curl -fsSL -o /tmp/tirith-review.sh https://example.com/install.sh \ # && less /tmp/tirith-review.sh && bash /tmp/tirith-review.sh ``` It also drops insecure-TLS flags and upgrades `http://` to `https://`. For findings with no safe mechanical rewrite it says so plainly instead of guessing. The flag is advisory — it never changes the verdict or exit code. Use `tirith explain --rule curl_pipe_shell --fix` to see a rule's remediation on its own. ### Using tirith run (built-in, Unix only) `tirith run` downloads, inspects, and prompts before executing: ```bash # Instead of: curl -fsSL https://example.com/install.sh | bash tirith run https://example.com/install.sh ``` Download and inspect only (no execution): ```bash tirith run --no-exec https://example.com/install.sh ``` Pin to a known hash: ```bash tirith run --sha256 abc123... https://example.com/install.sh ``` ### Using tirith install (recorded install transaction) `tirith install` wraps a real package install with pre-execution supply-chain risk analysis and records the transaction. It analyzes first, presents a verdict, takes a working-directory checkpoint and an audit entry, then runs the real `npm install` / `pip install` / `cargo install` (or the downloaded script for the `url` form) only after the analysis and your go-ahead: ```bash # Instead of: npm install left-pad tirith install npm left-pad ``` A block refuses the install (bypassable per policy with `TIRITH=0`); a warn asks for acknowledgement; an allow proceeds. tirith's own flags go *before* the source — everything after the source is passed verbatim to the package manager: ```bash # --online adds registry-API provenance signals; --save-dev goes to npm tirith install --online npm some-pkg --save-dev # Analyze and record only — do not run the real install tirith install --no-exec pip requests # Proceed past warnings without the interactive prompt tirith install --yes cargo ripgrep # The url form delegates to `tirith run`'s safe download-and-run machinery tirith install url https://get.example-tool.sh ``` `tirith install` is pre-execution install-**risk analysis** plus a recorded transaction. It does **not** sandbox or isolate the install — the real install runs with your full privileges. The checkpoint is a before/after record (`tirith checkpoint diff `), not an automatic rollback. Runtime sandboxing is an explicit tirith non-goal. ### Using vet (external, cross-platform) [vet](https://getvet.sh) is an external tool for safer remote-script workflows (see getvet.sh for details): ```bash # Instead of: curl -fsSL https://example.com/install.sh | bash vet https://example.com/install.sh ``` Both approaches ensure you can inspect the script before it runs. ### Policy: suppress pipe-to-shell for trusted sources If you routinely install from trusted URLs, allowlist them instead of bypassing: ```yaml # .tirith/policy.yaml allowlist: - "get.docker.com" - "raw.githubusercontent.com/org/repo" ``` ### CLI: manage trust without editing YAML `tirith trust` does the same thing from the command line, and steers you toward the narrowest scope that works. Trusting a specific path is accepted as-is; trusting a whole domain is broad and must be opted into with `--broad`. Entries expire after 30 days by default, so a temporary allow does not linger. ```bash # Narrow: trust one exact resource. Expires in 30 days. tirith trust add raw.githubusercontent.com/org/repo/main/get.sh # Broad: trust a whole domain for one rule only. --broad is required. tirith trust add get.docker.com --broad --rule curl_pipe_shell tirith trust list # see every entry, its scope, and its TTL tirith trust explain get.docker.com tirith trust diff # what changed since last time tirith trust gc --expired # remove entries whose TTL has passed ``` Use `--permanent` if an entry genuinely should never expire, and `--reason` to record why it was added — `tirith trust explain` shows it back to you.