Documentation
Crossfyre docs
Everything to install the toolchain, bring nodes online, run distributed offensive security workflows, and read the results. New here? Jump to the Quickstart.
Overview
Crossfyre is a hosted control plane for distributed offensive security. You enrol your own machines as nodes; the platform orchestrates scan engines across them, and turns raw output into prioritized findings. Five engines ship today and chain into a single recon-to-vulnerability pipeline: voyage (subdomain enumeration), pulse (network scanning), mach (content discovery and crawling), scout (service enumeration and fingerprinting) and cortex (vulnerability scanning). The scan engines and node agent are open source; the orchestration, scheduling and dashboard are the hosted platform.
The pieces: the CLI (crossfyre) you install on a host, a node (that host, enrolled and running scans), engines (the scanners), workflows (.cfx scripts that drive a scan), findings (the results), and credits (the metered fuel a scan burns).
Quickstart
- Create an account at crossfyre.io.
- Install the CLI:
curl -fsSL https://get.crossfyre.io/install.sh | sudo bash - Authenticate:
crossfyre login - In the dashboard create a node, copy its key, then enrol this host:
sudo crossfyre node init - Start a workflow from the dashboard (or
crossfyre run), then watch findings stream in and get notified when it finishes.
Requirements
- OS: Linux or macOS (the Windows installer is experimental).
- Docker: required. The engines persist scan state to a local database that the CLI runs as a Docker container, so install Docker before
crossfyre node init(curl -fsSL https://get.docker.com | sh). - Root: needed to install the node service and set up isolated egress tunnels.
Installation
Linux and macOS:
curl -fsSL https://get.crossfyre.io/install.sh | sudo bash Windows (PowerShell):
irm https://get.crossfyre.io/install.ps1 | iex The installer downloads crossfyre and the node worker to /opt/crossfyre/bin (symlinking crossfyre into your PATH), verifying every binary's SHA-256 against the signed release manifest, and adds /opt/crossfyre/bin to your shell PATH. Prebuilt binaries are on the toolchain page; you can also build from source from the open repo.
CLI reference
One CLI drives everything. The most-used commands:
crossfyre loginAuthenticate the CLI to your account (API key, username/password, or browser).crossfyre logoutRemove the saved session and stop/disable any installed engines.crossfyre node initEnrol this host as a node using a node key from the dashboard. Installs the selected engines, provisions the database, and installs the OS service.crossfyre node listList your fleet from the control plane, with live online/offline status.crossfyre node statusShow the node daemons running on THIS host.crossfyre node up / downStart / stop the node supervisor service (brings all local nodes online or offline).crossfyre node restart / enable / disableRestart the service, or toggle start-on-boot.crossfyre node remove [id] [--inactive]Remove a registered node from this host.crossfyre extension listList engines with install state and daemon health.crossfyre extension install <mach|voyage|pulse|scout|cortex|all>Download, checksum-verify and start an engine.crossfyre extension remove / update / start / stop / restart <name>Manage an installed engine.crossfyre run <script.cfx> <type:value ...>Run a .cfx workflow locally, no control plane required.crossfyre update [self|all|<ext>]Update the CLI, the node binary, and engines from the signed release manifest.crossfyre statusOverview of local node daemons, engines and the database.crossfyre db <up|down|start|stop|restart>Manage the toolchain database container.crossfyre doctorDiagnose the environment: Docker, database, release-CDN reachability, PATH.crossfyre uninstall [--purge]Remove services, engines, binaries and the database container.Nodes
A node is one of your hosts, enrolled to run scans. Create it in the dashboard (Nodes), then run sudo crossfyre node init on the host and paste the node key. Enrolment installs the engines you selected, provisions the local database, and installs an OS service so the node survives reboots.
The node service runs a supervisor that keeps every registered node online; each reports a heartbeat so the dashboard shows it online or offline. Bring them up or down with crossfyre node up / crossfyre node down, check local daemons with crossfyre node status, or the whole fleet with crossfyre node list.
OPSEC: a node can route outbound traffic through proxy chains and isolated VPN tunnels, so scans leave from where you choose and never touch the host's own network.
Scan engines
Engines are the scanners. Each is a standalone, open-source tool that runs as a local daemon, keeps its work in the toolchain database, and can run entirely on its own from the terminal. Enrol a host as a node and the platform supervises the same engines across your fleet, paces each scan to what the target can take, and streams every result into one shared asset graph. Install and manage them with the CLI (crossfyre extension install <name>); each runs a live terminal UI standalone and reports to your dashboard on the platform.
The five shipping engines chain into a single recon-to-vulnerability pipeline, and each also stands on its own:
voyageSubdomain enumeration. Passive intel sources plus active brute-force. Finds the hostnames.pulseNetwork and port scanning. Finds live hosts, open ports and the services on them.machContent discovery and crawling. Finds hidden paths and maps each web app's surface.scoutService enumeration and fingerprinting. Identifies the tech, versions and CVE leads.cortexVulnerability scanning. Runs detection templates and authorization tests, and confirms them.The pipeline in one line: voyage finds subdomains, pulse finds open ports and services, mach discovers content and crawls each web service, scout fingerprints the tech and versions, and cortex checks the surface for vulnerabilities. Every engine is stateful and resumable: a scan you stop, or one interrupted by a dropped node, picks up exactly where it left off.
voyage
voyage maps a domain's subdomains two ways at once. Passive enumeration pulls known hostnames from public intelligence sources; active enumeration brute-forces candidates from a wordlist and confirms the ones that resolve. Each discovered subdomain is reported with the source that found it, so you know whether it came from certificate transparency, passive DNS, or a live resolution. It is usually the first stage of a run: its output is the target list everything downstream works from.
Passive sources
Passive enumeration is fast, quiet, and needs no wordlist. voyage queries public sources and keeps only real subdomains of your target:
- crt.sh: certificate-transparency logs (every hostname that ever appeared in a TLS certificate for the domain).
- hackertarget: passive DNS and host search.
- AlienVault OTX: passive DNS records.
No API keys are required. Drop a single source with --exclude-passive-source, or skip passive entirely with --disable-passive-enum.
Active enumeration
Active enumeration takes each word in your wordlist, forms word.domain, and confirms it with a sequence of techniques. The first technique that succeeds marks the host found and records which one confirmed it:
- ipv4_lookup / ipv6_lookup: DNS A / AAAA resolution.
- http_probing / https_probing: an HTTP / HTTPS request on the ports you choose (defaults 80 / 443).
Exclude any technique with --exclude-active-technique, or skip the active phase with --disable-active-enum. You cannot disable both phases at once.
Standalone usage
voyage --daemon & # start the engine (port 4442)
# passive sources + active brute-force
voyage scan -d example.com -w ./subdomains.txt -t 32
# passive only (no wordlist needed)
voyage scan -d example.com --disable-active-enum
# active only, on custom probe ports
voyage scan -d example.com -w ./sub.txt --disable-passive-enum --http-probing-port 80,8080 --https-probing-port 443,8443 Key options
-d, --domainTarget domain to enumerate.-w, --wordlist-pathWordlist for the active brute-force (required unless active is disabled).-t, --tasksConcurrent workers for the active phase.--disable-passive-enum / --disable-active-enumTurn either phase off (not both).--exclude-passive-sourceDrop a source: crt.sh, hackertarget or alienvault.--exclude-active-techniqueDrop a technique: ipv4_lookup, ipv6_lookup, http_probing, https_probing.--http-probing-port / --https-probing-portPorts the HTTP / HTTPS probes hit. Defaults 80 / 443.--fresh-startDiscard saved state and start clean (the default resumes).Output
Each finding is a subdomain plus the source that confirmed it. Results stream to the live UI and into the toolchain database; on the platform they land in the shared asset graph and become the seed for the next stage. Passive and active results are merged and de-duplicated, so a host found by both counts once.
pulse
pulse scans hosts and ports to find what is open, closed or filtered, and can identify the service and grab a banner on each open port. It takes single hosts, hostnames or IPv4 CIDR ranges as targets, and port ranges, lists, or the built-in presets. On the platform, large scans are paced by the engine to the network path, so a scan runs fast on a healthy link without turning a slow-but-open port into a false negative.
Standalone usage
pulse --daemon & # start the engine (port 4443)
# top-1000 ports on a host
pulse scan -t example.com
# a CIDR block, explicit ports, service + banner detection
pulse scan -t 10.0.0.0/24 -p 22,80,443,8080 --service-detection
# a full sweep of several targets
pulse scan -t 10.0.0.5 10.0.0.6 -p all The live UI shows a progress gauge, an open / closed / filtered tally, and a table of open and filtered ports with service, latency and banner. Closed ports are counted but not listed, to keep the table focused on what matters.
Targets and ports
- Targets (
-t): hostnames, IPv4 addresses, or IPv4 CIDR blocks such as192.168.1.0/24. Pass several, space-separated. - Ports (
-p): a range (1-1024), a list (80,443,8080), or a preset:top-100,top-1000(the default), orallfor every port. - Service detection (
--service-detection): identifies the service on each open port and records the banner the server sends back.
Key options
-t, --targetsHosts, IPs or CIDR ranges. Repeatable.-p, --portsRange, list, or top-100 / top-1000 / all. Default top-1000.--service-detectionIdentify the service and grab the banner on open ports.--tasksHow many probes run in parallel. Higher is faster and louder.--timeoutPer-probe timeout in milliseconds. Raise it on high-latency links.--delayDeliberately slow the scan by pausing between probes.Postures
When run on the platform, a single dial sets how hard pulse is allowed to push, and the engine works within that envelope:
- stealth: stay quiet and low-footprint.
- balanced: the default middle ground.
- throughput: open up and go as fast as a healthy path allows.
On the platform
Run pulse as a network-scan workflow across your fleet; the open web services it finds become targets for scout and cortex. Pick a posture and the platform sets the pace for you, so you don't hand-tune concurrency for every target.
mach
mach is a stateful HTTP content-discovery and fuzzing engine. Point it at a URL with a FUZZ marker and a wordlist; it substitutes each word into the marker, fires the requests concurrently, and reports every path that comes back with a status you accept. Because each candidate request is recorded as it runs, a scan you stop resumes exactly where it left off and never re-tests a path it already checked. The same engine also powers wordlist-free crawling.
The FUZZ marker
mach replaces a single placeholder, ::FUZZ:: by default, with each word from your wordlist. Put it anywhere in the URL to control what you are fuzzing: leave it out and mach appends it to the path for classic directory discovery, or place it inside a path segment or a query value to fuzz there instead. Change the marker with --fuzz-marker if ::FUZZ:: collides with your target.
Standalone usage
mach --daemon & # start the engine (port 4441)
# directory / file discovery (the marker is appended if you omit it)
mach scan --url https://target.tld --wordlist-path ./common.txt --tasks 20
# place the FUZZ marker to control exactly what you fuzz
mach scan --url "https://target.tld/::FUZZ::/login" --wordlist-path ./dirs.txt
mach scan --url "https://target.tld/search?q=::FUZZ::" --wordlist-path ./payloads.txt The live UI shows progress, a found / not-found / error tally, and a table of hits with status code and response size. Press l for logs and q to quit.
Key options
-u, --urlTarget URL(s). Repeatable. The FUZZ marker is appended to the path if absent.-w, --wordlist-pathWordlist file, one candidate per line.--fuzz-markerThe placeholder each word replaces. Default ::FUZZ::.--http-methodget, post, put, delete or head. Default get.--success-status-codesComma-separated status codes counted as a hit. Defaults to the 2xx / 3xx range.-t, --tasksConcurrent workers. Higher is faster and louder.-i, --intervalDelay in milliseconds between requests per worker.--headers / --cookies / --basic-authRequest headers, cookies, and HTTP basic auth for reaching authenticated paths.--follow-redirects / --follow-redirects-depthWhether to chase redirects, and how deep (default 5).--random-user-agent-requestRotate a random User-Agent per request, or set one with --user-agent.--fresh-startIgnore saved state and rescan from scratch (the default resumes).--adaptive-rateLet mach adapt its concurrency and delay to the target's live health instead of a fixed rate.--postureHow aggressive the adaptive controller is: stealth, balanced or throughput.Output
Each result records the resolved URL, its scan status (found / not found / error), the HTTP status code, and the response and header sizes. mach classifies purely on the status codes you accept, so tune --success-status-codes to your target. Many engagements want 401 and 403 counted as interesting rather than ignored. Results are kept in the toolchain database and stream live; on the platform they land in your Findings explorer.
Crawling
Beyond wordlist fuzzing, mach can crawl a web app without a wordlist: it walks the site by following links, forms and the endpoints embedded in pages and JavaScript, mapping the reachable surface. On the platform this is the web-crawl workflow, and its output feeds scout and cortex the live URLs to work on.
On the platform
Run a content-discovery or web-crawl workflow from the dashboard and mach runs across your nodes against the targets you pass in, with the platform pacing it to the target and reserving credits for the work actually done.
scout
scout takes a live web service and works out what it is running. It fetches the target, reads the signals a server gives away (headers, cookies, the HTML, the generator meta tag, the favicon) and matches them against a curated signature set to identify the web server, language, framework, CMS and JavaScript libraries in use, with versions wherever the target exposes them. From each product and version it derives version-based CVE leads, detects the WAF, CDN or load balancer in front of the service, and computes a Shodan-compatible favicon hash for pivoting. It is the enrichment stage between discovery and vulnerability scanning.
What it detects
- Technologies and versions: web servers (Nginx, Apache, IIS, Tomcat and more), languages and runtimes (PHP, Java, Python, Node.js), frameworks (ASP.NET, Laravel, Django, Rails, Spring, Express), CMSes (WordPress, Drupal, Joomla, Magento) and JS libraries (React, Vue, Angular, Next.js, jQuery). A detected technology pulls in the ones it implies: WordPress implies PHP and MySQL, Tomcat implies Java.
- CPEs: each detection emits a CPE identifier carrying the detected version, the join key that CVE matching pivots on.
- WAF / CDN / load balancer: passively identifies the edge and protection vendor in front of a service (Cloudflare, Akamai, CloudFront, Fastly, Sucuri, Imperva, F5 BIG-IP, ModSecurity) from its tells.
- Favicon hash: an mmh3 hash compatible with Shodan's
http.favicon.hash, plus MD5, for correlating hosts across your surface. - CVE leads: for each detected product and version, scout matches a version-ranged CVE ruleset and flags the versions that fall in a vulnerable range.
CVE leads are marked version-inferred, not confirmed: they flag that a version sits in a vulnerable range, but a backported patch can make that a false positive. They are leads for cortex to confirm, not final findings. The ruleset ships with a starter set and can be extended at runtime by pointing SCOUT_CVE_FILE at your own JSON rules, with no rebuild.
Standalone usage
scout --daemon & # start the engine (port 4444)
# fingerprint a single service
scout fingerprint https://example.com
scout fingerprint example.com:8443
# full control: timeout, redirects, favicon, probing depth
scout exec '{"operation":"fingerprint","target":"https://example.com","depth_tier":2,"favicon":true}' Probing depth
The depth_tier parameter controls how much active probing scout does against a target: 0 passive (only the signals in the landing response), 1 quiet (adds the favicon fetch), 2 standard (the default), 3 aggressive. Lower tiers are quieter against sensitive targets. A per-request timeout_ms and a follow_redirects toggle round out the knobs, and an auth object lets scout fingerprint the authenticated surface.
Output
scout emits four kinds of finding: a technology record per detection (name, category, version, CPE, confidence and the evidence that matched), a service summary (status, title, server, WAF/CDN, favicon and the tech list), a vulnerability record per CVE lead, and an environment record that tells the vulnerability scanner how to behave: whether a WAF is present, which status codes that WAF blocks with, and a recommended pace.
On the platform
scout runs after discovery: it takes the live web services that pulse and mach surfaced, enriches each one, and writes the results into the shared asset graph. Its environment record is what lets cortex pace itself sensibly against a WAF-fronted target, and its CPEs tie a detected version to the checks that matter.
cortex
cortex is the vulnerability-scanning engine, a dynamic scanner that runs detection templates and dedicated authorization tests against a target and reports evidence-backed findings. It reads nuclei-format templates (its own curated built-ins plus any external directory you point it at), matches on response status, body content, size, regexes and an expression language, and confirms blind vulnerabilities out-of-band. Beyond templates it has a purpose-built authorization engine that replays endpoints across multiple identities to catch broken access control. Every candidate is put through a correctness pass before it is reported, so findings are evidence-backed rather than "possible".
The template model
cortex runs a practical subset of the nuclei YAML template schema, so templates you already have will largely work. A template declares an id, some info (name, severity, description), and one or more HTTP requests:
id: git-config-exposure
info:
name: Exposed .git/config
severity: medium
http:
- method: GET
path:
- "{{BaseURL}}/.git/config"
matchers-condition: and
matchers:
- type: word
words: ["[core]", "repositoryformatversion"]
condition: and
- type: status
status: [200] Requests support method, a list of paths, headers, a body, and payloads, named value lists substituted into {{placeholders}} across the path, body and headers, which cortex expands into concrete requests (bounded for safety). Placeholders include {{BaseURL}}, {{RootURL}} and {{Hostname}}, your own payload names, and {{interactsh-url}} for out-of-band checks. External templates load from the directory set with templates_dir or the CORTEX_TEMPLATES_DIR environment variable, on top of the built-in set (which always runs). The built-ins cover common high-signal exposures: .git/config and .env files, private keys and cloud credentials, backup files, directory listings, phpinfo and server-status pages, debug stack traces, path traversal and blind SSRF.
Matchers
A matcher decides whether a response is a hit. cortex supports:
statusThe response status code is in a given list.wordThe response contains given substrings (combined with and / or).regexThe response matches given regular expressions.sizeThe response body length is one of given sizes.dslA boolean expression over the response (see below).Each matcher can target a part of the response (body, headers, or the whole raw response), can be combined with and / or via matchers-condition, and can be marked negative to require that something is absent.
The expression DSL
The dsl matcher evaluates a boolean expression against the response, for checks a plain word or status match cannot express, for example status_code == 200 && icontains(body, 'werkzeug'). It exposes response variables (status_code, body, headers, content_length), the usual comparison and logical operators, and helper functions including contains, icontains, startswith, endswith, contains_any, contains_all, regex, len, tolower and trim.
Out-of-band confirmation
Some vulnerabilities never show in the response: the target just makes an outbound request if it is vulnerable. cortex confirms these by embedding a unique out-of-band callback URL in the payload ({{interactsh-url}}), firing the check, and watching for the callback. A received callback is high-confidence proof the vulnerability fired: this is how cortex confirms blind SSRF, blind injection and similar flaws. cortex never reports a blind finding without a real callback, so there are no phantom out-of-band alerts. Out-of-band checks run when an out-of-band endpoint is configured for the engine; otherwise those templates are safely skipped.
Authorization testing (BOLA & BFLA)
cortex includes a dedicated authorization engine, separate from templates, that is hard to test any other way. It takes a set of endpoints and a set of identities (each a role plus its login), replays every endpoint as every identity, and compares the responses to find broken access control:
- BOLA / IDOR (object-level): two different users get the identical response body for an object-scoped endpoint (like
/invoices/3), meaning the object is not scoped to its owner. Reported critical. cortex spots object references both in the path (numeric ids, UUIDs, opaque handles) and in query parameters (?account_id=…, anything ending in_id), and is precise enough not to flag?page=2. - BFLA (function-level): a non-privileged identity reaches a privileged endpoint (
/admin,/manage,/internaland the like) that a privileged identity also reaches, proving it is a live function and not a 404. Reported high. - Broken authentication: an anonymous request reaches a protected endpoint that should require a login. Reported high.
Every finding carries a per-identity response matrix (who got which status and body size) as its evidence, and cortex guards against false positives from soft "please sign in" pages and login redirects.
Correctness and resilience
cortex is built to be trusted, not just noisy. Every candidate goes through a generate, detect, confirm, report pass: a response-based match is re-issued and must reproduce before it becomes a finding; an authorization candidate is re-probed as the accused identity; a blind check must produce a real callback. And it is resilient to rate limiting: on a 429 or 503 it backs off and retries rather than giving up, so a busy or WAF-fronted target does not quietly hide real vulnerabilities. The result is a low false-positive rate and findings you can put straight into a report.
Standalone usage
cortex --daemon & # start the engine (port 4445)
cortex scan https://example.com
# high/critical only, external templates, authenticated
cortex exec '{"operation":"scan","target":"https://example.com","severity":["high","critical"],"templates_dir":"/opt/nuclei-templates","auth":{"headers":{"Authorization":"Bearer <token>"}}}'
# authorization testing across two identities (BOLA / BFLA)
cortex exec '{"operation":"authz","endpoints":[{"method":"GET","url":"https://api.example.com/invoices/3"}],"identities":[{"role":"user_a","auth":{"cookies":"session=a..."}},{"role":"user_b","auth":{"cookies":"session=b..."}}]}' Key options
targetURL or host:port to scan.severityRestrict to given severities such as high or critical. Empty runs all.templates_dirExternal nuclei template directory, in addition to the built-ins.passive_onlyOnly run passive header checks; make no active requests.authHeaders and cookies for authenticated scanning.timeout_ms / follow_redirectsPer-request timeout and whether to follow redirects.Output
Each finding carries the target, severity, name, the template id that fired, the exact URL it matched at, a description and evidence, all marked confirmed. Passive checks also flag missing security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options). On the platform, cortex runs as a vulnerability-scan workflow against the surface the earlier engines mapped, uses scout's fingerprints and CVE leads to focus where it matters, and streams confirmed findings into your Findings explorer.
Workflows
A workflow is a .cfx script (Python) that drives a scan: it picks targets, calls the engines, and reports results. Run one locally with crossfyre run content-discovery.cfx url:https://example.com, or launch it from the dashboard to run across your nodes.
Workflows are crash-safe: the platform reserves an estimated cost at launch and reconciles the actual work at completion (reserve-then-reconcile). A dropped node or a crash resumes where it left off, and you are never charged twice or for work that did not run. Schedule a workflow to run on a recurring basis and have results pushed to you.
Findings
Results land in the Findings explorer: filter by severity, by active or passive discovery, and by host; search, paginate, and export the set to CSV, JSON or Markdown for your report or pipeline.
Notifications
Get alerted the moment a scan finishes or a finding lands, on the channel you already live in:
- Discord / Slack: add the Crossfyre bot, run
/login <email>and confirm with the code we email you (/verify). Subscribe a channel and alerts post there. - Email: enable email alerts in Settings → Notifications.
Manage channels and what triggers an alert from the Notifications page in your dashboard.
Teams & roles
Work as a crew in a Team Space: shared nodes, wordlists and findings, with role-based access (leaders manage members and billing; members run work). Seats are set by your plan. Invite teammates from Team Space and they join with the invite code emailed to them.
Wordlists
Crossfyre ships default wordlists and lets you upload your own, scoped to your team. Engines (mach, voyage) pull from them during active discovery. Manage them from the Wordlists page.
Billing & credits
Your plan sets your limits (nodes, concurrent workflows, storage, seats) and drops a monthly batch of credits into your balance. Credits are the metered fuel a scan burns, billed to the actual work done (reserve-then-reconcile). Credits never expire, and individual accounts pause new work at zero rather than overspending.
- Free: 1 node, 1 workflow at a time, starter credits.
- Pro: 10 nodes, 5 concurrent, 2,500 credits / month.
- Team: 50 nodes, 20 concurrent, 10 seats, 12,000 credits / month.
Top up credits any time. See pricing for the full breakdown.
Coming soon
On the roadmap, not yet available. These are in active development:
Extensions ecosystem & marketplace
Coming soonAn open marketplace of engines and integrations, built both by the Crossfyre team and the community. Discover, install and publish extensions that plug straight into your nodes and workflows, the same way the core mach / voyage / pulse engines do.
Mission Planner
Coming soonCompose multi-stage engagements visually: chain workflows, branch on findings, gate steps, and schedule the whole campaign across your fleet. A planner for end-to-end recon missions rather than one-off scans.
Valkyrie
Coming soonAn AI security agent that works alongside your scans: it gives intel and context on findings, and mutates vulnerability-scanning payloads to probe deeper and cut false positives. Not yet live.
Troubleshooting
Start with crossfyre doctor, which checks Docker, the database container, release-CDN reachability and your PATH.
- node init fails / engines won't start: Docker isn't installed or running. Install it (
get.docker.com) and start the daemon. - node shows offline: check the service with
crossfyre node status; bring it up withsudo crossfyre node up. - command not found after install: open a new shell so
/opt/crossfyre/binis on your PATH (crossfyreis also linked into/usr/local/bin). - dashboard says systems offline: the control plane is unreachable; the in-app status page shows what's down.
Support
Questions or stuck? Join the Discord, or email team@crossfyre.io. Found a security issue? Email team@crossfyre.io privately (see the toolchain repo's SECURITY policy).