Configuration
Every Kartis component is configured through a YAML file plus a small number
of Docker Compose / .env variables. All three long-running commands take
-c/--config <path>:
| Component | Default config path |
|---|---|
kartis-scan (serve, daemon, scan) | /etc/kartis/kartis-scan.yaml |
kartis-discovery | /etc/kartis/kartis-discovery.yaml |
kartis-recon | /etc/kartis/kartis-recon.yaml |
kartis-scheduler | /etc/kartis/kartis-scheduler.yaml |
kartis-scan.yaml
name: kartis-scan
listen: ":8080"
database: "postgres://<postgres-user>:<postgres-password>@<host>:5432/kartis?sslmode=require"
redis:
host: localhost
port: 6379
password: "<redis-password>"
scan_profile: polite # polite | normal | aggressive — which entry in `profiles` is active
profiles:
polite:
nmap_timing: T2
nmap_scan_type: "-sT"
max_rate_pps: 50
inter_probe_delay_ms: 200
concurrent_services_per_ip: 1
concurrent_runs: 5
normal:
nmap_timing: T3
nmap_scan_type: "-sT"
max_rate_pps: 200
inter_probe_delay_ms: 50
concurrent_services_per_ip: 2
concurrent_runs: 10
aggressive:
nmap_timing: T4
nmap_scan_type: "-sS"
max_rate_pps: 1000
inter_probe_delay_ms: 0
concurrent_services_per_ip: 4
concurrent_runs: 20listendefaults to:8080andscan_profiletopoliteif omitted.profilestunes nmap pacing (timing template, scan type, max packets/sec, inter-probe delay) and concurrency (services scanned per IP at once,concurrent_runs= the daemon’s worker-pool size) — this is the sameprofilevalue that can be overridden per-run via--profileor the API’sprofilefield, orthogonal to scan depth (max_level).-sT(polite/normal) is a plain TCP connect scan;-sS(aggressive) is a raw-socket SYN scan and needs theNET_RAWcapability (in Docker Compose:cap_add: ["NET_RAW"]).
Portal
The customer-facing portal is served by kartis-scan serve and is disabled
by default, so an existing deployment that only serves Eon Center keeps the
surface it has. See Portal for what it exposes.
portal:
enabled: true
trusted_proxies: "127.0.0.1" # who may set X-Forwarded-For / X-Forwarded-Proto
insecure: false # drop the __Host- cookie prefix and Secure flag
smtp_host: localhost # delivers the login codes
smtp_port: 25
mail_from: kartis@example.com| Key | Meaning |
|---|---|
enabled | Mount /portal/... at all. Default false |
trusted_proxies | Addresses or CIDRs whose X-Forwarded-For and X-Forwarded-Proto are believed. Empty means believe nobody — right when nothing fronts the process, wrong the moment nginx does |
insecure | Drops the __Host- cookie prefix and the Secure flag, for a test over plain HTTP. The portal is otherwise served behind TLS and the browser would reject the cookie |
smtp_host / smtp_port / mail_from | Delivery of the one-time login codes |
kartis-scheduler.yaml
The scheduler needs the database and nothing else: it does not scan and serves no HTTP.
database: "postgres://<postgres-user>:<postgres-password>@<host>:5432/kartis?sslmode=require"
interval: "1m" # how often to look for due sessions
batch: 50 # most runs placed per tickinterval only decides how long after a slot a run is placed, since cadences
are monthly or biweekly. batch caps a backlog so it is worked through over
several ticks rather than arriving at the customer all at once.
kartis-discovery.yaml
name: kartis-discovery
redis:
host: localhost
port: 6379
password: "<redis-password>"
db:
host: localhost
port: 5432
database: kartis
username: <postgres-user>
password: <postgres-password>
rate_limit:
whois_per_minute: <n>
ctlogs_per_minute: <n>
reverse_ip_per_minute: <n>
asn_per_minute: <n>
dns_recon_per_minute: <n>
tech_fp_per_minute: <n>
ai_calls_per_minute: <n>
sources:
enable_whois: true
enable_ctlogs: true
enable_reverse_ip: true
enable_asn: true
enable_dns_recon: true
enable_tech_fingerprint: true
enable_ai_correlation: true
enable_reverse_whois: false # opt-in, needs whoisxml_api_key (paid)
whoisxml_api_key: ${WHOISXML_API_KEY}
enable_brand_permutation: false # opt-in — permute brand label across common TLDs
enable_ai_org_expansion: false # opt-in — AI-assisted organization expansion, needs ai.api_key
ai:
provider: anthropic # anthropic | openai | ollama (local)
api_key: ${ANTHROPIC_API_KEY}
model: <model>
# base_url: http://localhost:11434 # ollama onlyEach enable_* flag under sources turns a discovery source on or off
independently; the run <domain> recursive-expansion parameters (--depth,
--min-confidence, --max-related-per-job, --max-jobs) are CLI flags, not
config keys (see Scanning).
kartis-recon.yaml
name: kartis-recon
redis:
host: localhost
port: 6379
password: "<redis-password>"
db:
host: localhost
port: 5432
database: kartis
username: <postgres-user>
password: <postgres-password>
ai:
provider: anthropic
api_key: ${ANTHROPIC_API_KEY}
model: <model>Environment variables (.env / Docker Compose)
.env (copy from .env.example, or generate it with ./setup.sh)
feeds variable substitution into the Docker Compose file, and is what the ${VAR} references inside
the YAML files above expand from at container startup.
| Variable | Purpose |
|---|---|
POSTGRES_PASSWORD / POSTGRES_USER / POSTGRES_DB | Postgres credentials — no default in prod compose |
REDIS_PASSWORD | Redis auth — required |
KARTIS_TAG | image tag the Compose file pulls (default latest) |
KARTIS_PORT | host-side port mapped to the API’s internal 8080 |
KARTIS_SCAN_LISTEN | listen address baked into kartis-scan.yaml by setup.sh |
KARTIS_SCAN_PROFILE | default scan profile: polite | normal | aggressive |
KARTIS_SCAN_CONTACT | contact URL/mailto: appended to the scanner’s User-Agent and SSH ident comment |
KARTIS_SCAN_EHLO | hostname announced in SMTP EHLO/HELO (default scanner.eon-kartis) |
ANTHROPIC_API_KEY | enables the AI-assisted subdomain source and the run --judge review pass |
SHODAN_API_KEY | enables the Shodan host/port enrichment source |
SECURITYTRAILS_API_KEY | optional discovery enrichment source |
WHOISXML_API_KEY | enables reverse-WHOIS (enable_reverse_whois) |
An empty API key simply disables the corresponding optional source — none are required for the core discovery/scan pipeline to run.
Bearer tokens
kartis-scan serve authenticates every /v1/... route except /healthz and
/readyz with a bearer token (only a hash is stored):
kartis-scan token create --name operator # prints the plaintext token once
kartis-scan token list # prefixes + metadata, no plaintext
kartis-scan token revoke <prefix-or-uuid>Store the printed token as a secret in whatever consumes the API (Eon
Center, Eon Aethis, Eon Insights, an operator’s ~/.kartis-token, CI); it
cannot be retrieved again after creation.
Deployment lifecycle
On the target, ./setup.sh interactively writes .env and the runtime
YAMLs, ./install.sh brings the stack up via Docker Compose, and
./upgrade.sh applies an upgrade package on top of an existing install
(backing up compose, .env, and YAML under .upgrade-backup-<timestamp>/).