phantomfleet: a synthetic osquery fleet you can query
We've open-sourced phantomfleet, a synthetic osquery fleet in a single Go binary. It enrolls simulated Linux, macOS and Windows hosts into osctrl, Fleet or any osquery TLS server and answers distributed queries with plausible, schema-shaped rows, so you can test, demo and write queries without touching real endpoints.
TL;DR: We've open-sourced phantomfleet, a synthetic osquery fleet in a single Go binary. It enrolls simulated Linux, macOS and Windows hosts into an osquery TLS server, such as osctrl or Fleet, and answers distributed queries with plausible rows shaped by the osquery schema. Use it to test anything built on osquery, write hunting queries against a mixed estate, or fill a demo console, without touching real endpoints. It's MIT licensed and v0.1.0 is out now.
Why we built it
Huntbase hunts on osquery, and anything you build on osquery hits the same awkward step sooner or later: you need hosts.
- Real hosts are slow to set up. A handful of VMs takes an afternoon and still doesn't look like an estate.
- They carry real data. That's the last thing you want in a demo, a screenshot or a CI log.
- They never have what you're hunting for. Writing a query for a vulnerable package or a persistence mechanism is guesswork when no host has it installed.
The load generators we found replay a fixed payload. That's fine for throughput, but useless the moment you ask a host a question. We wanted a fleet that you can query.
What it does
One process simulates a fleet of osquery hosts. Each one enrolls with an enroll secret and then runs the same loops osqueryd does: it polls for config, sends status and result logs, and picks up distributed queries. No osquery binary is involved.
Answers are built per host from three things:
- The osquery 5.24.0 table schema, embedded: 292 tables. A query against any core table returns rows with the right column names and types, even for tables nobody wrote seed data for.
- Seed data per platform. Linux, macOS and Windows each get believable
os_version,processes,users, packages, services, persistence and network tables. - Row counts per table.
processesreturns a few hundred rows;os_versionreturns one.
On top of that population, phantomfleet evaluates WHERE filters, count(*), LIMIT and scalar subqueries on each host's data. Ask a Windows host SELECT name, status FROM services WHERE name = 'WinDefend' and you get one plausible row back. --explain prints how each query was handled.
A nodes file pins hostnames, platforms, IPs, OS and hardware, and derives stable UUIDs. The same named hosts reappear after a restart, their identity columns agree across tables, and equality joins between their tables are real.
Example Corp: something to find
phantomfleet ships with Example Corp, a fictional 15-host company. Its hosts have pinned identities, some vulnerable software and an intrusion story baked into the seed data. Point a console at it and there's something to hunt for. This is the same estate we use for Huntbase demos.
phantomfleet \
--url https://osctrl-tls.example.com --env "$ENV_UUID" --secret-file ./enroll_secret \
--nodes-file examples/examplecorp/nodes.yaml \
--seeds-dir examples/examplecorp/seeds \
--state examplecorp_state.jsonWorks with osctrl, Fleet and standard servers
phantomfleet grew out of fake_news, the load-testing tool in osctrl, so osctrl works out of the box. With --flavor generic, it uses osquery's standard /api/v1/osquery/* paths and works with Fleet and other TLS servers. Any endpoint path can be overridden.
We tested 20 mixed nodes against osctrl v0.6.2 and Fleet v4.93.2: every node enrolled, every request succeeded, and every distributed query completed on all 20 hosts. Fleet support is basic for now; the README lists exactly which Fleet features it doesn't cover.
Light on resources
One process on an 8-core workstation, against a local osctrl, with default intervals:
| Nodes | Enrollment | Requests/s | Failed requests | CPU (cores) | Memory |
|---|---|---|---|---|---|
| 500 | 1 s | 44 | 0 | 0.03 | 47 MB |
| 2,000 | 3 s | 178 | 0 | 0.09 | 124 MB |
| 5,000 | 6 s | 444 | 0 | 0.23 | 278 MB |
Those runs carried no distributed queries; answering queries costs more. In practice the server, not phantomfleet, is the limit.
What it is and isn't
phantomfleet is synthetic. The rows are plausible, not true, so never treat its output as evidence about real systems. Its SQL support is pattern-based rather than a full engine: there's no GROUP BY or ORDER BY yet, and it doesn't run scheduled queries. The README sets out the full list.
It's also built to be easy to spot. Every request carries a phantomfleet/<version> User-Agent, fleets over 1,000 nodes need an explicit flag, and simulated hosts don't claim source addresses unless you ask them to. Please only point it at servers you run.
Get started
go install github.com/huntbase-io/phantomfleet@latest
# or
docker pull ghcr.io/huntbase-io/phantomfleet:latestThe README includes a five-minute quick start with a local Fleet or osctrl. Prebuilt binaries for Linux, macOS and Windows are on the release page.
Thanks
phantomfleet started life as osctrl's fake_news tool. Thank you to Javier Marcos and the osctrl contributors; the enrollment loops and statistics come from their work. The embedded table schema is adapted from the osquery project's published schema.
Issues and pull requests are very welcome, especially reports of servers or queries it gets wrong.