- Docs
- Bureau — Red & Blue (dual-use)
- Acoustic-Scribe
Bureau — Red & Blue (dual-use)
Acoustic-Scribe
Mechanical keyboards leak typing cadence acoustically, and modern silicon emits coil whine that encodes workload state. Acoustic-Scribe captures both signal classes and signs each capture with a tamper-evident chain of custody.
Posture: 🟣 Red & Blue (dual-use) · Status: alpha
What it does
A microphone in a room is, to a determined adversary, a side-channel surveillance device. Mechanical keyboards leak typing cadence – the inter-key timings encode characters with surprising reliability. CPU and GPU coil whine – the high-pitched electrical noise modern silicon emits under load – encodes whether a workload is idle, attention-heavy, GEMM-heavy (matrix multiplication, the hot loop in deep learning), or one of several other classes. If a state-level adversary has put a microphone in your office, the audio is not just an audio file; it's a partial transcript of what you typed and a partial trace of what your machine was doing.
Acoustic-Scribe is the Bureau program for both halves of that fact. The red-team angle: recover what someone typed from a Zoom call that recorded their microphone, classify what their GPU was doing while the audio was capturing. The blue-team angle: prove your secure room (a SCIF, a sealed lab, a journalist's safe house) didn't have an ultrasonic exfil channel running – capture the room's acoustic baseline, sign it, characterize the recorder, and bind the chain of custody so an opposing expert cannot dispute that the audio they're looking at is exactly what was recorded by the device they think recorded it.
The unit of observation is a signed capture that binds the audio's sha256, sample rate, channels, duration, optional GPS location, the operator key that signed it, and a device fingerprint – a 32-band frequency response, noise floor, and harmonic distortion characterization of the recorder. Swap the recorder mid-investigation: the device fingerprint discontinuity is detectable. Re-encode the audio: the audio hash breaks.
Who would use it
- A digital-forensics expert defending a corporate-espionage case where the prosecution claims a microphone recording captures the defendant typing trade secrets.
- A SCIF (Sensitive Compartmented Information Facility) operator doing periodic acoustic sweeps to prove no ultrasonic exfil channel was running.
- A journalist sweeping a meeting room before an interview with a high-risk source.
- An academic lab studying LLM workload acoustic signatures and needing reproducible signed observations.
- A red-team consultant assessing an executive's home office for side-channel exposure.
What you'll need
- Node.js 20 or newer.
- The Pluck CLI:
npm i -g @sizls/pluck-bureau-cli. - An operator key – a directory containing your signing identity.
- A
.wavfile. Acoustic-Scribe is offline-only for now (live streaming and native device enumeration are on the roadmap). You decode an existing.wav, sign the bytes. - For coil-whine workload classification: an ultrasonic-capable capture chain. Most consumer microphones cap at 20 kHz; the classifier looks at peaks in the 50–500 kHz band and needs a specialized mic with a sample rate around 1 MHz.
- For production-grade workload classification: your own characterized hardware baseline. The shipped baseline corpus is synthetic.
Step-by-step
Characterize the recorder once at intake. The fingerprint binds frequency response, noise floor, harmonic distortion.
pluck bureau acoustic-scribe fingerprint-device ./intake-mic.wav \
--keys ./keys \
--out ./mic-fp.json
Output:
acoustic-scribe/fingerprint-device: fingerprint=4f2c1d3e7a8b9012... ./mic-fp.json
Sign the capture. capture refuses without a device fingerprint. Captures are private by default – --notarize --accept-public is required to anchor on the public log.
pluck bureau acoustic-scribe capture ./prosecution.wav \
--keys ./keys \
--device-fingerprint ./mic-fp.json \
--location 37.7749,-122.4194 \
--out ./bundle
Output:
acoustic-scribe/capture: captureId=8f3a...b21c envelopeHash=4f2c1d... (not notarized)
Run keystroke recovery on the signed capture. Acoustic-Scribe detects keystroke-shaped acoustic transients, computes inter-keystroke intervals, and produces a per-keystroke candidate-character distribution.
pluck bureau acoustic-scribe keystroke-scan ./bundle/<id>.bundle.json \
--wav ./prosecution.wav \
--keys ./keys \
--out ./bundle
Classify CPU/GPU coil-whine workload, optionally diffing against a baseline you captured earlier. A swap classification exits non-zero, so a CI pipeline can catch unexpected hardware changes.
pluck bureau acoustic-scribe workload-scan ./bundle/<id>.bundle.json \
--wav ./prosecution.wav \
--keys ./keys \
--baseline ./bundle/<baseline-id>.bundle.json \
--baseline-wav ./baseline.wav \
--out ./bundle
Verify a bundle (cosign-friendly):
pluck bureau acoustic-scribe verify ./bundle/<id>.bundle.json --keys ./keys
Run it yourself
Drop this into a Node 18+ project (npm install @sizls/pluck-bureau-acoustic-scribe @sizls/pluck-bureau-core tsx). This skips the WAV decode and feeds synthetic samples directly to the build/attest pipeline:
// index.ts
import { createAcousticScribeSystem } from "@sizls/pluck-bureau-acoustic-scribe";
import { generateOperatorKey } from "@sizls/pluck-bureau-core";
async function main() {
const operator = generateOperatorKey();
const sampleRateHz = 44_100;
const samples = synthesize(sampleRateHz, 0.5);
const system = createAcousticScribeSystem({
signingKey: operator.privateKeyPem,
disablePausePoll: true,
disableLogging: true,
});
try {
// 1) One-time device characterization.
const fp = system.characterizeDevice({ samples, sampleRateHz });
console.log(`device fingerprint=${fp.fingerprint.slice(0, 16)}…`);
// 2) Observe + attest a signed capture (binds the device fingerprint).
const { capture, attestation } = await system.observeCapture({
samples,
sampleRateHz,
channels: 1,
durationMs: (samples.length / sampleRateHz) * 1000,
capturedAt: new Date().toISOString(),
attest: true,
});
console.log(`captureId=${capture.captureId.slice(0, 16)}… envelopeHash=${attestation?.envelopeHash.slice(0, 16)}…`);
// 3) Verify the signature locally.
console.log(`signature verified=${system.verify(capture)}`);
// 4) Run keystroke recovery + sign the candidate.
const { candidate, signed } = await system.scanKeystroke({ samples, sampleRateHz, sign: true });
console.log(`keystrokes=${candidate.candidates.length} confidence=${candidate.confidence} envelopeHash=${signed?.envelopeHash.slice(0, 16)}…`);
console.log(`captures=${system.facts.captures().length} candidates=${system.facts.candidates().length}`);
} finally {
await system.shutdown();
}
}
function synthesize(sampleRateHz: number, durationSec: number): Float32Array {
const len = Math.floor(sampleRateHz * durationSec);
const out = new Float32Array(len);
let seed = 0x9e3779b9;
for (let i = 0; i < len; i++) {
seed = (seed * 1664525 + 1013904223) >>> 0;
out[i] = ((seed & 0xffff) / 0xffff - 0.5) * 0.05;
}
// Inject 4 click transients.
for (const idx of [Math.floor(len * 0.1), Math.floor(len * 0.3), Math.floor(len * 0.5), Math.floor(len * 0.7)]) {
for (let i = 0; i < 80 && idx + i < len; i++) out[idx + i] += 0.6 * Math.exp(-i / 12);
}
return out;
}
main().catch((err) => { console.error(err); process.exit(1); });
Run with tsx index.ts. Expected output:
device fingerprint=…
captureId=… envelopeHash=…
signature verified=true
keystrokes=4 confidence=…
captures=1 candidates=1
Open in StackBlitz – runs in your browser, no install required.
What you get
You get a signed cassette that pins exactly which bytes were recorded, by which device, by which operator, at which time. Anyone in possession of the bundle can re-verify the audio hash, re-verify the device fingerprint hasn't drifted, re-verify the operator signature. If you ran keystroke-scan, the candidate-character distributions are themselves signed cassettes – independent analysts can re-run their own keystroke-recovery passes against the same audio and compare conclusions, but they cannot dispute that the audio is exactly what your device recorded.
If you're on the blue-team side, the signed baseline plus periodic re-fingerprints is your proof to a regulator, a customer, or a court that the room you say is acoustically clean is actually acoustically clean. A device-fingerprint discontinuity is a lead – a swap of the recorder, a new device in the room, a hardware tamper – that a recurring sweep will catch.
What it can't do
- Keystroke recovery is research-grade. Acoustic-Scribe promises tamper-evident chain of custody for the observation, not 95% character recovery. The candidate-character distributions are top-N sorted; the analyst decides admissibility.
- The shipped coil-whine baseline corpus is synthetic. Operators must characterize their own hardware to get production-grade workload classifications. A rack you have not characterized will produce garbage classifications.
- Coil-whine classification needs ultrasonic capture. Consumer mics will not see the 50–500 kHz peaks the classifier looks for.
- Captures are private by default. Once notarized, the capture id is in the public log forever.
- The audio body is not in the bundle.
audioHashcontent-addresses the canonical PCM body; the operator must keep the.wavaccessible to verifiers. Lose the.wavand the bundle still verifies, but no analyst can re-derive the analysis. - Offline-only for now. Live microphone streaming and native audio device enumeration are on the roadmap.
A real-world example
A digital-forensics expert is retained by a defense team in a corporate-espionage case. The prosecution's evidence includes a microphone recording that the prosecution argues captures the defendant typing trade-secret content. The defense expert obtains the original .wav file, but the chain of custody is incomplete – three different machines handled the file before it reached discovery. The expert characterizes the intake recorder, signs the audio against the intake fingerprint, then runs keystroke-scan and workload-scan. The candidate-character distributions for each detected keystroke are well below the prosecution's claimed character recovery: the audio's noise floor, signed into the device fingerprint at intake, is too high to support the claim. The expert produces the signed bundle in court. The opposing expert can dispute the analysis itself, but cannot dispute the device fingerprint, audio hash, captured-at timestamp, or operator signature, all of which are anchored to the same evidentiary standard the prosecution claims to apply.
For developers
Predicate URIs
| Predicate URI | What it attests |
|---|---|
https://pluck.run/AcousticScribe.Capture/v1 | One audio capture: PCM hash, sample rate, channels, duration, optional location, device fingerprint, operator. |
https://pluck.run/AcousticScribe.KeystrokeCandidate/v1 | A keystroke-recovery scan: inter-keystroke intervals, per-keystroke candidate-character distribution, confidence. |
https://pluck.run/AcousticScribe.CoilWhineWorkload/v1 | A coil-whine workload classification: workload class, narrowband peaks, optional baseline diff (`stable |
https://pluck.run/AcousticScribe.DeviceFingerprint/v1 | A recorder characterization: 32-band frequency response, noise floor RMS dBFS, harmonic distortion. |
Programs composed
- Pluck FFT / MFCC / Goertzel DSP – the underlying signal-processing primitives.
- Pluck cassette format – audio bodies are NOT inlined; sha256-addressed
.wavsidecars sit next to the cassette. - DSSE / in-toto – bundles are signed in cosign-compatible envelope format.
- Fingerprint –
workload-scanbaseline-diff classifications use the samestable | drifted | swaptaxonomy. - Custody – parallel chain-of-custody primitive for browser-captured AI conversations.
- Sigstore Rekor – captures and analyses can optionally be notarized.
Threat model
- Captures private by default;
--notarize --accept-publicrequired for public anchor. - Device-fingerprint discontinuity is detectable.
- A
swapworkload-scan classification exits non-zero. - Audio body not in the bundle – operator keeps
.wavaccessible.
Verify a published cassette
pluck bureau acoustic-scribe verify ./bundle/<id>.bundle.json --keys ./keys
cosign verify-blob \
--key <pubkey.pem> \
--signature <sig> \
--type https://pluck.run/AcousticScribe.Capture/v1 \
<body.json>
See also
- Custody – chain-of-custody for browser-captured AI conversations.
- Fingerprint – model-identity baseline diffs reuse the same
stable | drifted | swaptaxonomy. - Bounty – file an Acoustic-Scribe finding against a vendor's bounty program.
- Bureau Foundations
- Threat Model
- Verify a dossier