mcp server
tien.app
Local-first security CLI for AI-built apps: run Gitleaks, OSV, and Semgrep, then let MCP agents scan, fix, and verify safely.
Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 23, 2026Tien
Repository: https://github.com/seibel777/tien.app
Tien is a local-first security CLI that helps people and AI coding agents turn AI-built or vibe-coded apps into reviewable evidence. Its built-in checks are passive on:
- Repositories (JS/TS-focused) via SAST/SCA/secrets tooling
- URLs via non-invasive HTTP/TLS/header analysis (GET/HEAD only)
It’s inspired by “pipeline-style” CLIs (scan → encode/merge → report/plot): the scanner emits results, and other commands consume those results to generate reports or visualizations.
Tien does not attempt exploitation. Passive findings are signals + evidence, not proof of compromise.
New to security tooling? Start with the getting-started guide. Using Codex, Claude Code, Cursor, Copilot, or another coding agent? Use the AI agent integration guide.
Goals
- Single binary (local usage)
- Passive-only built-in URL checks (no payloads, fuzzing, brute force)
- Repo + URL from day one
- Stream-friendly workflows (stdin/stdout, file outputs)
- Deterministic output (easy diff between runs)
- Scales locally (concurrency, caching, shard mode)
What Tien does
Repo scan (passive)
- Secrets detection (tokens/keys committed by mistake)
- Dependencies vulnerability scanning (SCA)
- Static rules for insecure patterns (SAST), with a JS/TS focus
- Normalizes results into a single finding format
URL scan (passive)
Only GET/HEAD and analysis:
- TLS certificate / HTTPS posture
- Security headers (CSP/HSTS/etc.)
- Cookie flags (Secure/HttpOnly/SameSite) when present
- CORS misconfig patterns
/.well-known/security.txtpresence- Mixed content signals (http assets referenced by https pages)
Behavioral safety boundaries
- No active exploitation
- No credential guessing / brute force
- No payload injection testing (XSS/SQLi/etc.)
- No aggressive crawling or wordlists
Tien does not provide active testing or penetration-testing behavior. Its built-in checks stay passive by design. External plugins are operator-trusted processes and are not sandboxed; review them and enforce read-only filesystem and network policy separately.
Tien does not impose arbitrary caps on target count, finding count, or repository file size. Configurable timeouts, response-body size, redirects, retries, and concurrency remain resource budgets so a scan cannot consume unbounded local or remote resources.
Status (current)
- Implemented: config loader, Finding schema, gob/jsonl IO, merge, encode, reports (text/md/sarif/json), HTML plot, diff, URL scan (TLS + headers + cookies + CORS + security.txt + mixed content), host summary, repo scan (gitleaks + osv-scanner + semgrep), caching (repo + optional URL), run history, shard mode, retry + per-target timeout, graceful cancellation, plugin system, policy mode, Prometheus metrics, minimal TUI summary, custom Semgrep rules pack, hybrid gitleaks runner (CLI or Docker fallback).
Stack
Core
- Go (CLI + pipeline + storage + reports)
Engines (invoked by Tien)
Modes:
- hybrid (default): use local CLIs when available, fallback to Docker with a warning.
- cli: require installed CLIs.
- docker: always run tools in containers.
Repo scan integrates gitleaks, osv-scanner, and semgrep.
Output formats (current)
gob(default; compact + fast)jsonl(debug + easy integrations)textreportmdreportsarifreportjsonreporthtmlplot
Install
Go install
go install github.com/seibel777/tien.app/cmd/tien@latest
tien --help
Homebrew
brew tap seibel777/tien.app https://github.com/seibel777/tien.app
brew install tien
Requires the tap formula updated with the latest release checksums. Check the public installation guide for the currently supported paths.
NPM
npm i -g @seibel777/tien
tien --help
Requires the npm package to be published (see packages/npm).
GitHub (clone)
git clone https://github.com/seibel777/tien.app.git
cd tien.app
go build -o tien ./cmd/tien
./tien --help
Releases (binary download)
Prebuilt binaries: https://github.com/seibel777/tien.app/releases
Linux amd64 example:
curl -L -o tien https://github.com/seibel777/tien.app/releases/latest/download/tien_linux_amd64
chmod +x tien
./tien --help
macOS arm64 example:
curl -L -o tien https://github.com/seibel777/tien.app/releases/latest/download/tien_darwin_arm64
chmod +x tien
./tien --help
Checksums are published as *.sha256 next to each binary.
Engine dependencies (repo scan)
Option A - installed CLIs Ensure these commands are available in $PATH:
- gitleaks
- semgrep
- osv-scanner
Option B - Docker Install Docker and enable the "docker engine" mode in config (see below).
Don’t mix A and B unless you enjoy environment debugging.
Usage (current)
URL scan (targets via stdin)
cat targets.txt | tien scan url --format gob > url.tien
URL scan (targets file)
tien scan url --targets targets.txt --format jsonl > url.jsonl
Host summary is printed to stderr by default; disable with --summary=false.
URL scan (shard mode)
tien scan url --targets targets.txt --shard 1/5 --format gob > url_shard_1.tien
URL scan with policy mode
tien scan url --targets targets.txt --policy high --format gob > url_policy.tien
URL scan with Prometheus metrics
tien scan url --targets targets.txt --metrics true --metrics-listen 127.0.0.1:9465 --format gob > url.tien
Merge results
tien merge url.tien other.tien > all.tien
Report (text)
tien report --type text all.tien
Report (Markdown)
tien report --type md all.tien > report.md
Report (SARIF)
tien report --type sarif all.tien > tien.sarif
Plot (HTML)
tien plot all.tien > report.html
Encode (JSONL)
tien encode --to jsonl all.tien > all.jsonl
Diff scans
tien diff old.tien new.tien
History (local run index)
tien history --limit 10
Enable history in config or pass --history true to record runs.
Repo scan (gitleaks + osv-scanner + semgrep)
tien scan repo --path . --format gob > repo.tien
Note: osv-scanner and semgrep may require network access for advisories or rules; disable them in config if needed.
Repo scans are cached by commit/config/engine versions by default; disable with --cache false.
URL cache is optional (ETag/Last-Modified) and disabled by default.
Hybrid gitleaks (CLI or Docker fallback)
tien hybrid --path . > gitleaks.json
Outputs raw gitleaks JSON (not normalized Findings yet).
Note: gitleaks returns exit code 1 when leaks are found; tien hybrid treats that as success so you still get output.
Note: every public URL target requires a scoped, expiring authorization for its exact hostname. Generate the DNS TXT grant with tien authorize dns; public-target authorization cannot be disabled by a scan caller.
Targets format (URL scan)
targets.txt (one per line):
https://example.com https://app.example.com https://api.example.com
You can also pipe from another tool:
some-generator | tien scan url > url.tien
Configuration
Default path:
- macOS/Linux:
~/.config/tien/config.yaml
When you explicitly pass --config <path>, that file is required. A missing, unreadable, or invalid selected file fails the command instead of falling back to the default path or built-in defaults.
Example:
repo:
engine_mode: "hybrid" # "cli", "docker", or "hybrid"
engines:
semgrep:
enabled: true
config: "p/default" # or rules/semgrep/tien-js.yml
# max_target_bytes is optional; Tien passes 0 to disable Semgrep's implicit file-size filter
# timeout_seconds and timeout_threshold are optional; 0 disables implicit skip thresholds
image: "semgrep/semgrep:<reviewed-version>" # or an immutable @sha256 digest
gitleaks:
enabled: true
image: "ghcr.io/gitleaks/gitleaks:<reviewed-version>"
osv:
enabled: true
image: "ghcr.io/google/osv-scanner:<reviewed-version>"
exclude:
- "node_modules/**"
- "dist/**"
- ".next/**"
- "build/**"
- "**/*.min.js"
url:
concurrency: 20
timeout_ms: 8000
max_target_ms: 15000
max_body_kb: 256
follow_redirects: true
max_redirects: 5
retry:
max_attempts: 2
backoff_ms: 200
checks:
tls: true
headers: true
cookies: true
cors: true
securitytxt: true
mixed_content: true
safety:
allow_localhost_without_proof: false
allow_private_ips_without_proof: false
allow_resolved_private_hosts: [] # exact hosts only; private answers require HTTPS + DNS TXT authorization
proof:
method: "dns" # DNS TXT is the only supported authorization method
hosts:
app.example.com: "GENERATED_HOST_SPECIFIC_256_BIT_CHALLENGE"
scope: "url-scan"
max_validity_minutes: 1440
output:
default_format: "gob"
redact_secrets_in_evidence: true
cache:
dir: "~/.cache/tien" # optional override
repo:
enabled: true
url:
enabled: false
history:
enabled: false
policy:
enabled: false
min_severity: "high"
metrics:
enabled: false
listen: "127.0.0.1:9465"
plugins:
- name: "custom-check"
kind: "repo" # or "url"
command: "/path/to/plugin"
args: ["--flag"]
format: "jsonl"
timeout_ms: 20000
enabled: false
Replace every <reviewed-version> placeholder with a reviewed immutable tag or image digest before using the example.
Plugins (custom checks)
Plugins let you add custom checks without forking Tien. Each plugin is an external command that outputs Findings in JSONL or gob.
Plugin environment variables:
• TIEN_PLUGIN_NAME
• TIEN_PLUGIN_KIND (repo|url)
• TIEN_SCAN_TIME (RFC3339)
• TIEN_REPO_PATH (repo plugins)
• TIEN_TARGETS_FORMAT=lines + TIEN_TARGET_COUNT (url plugins)
Repo plugins run with the repo path as their working directory. URL plugins receive the targets list on stdin (one URL per line), but the current url-scan authorization scope covers built-in GET/HEAD checks only. A batch 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.
Custom Semgrep rules pack
Use the bundled rules pack in rules/semgrep/tien-js.yml for JS/TS-focused checks. Set:
repo:
engines:
semgrep:
config: "rules/semgrep/tien-js.yml"
Metrics, policy, and TUI
- Prometheus metrics:
tien scan url --metrics true --metrics-listen 127.0.0.1:9465exposes/metrics. - Policy mode:
tien scan repo --policy highexits non-zero if findings >= severity. - Minimal TUI: add
--tuito show a live summary on stderr.
Scoped authorization (URL scan safety)
Every public URL target requires authorization for its exact hostname. Create a 256-bit challenge and an expiring grant instead of reusing a permanent token:
tien authorize dns --domain app.example.com --valid-for 24h
Repeat --domain in one command for a multi-host batch; Tien emits one exact-host grant and a distinct challenge per domain, forming the operator's explicit host allowlist.
Publish the structured value at the DNS name printed by the command, for example:
_tien-authorization.app.example.com. TXT "v=tien1;host=app.example.com;scope=url-scan;challenge=...;issued=2026-07-15T12:00:00Z;expires=2026-07-16T12:00:00Z"
An authorization for example.com does not authorize app.example.com. Tien validates the hostname, url-scan scope, challenge, issue time, expiry, and the operator-configured maximum validity.
Config example:
safety:
allow_localhost_without_proof: false
allow_private_ips_without_proof: false
allow_resolved_private_hosts: [] # private DNS answers require HTTPS + DNS TXT authorization
proof:
method: "dns"
hosts:
app.example.com: "GENERATED_HOST_SPECIFIC_256_BIT_CHALLENGE"
scope: "url-scan"
max_validity_minutes: 1440
DNS TXT is Tien's only URL authorization method. Legacy http and dns+http configurations fail closed and must be migrated to method: "dns".
The url-scan grant is a cooperative operational scope signal for Tien's built-in GET/HEAD checks. It is not legal or contractual authorization, does not replace written permission or rules of engagement, and cannot stop a modified binary or fork from removing the guardrail.
Tien authorizes the complete target batch before starting workers, cache reads, or built-in network requests. One unauthorized target stops the complete batch. If the earliest grant expires during execution, Tien aborts the run and reports authorization failure; MCP returns a tool error, not partial success. Public URL plugins fail closed because they are outside the url-scan capability; only explicitly proof-exempt literal localhost or private-IP targets can currently reach a URL plugin. Redirects must remain on the same scheme, hostname, and effective port. A hostname that resolves to RFC 1918 or IPv6 ULA addresses must appear exactly in allow_resolved_private_hosts, use an https:// target, and present its exact DNS TXT grant; resolved addresses are pinned for the run. Loopback through a non-local name, link-local, CGNAT, multicast, unspecified, and metadata addresses remain blocked. Local and private development targets require explicit operator exemptions.
Design
Pipeline model
Tien follows a two-step model: 1. scan commands emit a stream of findings (stdout/file) 2. report/plot/encode/merge consume findings and produce outputs
This makes it easy to: • run scans on multiple machines • merge outputs later • keep scanning logic separate from reporting/visualization
Finding schema (single format for repo + url)
Each issue is normalized into a Finding: • id: stable check identifier (url.headers.hsts_missing, repo.secrets.generic, repo.deps.osv) • source: url or repo • target: URL or path:line • severity: info|low|med|high|crit • confidence: low|med|high • title: short human title • description: what was detected and why it matters • evidence: short, redacted proof (headers/snippet/package) • remediation: direct fix guidance • tags: e.g. cwe:, owasp:, category:* • fingerprint: deterministic hash to deduplicate across runs • timestamp: when detected • run_id: run identifier (for comparisons)
Fingerprint rules (must be stable) • URL findings: hash of (id + normalized_host + normalized_path? + key_evidence_fields) • Repo findings: hash of (id + file_path + line + rule_id/package_id)
This enables: • deduplication • diff between runs • stable reporting even with concurrency
See docs/checks.md for the full list of check IDs.
Engines
Repo engines implement: • run external tool • parse tool output (JSON/SARIF where possible) • normalize into Finding
URL checks implement: • fetch (GET/HEAD only) with timeouts and limits • analyze TLS/headers/body (bounded) • emit normalized Finding
Commands overview
tien mcp
Runs a local Model Context Protocol server over stdin/stdout so compatible coding agents can invoke Tien with typed inputs and structured outputs.
Common flags:
--root <dir>(repository boundary; defaults to current directory)--config <path>(operator-owned policy fields loaded when the server starts; a missing selected path fails startup)
The server exposes capabilities, repository scanning, URL scanning, and artifact diff tools. Paths cannot escape the selected root, evidence is excluded by default, and the operator-owned policy fields for engines, plugins, arguments, and URL authorization are loaded once at startup. An explicitly selected missing, unreadable, or invalid --config path fails startup instead of falling back to defaults. Neither scan tool accepts agent-controlled replacement config. Referenced rule files, plugin binaries, ignore files, and mutable image tags are not snapshotted and can drift independently; protect them from agent writes and pin versions or digests. Every URL batch fails closed unless the startup policy authorizes all exact hosts, and public URL plugins remain outside the url-scan scope. MCP imposes no target-count or finding-count ceiling; a caller may still request a smaller presentation with the optional max_findings tool argument.
See docs/ai-agents.md for client configuration and the agent operating contract.
tien scan repo
Scans a repo path using configured engines and emits findings. Runs the enabled engines (gitleaks, osv-scanner, semgrep). Supports commit-based caching when enabled.
Common flags: • --path • --format gob|jsonl • --config • --cache auto|true|false • --history auto|true|false • --policy <severity|off> • --metrics auto|true|false • --metrics-listen • --tui
tien scan url
Reads URLs from stdin (or --targets) and emits findings.
Common flags: • --targets (optional; stdin by default) • --format gob|jsonl • --summary=true|false • --config • --shard N/M • --cache auto|true|false • --history auto|true|false • --policy <severity|off> • --metrics auto|true|false • --metrics-listen • --tui
Config options control concurrency, per-target runtime, response-body size, retries, redirects, and scoped authorization. These resource budgets do not cap target or finding counts.
tien hybrid
Runs gitleaks using the local CLI or Docker fallback and emits raw JSON.
Common flags: • --path • --image
tien merge
Merges multiple .tien files into one.
tien report
Generates reports: • --type text|md|sarif|json • --min-severity info|low|med|high|crit • --group-by source|severity|target|none • --only-tags tag1,tag2 • --top
tien encode
Converts formats: • --to gob|jsonl
tien plot
Generates report.html from findings. Common flags: • --min-severity info|low|med|high|crit • --only-tags tag1,tag2
tien diff
Compares two scans and reports new/fixed/changed findings.
tien history
Shows the local run history (when enabled in config).
Common flags: • --format text|json • --limit
Roadmap
See roadmap.md for the phased checklist and optional extensions.
Testing guide: docs/testing.md.
Project layout
tien/ cmd/tien/ # CLI entrypoint internal/app/ # config + wiring internal/cache/ # repo/url caches + history storage internal/history/ # local run history helpers internal/metrics/ # Prometheus metrics collector internal/mcpserver/ # local MCP tools + repository boundary enforcement internal/plugins/ # plugin runner for custom checks internal/scan/ # orchestration + URL/repo scanning internal/engines/repo/ # semgrep/osv/gitleaks wrappers internal/diff/ # scan diffing internal/store/ # gob/jsonl IO + merge internal/report/ # text/md/sarif/json internal/plot/ # html plot pkg/model/ # exported Finding schema pkg/version/ docs/ examples/ rules/semgrep/ .github/workflows/
License
Apache-2.0. See LICENSE.
Links
- Repo: https://github.com/seibel777/tien.app
- Releases: https://github.com/seibel777/tien.app/releases
- Issues: https://github.com/seibel777/tien.app/issues
- Docs:
docs/README.md - Flags:
tien --help - Changelog:
CHANGELOG.md - License:
LICENSE - Security:
SECURITY.md - Contributing:
CONTRIBUTING.md
Disclaimer
Tien is intended for scanning: • code you own • systems you are authorized to assess
Passive scanning reduces risk, but it is still your responsibility to obtain written authorization and comply with laws, policies, rules of engagement, and scope restrictions. Tien's DNS TXT proof is a cooperative operational guardrail, not legal or contractual permission.