- Docs
- Bureau — Blue Team (defensive)
- Cosmos
Bureau — Blue Team (defensive)
Cosmos
Aircraft (ACARS), ships (AIS), satellite phones (Iridium), and satellite messaging (Inmarsat) all broadcast their identity over radio. The radio itself has a fingerprint – frequency offset, IQ imbalance, ramp shape – that the legitimate device can't change. Cosmos catches when a "ship" or "plane" is broadcasting an identity its physical radio doesn't match.
Posture: 🔵 Blue Team (defensive) · Status: alpha
What it does
Four radio systems sit at Cosmos's heart. ACARS (Aircraft Communications Addressing and Reporting System) is short text messages between airliners and ground/satellite – every message claims to come from a specific aircraft via its ICAO24 (a unique 24-bit hex address baked into every aircraft's transponder). AIS (Automatic Identification System) is the maritime equivalent – every ship over a certain size broadcasts its position every few seconds, claiming a 9-digit MMSI (Maritime Mobile Service Identity). Iridium SBD (Short Burst Data) is satellite-phone messaging keyed off IMEI (the modem's hardware serial). Inmarsat classic / BGAN is older satellite messaging keyed off a vendor-assigned SatID.
The claimed identity in any of these messages is just bytes – software-defined radios make impersonation trivial. Ghost flights (aircraft broadcasting under stolen ICAO24s for sanctions evasion or smuggling) and ghost ships (vessels broadcasting AIS positions hundreds of miles from their real location for sanctions or insurance fraud) are routine. What an attacker cannot easily fake is the transmitter's physical-layer fingerprint – the analog distortions every radio's electronics leave on the signal. Cosmos receivers decode each message and also extract the transmitter's fingerprint. For each (kind, claimed-identity) pair, Cosmos maintains a centroid – the running average and standard deviation of every fingerprint observed under that identity. When an observation claims an identity but its fingerprint deviates 3+ standard deviations from the centroid that identity has built up, and two-or-more distinct receivers corroborate the deviation, Cosmos emits a GhostFleet proof.
Who would use it
- A maritime sanctions analyst at a think tank documenting suspected ghost-ship dark-fleet activity in the Strait of Malacca.
- An airline operations center wanting independent corroboration of its fleet's ACARS broadcasts during a disputed flight.
- An insurance adjuster investigating a "stolen container ship" claim where the vessel's AIS mysteriously rerouted.
- A regulator (FAA / IMO) building public datasets of suspected identity spoofing for enforcement.
- A journalist investigating sanctions-busting transits – the bundles are independently verifiable observations.
What you'll need
- The Pluck CLI installed (
pnpm add -g @sizls/pluck-cli). - A receiver capable of decoding the relevant satcom band. ACARS / AIS is in VHF and decoded with a $35 RTL-SDR Blog v4 plus open-source decoders (
acarsdec,rtl-ais). Iridium / Inmarsat require an L-band antenna and either a HackRF (~$330) or a USRP B200mini. - Two or more distinct receivers in the same window, each running a byte-identical DSP version (so cross-receiver fingerprints compare cleanly).
- An operator key per receiver host.
Step-by-step
The alpha runs an in-memory demo on synthetic ACARS messages. Production decoders (bit-accurate ACARS / AIS / Iridium / Inmarsat decoder bridges) are operator-supplied; the alpha consumes pre-decoded JSON. To see the engine work today:
pluck bureau cosmos demo
You'll see something like:
cosmos/demo: ingesting 6 synthetic ACARS messages (4 legit, 2 ghost)...
cosmos/demo: ghost candidates emitted = 1
cosmos/demo: candidate ghostId=85141fa25ef6… kind=acars identity=ICAO24:A12345 deviation=2933.36sigma
[Bureau/Cosmos] ghost confirmed kind=acars identity=ICAO24:A12345 ghostId=85141fa25ef6… deviation=2933.36sigma
cosmos/demo: ghosts notarized = 1
The synthesized stream is six ACARS messages claiming ICAO24:A12345. The first four come from two receivers and have nearly identical TX fingerprints (residual frequency offset ~12 Hz, jitter ~0.4 ppm) – these establish the centroid. The last two have the same claimed ICAO24 but a wildly anomalous fingerprint (frequency offset 850–870 Hz, jitter 4.5–4.7 ppm). Two distinct receivers corroborate, satisfying the k-of-n quorum. Cosmos emits the ghost proof.
Run it yourself
Drop this into a Node 18+ project (npm install @sizls/pluck-bureau-cosmos @sizls/pluck-bureau-core tsx):
// index.ts
import { createHash } from "node:crypto";
import {
createCosmosSystem,
fingerprintPrivateKey,
signCanonicalBody,
} from "@sizls/pluck-bureau-cosmos";
import { generateOperatorKey } from "@sizls/pluck-bureau-core";
const flush = () => new Promise((r) => setImmediate(r));
const digest = (s: string) => createHash("sha256").update(s).digest("hex");
function signMsg(body: any, key: string) {
const skel = { schemaVersion: 1, ...body };
const messageId = createHash("sha256").update(JSON.stringify(skel)).digest("hex");
const signed = signCanonicalBody({ ...skel, messageId }, key);
return { ...skel, messageId, signature: signed.signature };
}
async function main() {
const operator = generateOperatorKey();
const rxAKey = (generateOperatorKey()).privateKeyPem;
const rxBKey = (generateOperatorKey()).privateKeyPem;
const rxAFp = fingerprintPrivateKey(rxAKey);
const rxBFp = fingerprintPrivateKey(rxBKey);
const cosmos = createCosmosSystem({
signingKey: operator.privateKeyPem,
quorumThreshold: { required: 1, outOf: 1 },
disablePausePoll: true,
disableLogging: true,
});
const baseFp = {
schemaVersion: 1 as const,
iqImbalanceAmp: 1.005, iqImbalancePhaseRad: 0.012,
phaseNoiseMask: [4, 5, 6, 6, 5, 4, 3, 3],
txRampShape: [10, 35, 65, 90, 100, 100, 100, 100],
};
const fp = (freq: number, jitter: number) => ({
...baseFp, frequencyOffsetHz: freq, symbolTimingJitterPpm: jitter,
});
try {
// 4 legit messages – establish centroid for ICAO24:A12345.
const legit = [
[rxAKey, rxAFp, 12.0, 0.40], [rxBKey, rxBFp, 12.4, 0.42],
[rxAKey, rxAFp, 11.7, 0.39], [rxBKey, rxBFp, 12.2, 0.43],
];
for (let i = 0; i < legit.length; i++) {
const [key, rxFp, freq, jit] = legit[i] as [string, string, number, number];
cosmos.observeMessage(signMsg({
kind: "acars", identity: "ICAO24:A12345",
observedAt: `2026-04-26T12:00:0${i}Z`,
payloadDigest: digest(`legit-${i}`),
txFingerprint: fp(freq, jit), receiverFingerprint: rxFp,
}, key));
}
// 2 ghost messages – wildly anomalous fingerprint, both receivers see it.
cosmos.observeMessage(signMsg({
kind: "acars", identity: "ICAO24:A12345",
observedAt: "2026-04-26T12:00:04Z",
payloadDigest: digest("ghost-1"),
txFingerprint: fp(862.0, 4.5), receiverFingerprint: rxAFp,
}, rxAKey));
cosmos.observeMessage(signMsg({
kind: "acars", identity: "ICAO24:A12345",
observedAt: "2026-04-26T12:00:05Z",
payloadDigest: digest("ghost-2"),
txFingerprint: fp(882.0, 4.7), receiverFingerprint: rxBFp,
}, rxBKey));
await flush();
const candidates = cosmos.facts.ghostCandidates();
console.log(`ghost candidates: ${candidates.length}`);
const witnessFp = fingerprintPrivateKey(operator.privateKeyPem);
cosmos.receiveWitness({
schemaVersion: 1, fingerprint: witnessFp, ghostId: candidates[0]!.ghostId,
signature: createHash("sha256").update(`${witnessFp}|${candidates[0]!.ghostId}`).digest("base64"),
signedAt: new Date().toISOString(),
});
await flush();
const confirmed = cosmos.facts.confirmedGhosts();
console.log(`ghosts notarized: ${confirmed.length}`);
} finally {
await cosmos.shutdown();
}
}
main().catch((err) => { console.error(err); process.exit(1); });
Run with tsx index.ts. Expected output:
ghost candidates: 1
ghosts notarized: 1
▶ Open in StackBlitz – runs in your browser, no install required.
What you get
A signed Cosmos.GhostFleet cassette containing:
- The legitimate baseline observations (anonymized down to fingerprint vectors only).
- The deviating observations.
- The deviation in standard deviations (sigma).
- The kind discriminator (acars / ais / iridium / inmarsat).
- A Rekor uuid pinning the bundle to the public log.
Cosmos never carries the raw message body – only the payload digest. The proof says "the transmitter is not who they claim to be," not "here is the message they sent." Operator-side cassettes can retain bodies for legal/regulatory needs; the public predicate carries provenance only.
What it can't do
- A sophisticated adversary swapping the original transmitter (insider attack, supply-chain interdiction) can rebuild the centroid fraudulently. The k-of-n distinct-receiver gate raises the bar; widely-distributed receivers raise it further.
- Legitimate transmitter replacement (a ship installs a new AIS unit) creates a multi-day transient where ghost candidates fire on the new unit. Operators should expect re-baselining when known equipment changes.
- Receiver-side DSP must be byte-identical across receivers for fingerprints to compare. Mixed DSP versions = false divergence.
- Cosmos doesn't localize ghosts geometrically. It says "the transmitter isn't who they claim," not "the transmitter is at lat/lon X." Geometric localization needs more receivers than the alpha targets.
A real-world example
An NGO tracking sanctions evasion runs three Iridium-SBD receivers in different countries. For two months, they baseline the fingerprints of every IMEI broadcasting on a watchlist. One Saturday, an IMEI on the watchlist starts transmitting from what claims to be the Pacific – but the fingerprint is utterly different from its 60-day centroid, and all three receivers see the deviation. Cosmos signs the ghost proof. The NGO publishes the Rekor uuid alongside their analysis. Six weeks later, an investigative news outlet uses the cassette as the cryptographic backbone of a piece on a sanctioned shipping company that handed its terminal to a different vessel. Reviewers at three universities independently re-run the verification and reach the same conclusion.
For developers
Predicate URIs
| URI | What it attests |
|---|---|
https://pluck.run/Cosmos.ACARS/v1 | One receiver decoded an ACARS message claiming this ICAO24 at this wall-time, with this TX fingerprint and this payload digest. |
https://pluck.run/Cosmos.AIS/v1 | One receiver decoded an AIS broadcast claiming this MMSI. |
https://pluck.run/Cosmos.Iridium/v1 | One receiver decoded an Iridium SBD message claiming this IMEI. |
https://pluck.run/Cosmos.Inmarsat/v1 | One receiver decoded an Inmarsat message claiming this SatID. |
https://pluck.run/Cosmos.GhostFleet/v1 | Two-or-more distinct receivers observed messages claiming the same identity with TX fingerprints deviating ≥ 3 sigma from the centroid. |
Programs composed
- Raven – IQ-tile substrate (the satcom-band tiles the receiver's RF was extracted from)
- Pluck core's DSSE in-toto envelopes + Sigstore Rekor client
- Directive derivations/constraints/resolvers – per-identity centroid is a derivation; deviation detector + quorum gate are constraints; witness ingestion uses batched resolvers
- Whistle-style ephemeral witness keys for ghost co-signature
- Bureau core's pause kill-switch (Cosmos pauses if
cosmosOR upstreamravenis paused)
Threat model + adversary
An attacker who also swaps the original transmitter (insider/supply-chain) can rebuild the centroid fraudulently – k-of-n distinct-receiver gate is the primary defense. Legitimate transmitter replacement creates multi-day transients; operators should expect re-baselining. Receiver-side DSP must be byte-identical across receivers – mixed versions cause false divergence. Cosmos does not geometrically localize. Receiver itself is trusted; defense is two-or-more distinct receivers.
Verify a published cassette
pluck bureau verify <bundle-dir>
cosign verify-blob \
--key <pubkey.pem> \
--signature <signature.sig> \
--type https://pluck.run/Cosmos.GhostFleet/v1 \
<body.json>
See also
- Bureau Foundations
- Threat Model
- Verify a dossier
- Raven – RF substrate Cosmos rides on
- Celeste – sibling RF identity / time attestor