Skip to content

Bureau — Blue Team (defensive)

Election-Day-Watch

A civilian observer walks a polling place on election day with an RTL-SDR USB stick and a phone. Election-Day-Watch composes seven Bureau programs into a single FRE-902-compatible cassette per precinct, finalized at the close of polls and verifiable without trust in any single operator.

Posture: 🔵 Blue Team (defensive)   ·   Status: alpha

What it does

Election-Day-Watch is a meta-dossier – it does not add a new detection primitive. It orchestrates seven existing Bureau programs into a single signed envelope per precinct per election day:

  • Coordinated – bot-network detection across X / TikTok / Reddit / Telegram for precinct mentions.
  • Stingray – IMSI-catcher detection within 500 m of the polling place.
  • Celeste – GPS-spoof and time-tamper attestor on poll-worker tablets.
  • Dragnet – adversarial honesty probes against election-AI vendors.
  • Evidence-Locker – court-admissible exhibit chain-of-custody.
  • Press-Pipe – auto-route to ProPublica, DOJ Voting Rights, Verified Voting, Brennan Center.
  • Gossip – k-of-n peer co-signing so Pluck-Inc never holds singular keys.

The program collects red dots from each substrate and clusters cross-program co-fires. A Stingray equivocation that fires within 30 minutes of a Celeste time-tamper at the same precinct receives higher priority than either event in isolation, because correlated observations across independent channels are more difficult to attribute to coincidence. At the close of polls, the program finalizes a single FRE-902-compatible cassette per precinct, cross-attested by k-of-n peers. The dossier shape is the universal meta-dossier template inherited by Pharma-Mirror, Autonomy-Ledger, SCIF-Audit, and Frontline-Witness.

Who would use it

  • A civilian observer trained by Verified Voting walking a contested precinct with $40 of RTL-SDR + a phone.
  • An election-integrity researcher (J. Alex Halderman's lab, Brennan Center, Carter Center) running a co-signed observation network across N precincts.
  • An investigative journalist (ProPublica, AP, Bellingcat) who needs court-admissible exhibits within hours of polls closing.
  • A DOJ Voting Rights Section auditor establishing the cryptographic chain-of-custody for a contested precinct.
  • A civic-group transparency campaign tied to a midterm or general-election cycle.

What you'll need

  • The Pluck CLI installed (npm i -g @sizls/pluck-cli).
  • $40 of RTL-SDR (a USB-stick software-defined radio – RTL2832U is the standard chipset). Optional but high-leverage: it powers the Stingray substrate.
  • A phone with the Pluck mobile companion (or a laptop) running for 4+ hours at the precinct.
  • An operator key, plus enough peer relationships to reach the cosign quorum threshold (default 1-of-1 in the demo, 3-of-5 in production deployments). Peers that are commonly named: Verified Voting, Halderman's lab, Brennan Center, EFF, ProPublica.

Step-by-step

Shell
pluck bureau election-day-watch demo

The demo registers one precinct claim (ZZ-12, Cook County IL), four substrate-program red dots (a Stingray IMSI-catcher equivocation, a Celeste GPS-time-tamper, a Coordinated bot-cluster, and a Dragnet vendor probe-fail) all within a 30-minute window for the same precinct, and one peer cosign. The cross-program clustering math finds the four-distinct-program cluster, escalates the priority, and the dossier finalizes once the cosign quorum lands.

election-day-watch/demo: registering 1 precinct claim (ZZ-12, Cook County IL) + 4 substrate-program dots all within a 30-min window -> 1 cross-program incident escalates -> 1-of-1 GOSSIP cosign -> dossier finalizes.
[Bureau/ELECTION-DAY-WATCH] dossier=<digest>… precinct=ZZ-12 incidents=5 cosigns=1
election-day-watch/demo: cross-program-incident=<digest>… programs=celeste,coordinated,dragnet,stingray dots=4 severity=red
election-day-watch/demo: dossier=<digest>… precinct=ZZ-12 finalizedAt=<iso>
election-day-watch/demo: single-program-incidents=4 cross-program-incidents=1 dossiers=1

Production CLI (deferred):

Shell
# Initialize a precinct watch
pluck bureau election-day-watch init \
  --precinct-id=ZZ-12 \
  --jurisdiction="Cook County, IL" \
  --opens=2026-11-03T07:00:00-06:00 \
  --closes=2026-11-03T19:00:00-06:00 \
  --out=./election-day-bundle

# Walk the precinct collecting RF + GPS + bot-mention observations
pluck bureau election-day-watch walk \
  --precinct=ZZ-12 \
  --rtl-sdr \
  --duration=4h \
  --out=./election-day-bundle

# Finalize and ship the dossier (after polls close)
pluck bureau election-day-watch finalize \
  --precinct=ZZ-12 \
  --quorum=3-of-5 \
  --press-routes=propublica,doj-voting-rights,verified-voting,brennan \
  --out=./election-day-bundle

Run it yourself

Drop this into a Node 18+ project (npm install @sizls/pluck-bureau-election-day-watch @sizls/pluck-bureau-core tsx):

TypeScript
// index.ts
import {
  createElectionDayWatchSystem,
  digestCanonicalJson,
  fingerprintPrivateKey,
  signCanonicalBody,
} from "@sizls/pluck-bureau-election-day-watch";
import { dossierMerkleRoot } from "@sizls/pluck-bureau-election-day-watch";
import { generateOperatorKey } from "@sizls/pluck-bureau-core";

const flush = (n = 200) => new Promise<void>((r) => { let i = 0; const tick = () => (++i >= n ? r() : setImmediate(tick)); setImmediate(tick); });

async function main() {
  const op = generateOperatorKey();
  const opFp = fingerprintPrivateKey(op.privateKeyPem);
  const peer = generateOperatorKey();
  const peerFp = fingerprintPrivateKey(peer.privateKeyPem);

  const system = createElectionDayWatchSystem({
    signingKey: op.privateKeyPem,
    quorumK: 1,
    disablePausePoll: true,
    disableLogging: true,
  });

  const claimSkel = {
    schemaVersion: 1 as const, precinctId: "ZZ-12",
    jurisdiction: "Cook County, IL",
    opensAt: "2026-11-03T13:00:00.000Z", closesAt: "2026-11-04T01:00:00.000Z",
    expectedVoters: 1500, observedAt: "2026-11-03T12:55:00.000Z",
    operatorFingerprint: opFp,
  };
  const claimId = digestCanonicalJson(claimSkel);
  const claimSig = signCanonicalBody({ ...claimSkel, claimId }, op.privateKeyPem);

  const buildDot = (program: "stingray" | "celeste" | "coordinated" | "dragnet", observedAt: string, citation: string) => {
    const skel = { schemaVersion: 1 as const, program, precinctId: "ZZ-12", observedAt, severity: "red" as const, citation };

    return { ...skel, dotId: digestCanonicalJson(skel) };
  };

  try {
    system.claimPrecinct({ ...claimSkel, claimId, signature: claimSig.signature });
    system.observeDot(buildDot("stingray", "2026-11-03T19:14:00.000Z", "rekor:1"));
    system.observeDot(buildDot("celeste", "2026-11-03T19:25:00.000Z", "rekor:2"));
    system.observeDot(buildDot("coordinated", "2026-11-03T19:35:00.000Z", "rekor:3"));
    system.observeDot(buildDot("dragnet", "2026-11-03T19:42:00.000Z", "rekor:4"));
    await flush();

    // Cosign the current Merkle root over the precinct's incidents.
    const incidents = system.facts.incidents().filter((i) => i.precinctId === "ZZ-12");
    const dossierRoot = dossierMerkleRoot(incidents, []);
    const cosignSkel = { schemaVersion: 1 as const, precinctId: "ZZ-12", dossierRoot, peerFingerprint: peerFp, observedAt: "2026-11-04T00:30:00.000Z" };
    const cosignId = digestCanonicalJson(cosignSkel);
    const cosignSig = signCanonicalBody({ ...cosignSkel, cosignId }, peer.privateKeyPem);
    system.receiveCosign({ ...cosignSkel, cosignId, signature: cosignSig.signature });
    await flush();

    for (const d of system.facts.dossiers()) {
      console.log(`dossier ${d.dossierId.slice(0, 16)}… precinct=${d.precinctId} incidents=${d.incidentIds.length} cosigns=${d.cosignIds.length}`);
    }
  } finally {
    await system.shutdown();
  }
}

main().catch((err) => { console.error(err); process.exit(1); });

Run with tsx index.ts. Expected output:

dossier <digest>… precinct=ZZ-12 incidents=5 cosigns=1

▶ Open in StackBlitz – runs in your browser, no install required.

What you get

A single Dossier envelope per precinct per election day, Merkle-rolled over the precinct claim + every red-dot incident + every cross-program-incident + every peer cosign. All Rekor-anchored.

Three observation classes that compose into the dossier:

  • single-program-incident – any of the seven substrate programs emits a red dot for this precinct.
  • cross-program-incident – 2+ DISTINCT programs co-fire within a 30-minute window for the same precinct. Higher-priority cassette + auto-route to DOJ.
  • dossier-finalized – end-of-day Merkle-rolled signed envelope with k-of-n Gossip cosign quorum reached.

The cassette is FRE-902 self-authenticating: a court can verify the chain-of-custody without testimony from any single operator.

What it can't do

  • Real subscription to upstream substrate system instances is deferred. The alpha accepts dot JSON as input. Production wiring will subscribe directly to running Coordinated / Stingray / Celeste / Dragnet / Evidence-Locker / Press-Pipe / Gossip systems.
  • Production CLI (init / walk / finalize / verify) is deferred – alpha exposes only demo.
  • Inside-the-tabulator integrity is out of scope. Ballot-Chain (a separate program, deferred for press-cycle launch with Verified Voting / Halderman) handles that.
  • Voter-roll purging detection is a separate program, deferred.
  • No audio/video capture in alpha – polling-place harassment by humans is not detected. LiDAR-Whisper + Acoustic-Scribe could compose later.
  • Misinformation NOT amplified by detectable bot networks is invisible to the Coordinated substrate.
  • Cross-program clustering requires 2+ DISTINCT substrate programs – two Stingray equivocations 10 min apart do NOT cluster. The program looks for breadth across substrates, not depth within one.
  • Studio routes deferred.

A real-world example

At 2:14 PM on November 3rd, 2026, a civilian observer trained by Verified Voting is parked outside precinct ZZ-12 in Cook County, IL, running an RTL-SDR plugged into her phone. The phone records an IMSI-catcher equivocation: a cell tower advertising different identifiers to two adjacent geohashes within a 90-second window. The Stingray substrate signs a SuspectStingray body and emits a red PrecinctDot for ZZ-12.

At 2:31 PM, a poll worker's tablet reports a GPS time fix that contradicts NTP by 4.2 ms, outside Celeste's 1 ms tolerance. A red dot is emitted. Election-Day-Watch's clustering math combines them: 17 minutes apart, inside the 30-minute window, two distinct substrate programs at the same precinct. The cross-program incident escalates at priority 90.

At 2:42 PM, a Dragnet probe-pack run against the state-tabulation vendor's API records contradictory responses to two distinct probes. A fourth red dot. At 2:45 PM, Coordinated's social listener detects a bot cluster amplifying false claims about ZZ-12 ("voter intimidation reported, polls closing early"). The cross-program incident now names four substrate programs co-firing within a 31-minute span.

At 8:00 PM, polls close. A Halderman-lab node, a Verified Voting node, and a Brennan Center node each issue a GossipCosign over the precinct's dossier root. With 3-of-5 quorum reached, the dossier finalizes – Merkle-rolled over the precinct claim, 4 cross-program incidents, and 3 cosigns – and an .intoto.jsonl cassette is routed to ProPublica and DOJ Voting Rights at 11:47 PM. ProPublica receives verifiable exhibits supporting the next morning's reporting; the DOJ Voting Rights Section receives an FRE-902-compatible chain of custody.


For developers

Predicate URIs

URIWhat it attests
https://pluck.run/ElectionDayWatch.PrecinctClaim/v1Operator's signed declaration that they're watching precinct X on election day Y.
https://pluck.run/ElectionDayWatch.IncidentExhibit/v1A red dot from any of the 7 substrate programs, OR a cross-program co-fire with multi-program citations.
https://pluck.run/ElectionDayWatch.Dossier/v1The end-of-day Merkle-rolled signed envelope containing all incidents + cosigns for that precinct.

Programs composed

  • Coordinated – bot-network detection.
  • Stingray – IMSI-catcher detection.
  • Celeste – GPS spoof + time-tamper.
  • Dragnet – adversarial honesty probes.
  • Evidence-Locker – court-admissible chain-of-custody.
  • Press-Pipe – auto-route to journalists / DOJ.
  • Gossip – k-of-n peer cosign.
  • Pluck core's DSSE in-toto envelopes + Sigstore Rekor client.
  • Directive's facts/constraints/resolvers.

Threat model + adversary

Adversaries: state-actor election interference, vendor lies about election-AI honesty, IMSI-catchers near polling places, bot networks amplifying misinformation, retroactive observation tampering. Defenses: cross-program clustering forces 2+ distinct substrates to fire (raising fabrication cost), k-of-n Gossip cosign distributes trust, FRE-902 self-authentication makes the cassette court-admissible without single-operator testimony.

False-positive resistance: green substrate dots never cluster – legit cell-tower handover during shift change, organic news-cycle convergence, and vendor maintenance windows all stay inside their respective substrate gates and never cross into Election-Day-Watch. The 30-min window is INCLUSIVE – exactly 30 min apart fires, 31+ min apart does NOT. Finalize is paused-gated – a paused → resumed flow re-verifies cosign quorum still holds before publishing.

What's stubbed (alpha)

  • Real subscription to upstream Coordinated / Stingray / Celeste / Dragnet / Evidence-Locker / Press-Pipe / Gossip system instances deferred – alpha accepts dot JSON as input.
  • Production CLI (init / walk / finalize / verify) deferred.
  • Studio routes deferred.

Verify a published cassette

Shell
pluck bureau election-day-watch verify ./bundle/dossier.intoto.jsonl
cosign verify-blob --key <pubkey.pem> --signature <sig> \
  --type https://pluck.run/ElectionDayWatch.Dossier/v1 <body.json>

See also

Edit this page on GitHub
Previous
Cherenkov-Witness

Ready to build?

Install Pluck and follow the Quick Start guide to wire MCP-first data pipelines into your agents and fleets in minutes.

Get started →