CI and automation
Start with a local baseline before turning Tien into a release gate. A policy applied too early can block a team on existing noise instead of preventing regressions.
Basic CI sequence
tien scan repo --path . --format gob --policy high > tien-repo.tien
tien report --type sarif tien-repo.tien > tien.sarif
tien report --type md --min-severity med tien-repo.tien > tien.md
The scan writes findings before returning a non-zero policy result. Preserve the artifact even when the policy exits with code 3.
GitHub Actions example
name: Tien security
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
security-events: write
jobs:
tien:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: "1.23.x"
- name: Install Tien
run: go install github.com/seibel777/tien.app/cmd/tien@latest
- name: Scan repository
run: tien scan repo --path . --format gob --policy high > tien.tien
- name: Create SARIF
if: always()
run: tien report --type sarif tien.tien > tien.sarif
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: tien.sarif
Pin Tien to a reviewed version instead of @latest in a production pipeline.
Policy mode
policy:
enabled: true
min_severity: "high"
Or override per run:
tien scan repo --path . --policy crit > result.tien
Exit code 3 means the scan completed and found at least one issue at or above the threshold. Exit code 1 indicates a runtime or scan error. CI logic should distinguish the two.
Baseline comparisons
Store a reviewed baseline as a protected CI artifact or in a controlled branch, then compare the current run:
tien diff baseline.tien current.tien > diff.txt
A baseline is not an ignore file. Existing findings remain visible; the diff simply makes regressions and fixes easier to identify.
Large URL target sets
Tien does not impose a fixed target-count ceiling. Use deterministic shards when they help fit the scan into runner time and concurrency budgets:
tien scan url --targets targets.txt --shard 1/4 > shard-1.tien
tien scan url --targets targets.txt --shard 2/4 > shard-2.tien
tien scan url --targets targets.txt --shard 3/4 > shard-3.tien
tien scan url --targets targets.txt --shard 4/4 > shard-4.tien
tien merge shard-*.tien > url-complete.tien
Every runner must use the same protected operator config. An explicitly selected missing, unreadable, or invalid config fails the runner instead of falling back to defaults. Each exact hostname in a shard needs a current DNS TXT authorization and its own entry in the operator hosts challenge map. Tien completes authorization preflight for the entire shard before cache access, workers, or built-in target requests start, so a missing or expired grant fails that shard without partially scanning it. If the earliest grant expires during execution, Tien aborts the shard with authorization failure; MCP reports a tool error rather than partial success. The url-scan scope covers only built-in GET/HEAD checks: a shard containing any public target fails closed when a URL plugin is enabled. URL plugins can currently run only for explicitly proof-exempt literal localhost or private-IP development targets. A hostname that resolves to RFC 1918 or IPv6 ULA space must be exactly allowlisted, use an HTTPS target, present its exact DNS TXT grant, and keep its resolved addresses pinned for the scan.
Treat DNS TXT proof as a cooperative operational guardrail, not legal or contractual authorization. Preserve written permission and rules of engagement separately. Protect and pin the config's referenced rules, binaries, ignore files, advisory inputs, and image digests because loading the config at startup does not snapshot those resources.
Metrics and history
Prometheus metrics can be exposed during longer scans:
tien scan url --targets targets.txt --metrics true --metrics-listen 127.0.0.1:9465 > url.tien
Metrics are runtime visibility, not a persistent result. Enable history for a lightweight local run index; use scan artifacts for durable evidence.