Platforms & Operations
Android is Rutken's flagship target, but the same analysis layer reaches Linux ELF binaries, container images, and CI/CD pipeline definitions — with Windows PE actively building and macOS (Mach-O) on the roadmap — and the engine ships several ways to operate it: a terminal CLI, a localhost REST API, a desktop GUI, and device-backed dynamic analysis. This page is the end-to-end CLI reference for each of those operational surfaces. For install, the quickstart, and editions, see the main docs.
Install & editions
Everything on this page runs from the same single binary you install once with the launcher. If you haven't installed Rutken yet, follow the install section in the main docs, then come back here. In short:
curl -fsSL https://www.rutken.com/install.sh | bash
rutken --help
Rutken ships as one engine with capability tiers on top.
Community (free) is the full static analysis
(SAST) pipeline plus SCA/CVE matching and a taste of live
capture. Several surfaces on this page sit behind
paid-tier Cargo features that are compiled into the
distributed builds for the relevant editions — notably the
REST API (rest-api), the device capture/intervene
verbs (intercept / unpinned-capture /
offense), and the bespoke hooking engine
(bespoke-agent). The
editions section explains
what each tier is for; the
pricing page lists the
plans. This page describes the commands, not who can run them.
Two commands answer "what can I run, and against what?":
# every verb and its flags
rutken <target> <verb> --help
# the machine-checked verb-vs-target support matrix
rutken capabilities --format json
Several runtime verbs change a live app's behavior and are
gated behind explicit, verbatim authorization phrases plus a
target-hash match — they fail closed (exit 2)
without them. Only operate against targets you own or are
explicitly authorized to test.
Containers — image scan
rutken image scan scans a container image offline:
either an OCI layout directory or a docker save
tarball, auto-detected. It takes no APK argument (it's a
top-level, pre-dispatched verb like sca update),
and it reuses every existing report renderer — no new output
format to learn.
# save a running image to a tarball, then scan it
docker save my-image:latest -o image.tar
rutken image scan ./image.tar --fail-on high
What it looks at
Two independent finding sources are always combined into one report:
-
Container rule pack
(the container rule pack, 11 rules): an image-only
config pack (root user, missing healthcheck, broad exposed
port, credential-shaped env var, leftover package-manager
cache), a per-layer secret pack, an opt-in Dockerfile pack
(
--dockerfile <path>), and a coverage rule that renders every scan limitation as anInfofinding — an unscanned image is never reported "clean." -
OS-package vulnerability matching: reads
dpkg/apkinventories off the squashed image view and matches them against the same locally cached datasetrutken sca updatemanages, using identical--db/--cache-dir/--auto-update/--max-age-daysflags. Disable with--no-packages.
Key flags
--dockerfile <path>— unlock the Dockerfile rule pack (absence of a Dockerfile is recorded as a coverage limitation, never a pass).--framework cis-docker— render the scan as a CIS Docker Benchmark v1.7.0 assessment (control-id mapped;text/jsononly).--format—text(default),json,sarif,markdown,html,pdf,gitlab-codequality,junit.--only <id>/--skip <id>— select container rule ids (mutually exclusive; neither affects the SCA presence finding).--fail-on <severity>— CI gating; exit2when tripped.--list-rules— print the registered catalog instead of scanning.
Examples
rutken image scan ./my-image-oci-layout --no-packages
rutken image scan ./image.tar --dockerfile ./Dockerfile --format json
rutken image scan ./image.tar --db datasets/sample-os-package-advisories \
--cache-dir ./.rutken-cache --auto-update --fail-on high
rutken image scan ./image.tar --framework cis-docker --format json
Expected output & exit codes
stdout (or --out <path>) contains
only the rendered report. Dataset provenance (version,
cache age, skipped records) and the coverage-limitation count always
go to stderr — same discipline as rutken <apk> sca.
Exit 0 is a clean scan (including zero findings),
1 an operational error (not a valid image, empty
dataset cache with packages enabled, failed fetch, write
failure), and 2 a tripped --fail-on
gate.
Local image sources only — there is no registry pull. The
dpkg/apk version comparator refuses -/+
version strings, so most OS-package matches land as exact-version
but Low severity rather than range-confirmed. The checked-in
sample dataset is illustrative only (a few hand-picked real
CVE ids), not an authoritative feed.
Multi-platform parity
Android is the flagship — the deepest, most battle-tested surface — and the other platforms are real but deliberately not at that proof depth. The one architectural principle that makes this work: every layer above the rule engine is written against a single finding model, so a finding from any branch flows through the same compliance, correlation, reporting, and gating machinery.
Platform status
- Flagship Android (APK/DEX) — the complete pipeline, from parsing through taint, call-graph chains, compliance, and remediation PRs, out to the full verb surface, every report format, the REST API, the GUI, and MCP.
-
Actively building
Linux/ELF — a real vertical slice: parse →
native IR → rule packs → runtime → capture → patch →
correlation. Supports
info,rules,compliance,sca,patch,run,verify,correlate,serve, andmcp. The analysis foundation is section flags and symbol tables rather than full instruction semantics (lifting is a separate, AArch64-first effort). -
Actively building
Container and CI/CD — each is one scanning
verb (
image scan,cicd scan) that emits findings and stops; both are complete within their own scope. They also exposeserve/mcp;run/verify/correlateare permanently not-applicable — a saved image and a pipeline definition are static targets with nothing to observe at runtime. - Actively building Windows/PE — PE binary analysis shares the native pipeline and verb surface; the full workspace compiles today and validation is underway. It does not yet carry Android's proof depth.
- Coming soon macOS/Mach-O — on the roadmap. A Mach-O target is recognized today but deliberately fail-closed.
The sniffer routes any non-APK file to the native pipeline automatically, so on supported targets the same verbs just work:
# native binary — functions/CFG recovered so linkage rules are reachability-qualified
rutken libfoo.so rules --lift --format json
# same verb surface across native kinds (the dispatcher never branches on file kind)
rutken libfoo.so rules --format sarif
# CI/CD pipeline definitions (GitHub Actions / GitLab CI / Azure Pipelines)
rutken cicd scan .github/workflows --fail-on medium
APK-only flags (interprocedural analysis, chains, signature verification) are rejected with a named error on native targets rather than silently ignored, and a tripped lifting budget yields a partial result with the uncovered area recorded, never a hard error.
Compliance framework by target — SSDF vs CIS
The compliance axis resolves to a different standard per branch, and one of them carries a licensing constraint worth knowing:
- Android — MASVS and CERT-In.
- Native binaries (ELF, and PE as it lands) — NIST SP 800-218 (SSDF). The build-hardening controls (
PW.6) are exactly what Rutken's-no-pie/-no-aslr/-no-dep/-relro-weakrule families evidence. Run it as a verb:rutken libfoo.so compliance --framework ssdf. - Container images — CIS Docker Benchmark v1.7.0, as a rollup flag on the scan:
rutken image scan ... --framework cis-docker. - CI/CD — SLSA v1.0 plus SSDF, likewise a rollup flag on
cicd scan.
The framing difference is not cosmetic. SSDF (public domain),
MASVS (CC BY-SA), and SLSA (CC BY) control text may be
reproduced verbatim in reports. The CIS Benchmark terms of use
restrict redistribution, so Rutken's CIS output maps control
ids and taxonomy only — it embeds no benchmark prose. A
cis-docker report cites the control, not CIS's
words.
To see the authoritative, machine-checked map of which verb
works on which target kind (with a reason recorded for every
not-applicable cell), run rutken capabilities.
REST API — serve
rutken <target> serve exposes one
already-analyzed target as a localhost HTTP API, backed by the
service/api crates and an axum router.
It opens the target once through the same analysis path every
other verb uses, then serves synchronous request/response
routes until the process is killed — pinned to that one target
for the life of the process (no multi-tenant routing, no job
queue, no streaming).
rutken app.apk serve # binds 127.0.0.1:8787
rutken app.apk serve --bind 127.0.0.1:9000
rutken app.apk serve --max-concurrency 4
Endpoints
GET /v1/health— liveness; bodyok.GET /v1/meta— package, min/target SDK, sha256, size, DEX file count.GET /v1/rules— the rule catalog (same JSON asrules --list-rules --format json).GET /v1/verify-signature— signature integrity and trust.POST /v1/analyze— run the rules; JSON body selects rules/format ({}means every rule, JSON output).POST /v1/complianceandPOST /v1/sca— the compliance and SCA surfaces.POST /v1/correlate— unified static+dynamic report over supplied sessions.
Unknown paths return 404. Errors are
{"error":{"code":"...","message":"..."}} with
appropriate HTTP status (400 bad selection,
413 body over 8 MiB, 422
unprocessable session set, 500 internal). A key
design invariant: POST /v1/analyze?format=json is
byte-identical to rutken app.apk rules --format
json for the same selection — the HTTP response and the
CLI's stdout are interchangeable.
curl example
curl http://127.0.0.1:8787/v1/health
curl http://127.0.0.1:8787/v1/meta
curl -X POST http://127.0.0.1:8787/v1/analyze \
-H 'content-type: application/json' \
-d '{"rule": ["weak-crypto-algorithm"], "format": "json"}'
Key flags
--bind <addr:port>— default127.0.0.1:8787.--allow-remote— lifts the loopback-only refusal so--bindmay be non-loopback. Adds no auth or TLS; prints a loud stderr warning first.--max-concurrency <N>— concurrent analysis calls (default: available parallelism).
This is a localhost developer service with no
authentication and no TLS. Binding to a non-loopback
address is refused outright unless you pass
--allow-remote, and even then nothing protects it —
do not expose it to an untrusted network. The same
serve surface is available on native, container,
and CI/CD targets (with correlate refused on the
static-only ones); it is compiled only into builds with the
rest-api feature.
Terminal UI
There is no terminal UI. Rutken is GUI + CLI
only. An earlier rutken tui command (and
the matching image tui / cicd tui
forms and the internal TUI capability) was removed outright —
the crates/tui crate, its CLI command, and the
capability variant were all deleted together.
If you want a terminal-friendly, non-interactive workflow, the
CLI verbs on this page are fully scriptable (every one takes
--format json and friends), and headless live
traffic capture runs over SSH with no UI via
rutken capture record. For an interactive,
visual workspace, use the desktop GUI
instead.
Desktop GUI
The GUI is a separate binary, rutken-gui (package
gui) — a peer of the rutken CLI, not a
verb dispatched by it. It opens straight into a desktop security
workspace built on egui.
cargo run -p gui
cargo run --bin rutken-gui
Neither form takes a target path as an argument: the workspace opens empty, and you load a target from inside the running session via File → Open…, which shows a native OS file dialog. The dialog is the only way to open a target — there is no typed-path field and no CLI flag for it.
Panels
A three-pane frame — APK tree | call-graph canvas | Findings — over a six-tab bottom bar (IR · Disassembly · Dataflow · Runtime · Network · Console), plus two rail modes: Deps (a vulnerable-components table with an advisory detail drawer) and CI/CD (a baseline delta with a PASS/FAIL gate verdict at a selectable severity).
- Call-graph canvas — five live scopes (current method callers/callees, N-hop neighbourhood, finding-centred, path between A → B, custom filtered subgraph). An over-budget selection is refused with a count and advice to narrow, never silently truncated; "path between" is a directed search and tells you whether it hit a cap, so "no path within N hops" is never presented as "no path exists."
-
Network tab — populated with recorded
.rcapcapture-bundle flows when the GUI is built with theinterceptfeature (cargo run -p gui --features intercept). Theoffensefeature likewise populates a read-only, plan-only Offense window. - Console tab — a diagnostics surface (a bounded activity log plus a coverage section of skipped DEX files and native-recovery limitations), not a REPL. Nothing is typed into it.
AI chat — bring your own model
A docked chat panel — toggled from the sparkle button in the
title bar — lets you converse with an AI agent that drives
Rutken's own analysis against the loaded target by calling its
MCP tools (analyze, explain_finding,
finding_code, remediate_plan, and the
rest). It is strictly bring-your-own-model:
Rutken bundles no model and ships no key. You point it at your
own endpoint and pay for your own inference — which is why it is
a free capability: the cost is entirely yours.
-
Providers — Anthropic
(Claude models) or OpenAI (GPT models),
connected directly over TLS with just your key; or
Custom for your own self-hosted endpoint
(ollama, LM Studio, vLLM, or any OpenAI-compatible server,
e.g.
http://127.0.0.1:11434). - Model & effort — a clickable, per-provider model list (fetched from the endpoint, with a curated fallback) and a reasoning-effort control, in the chat's bottom bar.
- Your key — held in memory for the session by default. Tick Save key in OS secret store to persist it in your OS keyring (Secret Service / Keychain / Credential Manager), with a Forget button to remove it; the key is never written to disk in plaintext and never logged.
- Transparent — the transcript shows each tool call and its result inline, so you see exactly which Rutken tool the agent ran and what it returned.
The same tools are available to an external agent: the MCP tab sets up Rutken's MCP server so Claude Desktop, Claude Code, or VS Code can drive the analysis instead of the in-GUI chat.
Gotchas
A default build (no features) is a fully working
static-analysis GUI; the Offense and Network menu items still
render, greyed, with a hover explanation rather than vanishing.
The GUI has no persistent projects/sessions, no live capture,
and no run/verify instrumentation — it
is an interactive desktop tool only, with no CLI flags and no
scripted use case.
There is one dev/test-only exception to "no headless mode,"
and it is not an operator-facing flag: setting
RUTKEN_GUI_OPEN=<target path> preloads a
target the way File → Open would,
RUTKEN_GUI_VIEW=<view id> selects a rail
view, and RUTKEN_GUI_SCREENSHOT=<png path>
renders a few frames and writes a PNG before exiting. These env
vars exist only to make documentation screenshots reproducible
under xvfb-run; none are read unless explicitly
set, and the normal interactive launch is unaffected.
Device verbs — devices, run, verify
The dynamic-analysis verbs touch a live device, emulator, or
process over adb (and, in paid builds, Rutken's own
in-house bespoke agent). A typical loop is: list devices,
record a session by running the app, then verify the recorded
session offline against the static findings.
devices — enumerate targets
Lists every ADB-visible device, including unauthorized and offline ones. Takes no target argument.
rutken devices
rutken devices --format json
With no devices attached it prints No devices found.
and exits 0. The JSON form is the scripting hook —
fan a run out across every device with a shell loop over
rutken devices --format json | jq -r '.devices[].serial'.
run — install, launch, observe
Connects to the device, installs the app (unless
--no-install), launches it, observes for a bounded
window, records a session, and always runs teardown on every
exit path.
rutken app.apk run --frida --duration 60 --session-out s.json --verify
--device <serial>/--serial <s>— pick the target device.--duration <secs>— bound the observation window.--session-out <path>— write the recorded session to disk.--frida/--frida-attach— spawn-and-gate or attach a Frida hook/observation session (plain launch + logcat otherwise).--verify— cross-check the observed session against the same static rulesrutken rulesruns, inline.--correlate— additionally join via the correlation engine.--no-install— observe an already-installed build.--bespoke …— drive Rutken's in-house agent instead of Frida/logcat (paidbespoke-agentbuilds).
run/verify are intentionally
single-device, single-target per invocation; multi-device
fan-out is a shell loop, not a built-in flag. Native targets
(Linux ELF today) support run too, with their
own flags.
verify — offline session report
Renders a runtime-verification report over a session already
recorded by run --session-out. No device access —
the session is read entirely from disk, and the result is
rendered verbatim with no re-scoring.
rutken app.apk verify --session s.json
rutken app.apk verify --session s.json --format json
--session <path>— the recorded session (required).--rule <id>/--exclude-rule <id>— narrow the rule set.--interprocedural[-params]— widen the static side of the comparison.
To join several runs — multiple sessions and/or
.rcap capture bundles — into one confidence-scored
report, use rutken <apk> correlate
(monotone-upward confidence: dynamic corroboration raises a
finding's confidence but never lowers it).
The active (behaviour-changing) and capture tiers —
capture, unpin,
intervene — require explicit verbatim consent
acknowledgements plus a target-hash match, evaluated before any
device work happens; a failed gate exits 2. Plain
devices/run/verify
observation does not change app behavior, but you must still
only test targets you are authorized to test. For the full
traffic-capture and TLS-unpinning runbook, see the main docs'
Network capture & TLS
section and the
Dynamic & network capture
guide.