Skip to content
Conduit

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.

seibel777/tien.appcommunitylistedunreviewed

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, 2026

Tien

CI

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.txt presence
  • 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)
  • text report
  • md report
  • sarif report
  • json report
  • html plot

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:9465 exposes /metrics.
  • Policy mode: tien scan repo --policy high exits non-zero if findings >= severity.
  • Minimal TUI: add --tui to 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


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.

↑↓ navigate openesc close