Plates as an internal correlation key only: HMAC match key from spoken plates, 2h TTL, no search/watchlist/display (CISO ruled) #248
Open
opened 2026-10-05 02:18:54 -04:00 by logan
·
1 comment
No Branch/Tag Specified
Labels
Clear labels
area/ai-pipeline
area/auth
area/billing
area/correlation
area/discord
area/frontend
area/governance
area/growth
area/infra-deploy
area/legal
area/map
area/mqtt
area/node-ota
area/osint
area/recording-audio
area/sdr-radio
area/transcription
board
board:agenda
minutes:draft
minutes:final
role:ceo
role:cfo
role:cio
role:ciso
role:cmo
role:coo
role:cto
Scene/entity extraction, summarizer, AI providers, embeddings
AuthN/Z, roles, tenancy/org scoping, API keys, credentials
Plans, entitlements, AI spend accounting and caps
Incident correlation, linking, lifecycle, severity, corr_debug
Discord bot, radio bots, voice streams, bot tokens, webhooks
Web UI: Next.js app and the edge-node local dashboard
Board process, minutes, unattended runner, agent tooling
Go-to-market, outreach, cold email, discovery conversations
CI/CD, VM, registry, Firestore rules/indexes deploy, Terraform/Caddy
ToS, retention, licences/DAAs, CAN-SPAM, E&O, name suppression
Live map: pins, paths, tiles, legend, geocoding
Node-C2 MQTT, checkins, commands, node sweeper, dynsec
Node install, enrollment, OTA updater, image skew
External feeds: cameras, 511, ADS-B, AIS, outages, and their licences
Call capture, boundaries, PulseAudio, ring buffer, trimming, attribution
OP25 decode, SDR hardware/tuning, conventional, second SDR, mesh radio
STT, transcript correction, place verification, name accuracy
Board process: minutes and board-filed items
Needs board scoping at the next sitting; CEO removes the label once scoped
One exec pre-read, closed by CEO
CEO ruling; open until enacted
Filed by the CEO
Filed by the CFO
Filed by the CIO
Filed by the CISO
Filed by the CMO
Filed by the COO
Filed by the CTO
kind
behavior-bug
Wrong product behavior; the instrument reads it correctly
kind
measurement-bug
Corrupts the recording/instrument (debug data, evals, health checks), not behavior
priority
p0
Drop everything: prod broken, live exposure, or owner-ruled P0
priority
p1
Next up: blocks a gate, a customer, or core incident quality
priority
p2
Normal: real defect or planned work, schedule in turn
priority
p3
Low: minor, cosmetic, or someday
status
blocked
Waiting on an external party, spend, or another issue
status
in-progress
Branch or PR open against it
status
needs-decision
Waiting on an owner or board ruling before work
status
needs-info
Root cause or repro unknown; needs a reading first
type
bug
Something built is wrong: code, data, or a measurement
type
chore
Maintenance, refactor, tests, pinning, cleanup: no user-visible change
type
decision
A ruling to make or record: board minutes, owner calls, legal reviews
type
docs
Docs, runbooks, CLAUDE.md, skills, SOPs
type
feature
New capability or an extension of an existing one
type
security
Security, privacy, credential or tenant-isolation exposure
type
tracking
Tracker, checklist, initiative or punch-list spanning several changes
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: logan/server-26#248
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What (narrowed by owner, 2026-10-05; replaces the original broad proposal)
Licence plates spoken on the radio are extracted from transcripts and used only as an internal correlation key. The same plate heard across talkgroups or calls links those calls into one incident or pursuit path. No plate search, no watchlist, no alerts, no display on any surface (UI, Discord, webhooks, exports, summaries, public tier). Not LPR and not camera-derived (that stays under #200). Roadmap: #247 (NEXT).
The original proposal (structured plates on the call doc, search, watchlist, map pins) is withdrawn. CISO position on that version is HOLD; it needs a new ruling plus E&O, #43, #13, #44 and #47.
Design as ruled (CISO ruling comment, 2026-10-05)
_PLATE_READ(intelligence.py:529) as a pre-filter plus a phonetic-alphabet normaliser.PLATE_HMAC_KEY+ org_id). Never plaintext, never partial or n-gram hashes.plate_keys(org_id,hmac,call_id,incident_id,seen_at,expires_at) with an explicit deny rule. Nothing plate-derived on call or incident docs (browser reads Firestore directly; #13 rules not verifiably live).expires_at = last_seen + 2h, code-enforced ceiling 6h, not org-configurable. In-process sweep loop plus a Firestore TTL policy.corr_debugand logs: match boolean, count, and at most a 4-hex-char HMAC prefix. Never plaintext, normalised plate, span or full hash. Remove vehicle lists from the INFO line atincident_correlator.py:1950-1952. Requiredcaplogandcorr_debugtests.vehicles(prompt + deterministic) and from AI summaries. Add plates as a redaction class in #43's write-time redacted copy.drb-correlation-review.Status
Ruling: ship-with-conditions (see comment). Needs CEO acceptance. Nothing in the narrowed build waits for E&O, Gate A or Gate B.
Related: #200 (camera frames, unruled), #43 (name/plate redaction), #44 (retention), #13 (rules), #47 (ToS/Privacy), #96 (corr_debug taint), #170 (replay dumps), #247 (roadmap).
Plate tracking from transcripts: extract spoken plates, search, watchlist, show where mentioned (needs CISO ruling)to Plates as an internal correlation key only: HMAC match key from spoken plates, 2h TTL, no search/watchlist/display (CISO ruled)CISO ruling, 2026-10-05: plates as an INTERNAL correlation key only (narrowed scope)
Verdict: SHIP WITH CONDITIONS. I did not post a ruling on the broad version (search, watchlist, alerts, map pins). Nothing here supersedes an earlier ruling. My position on the broad version, for the record, is HOLD: it needs E&O, #43, #13, #44 and #47 first, and I would want a fresh ruling before it is picked up again. The CEO can overrule any of this.
Position
Hash-only, same-org, short-lived matching keys create no new disclosure. They have no reader and no display. That can ship ahead of every gate. The honest limit is that it cannot make the existing plate exposure go away. Raw plates already sit in transcripts, and probably in
vehicles, on browser-readable docs. See finding 2.Findings (ranked by blast radius if we get it wrong; all cited from origin/main
a75149b)callsandincidentsdirectly, and the rules allow any org member, whatever their role, to read those docs (infra/firestore/firestore.rulesdocInMyOrg()). Rules cannot mask fields. #13 was reopened 2026-09-13 because CI never actually deploys them, and I found no later comment saying that is fixed. So the live rules are not verifiable from here. Nothing plate-derived goes on a call or incident doc.vehiclesis free text from the extraction prompt (intelligence.py:58). It is rendered atdrb-frontend/app/incidents/[id]/page.tsx:217-221and substring-searched client-side atincidents/page.tsx:43. If Gemini puts a plate in it, a plate is searchable by any org user right now. I could not verify from here whether it does.corr_debugcopies the raw scene transcript onto the call doc (incident_correlator.py:1546-1562)._snippet_allowed(alerter.py:97) is closed by default, and that holds.llm_correlator.py:101,110sends the transcript (700 chars) andvehiclesto Gemini. Plates already leave to Google today. This is not new, but I cannot verify the key's data-handling tier from here.incident_correlator.py:1950-1952logsinc_vehiclesandcall_vehiclesat INFO. Any plate that reachesvehicleslands in container logs. This is the recurring "library or app logs the sensitive thing" class.Rulings
1. HMAC vs plaintext: HMAC. Plaintext is not allowed.
PLATE_HMAC_KEY) in the ansible vault, read into the process only. Never logged, never in.env.example.plate_keys(org_id,hmac,call_id,incident_id,seen_at,expires_at). Add an explicitallow read, write: if falserule for it. It is also a server-only decision (finding 1), so it never depends on a rule being deployed.plate_key(org, plate)is private to the pipeline.Lifetime: the correlation window, not a retention period.
config.py:119). Setexpires_at = last_seen + 2h, with a code-enforced ceiling of 6h. It is not org-configurable.expires_at(set in the console or gcloud, not verifiable from this repo) AND an in-process sweep loop in c2-core. Do not rely on Firestore TTL alone, because it deletes lazily.PLATE_HMAC_KEYis cheap and safe, since keys are only 2h old. Rotate yearly or on any c2-core env exposure.2. Redaction of raw plates in transcripts and summaries.
vehiclesbefore write. Add a prompt instruction not to put plates invehicles, plus a deterministic scrub. Reuse the_PLATE_READpattern (intelligence.py:529).summarizer.py) after generation. Summaries are our own first-party content, so a plate next to an incident is our own assertion.vehiclesleak is checked against live data.3. Logging and
corr_debug.corr_debugand logs:corr_plate_match: true|false, a count, and a 4-hex-char prefix of the HMAC at most. The prefix is for disambiguating two plates in one incident during debugging.admin.py:107-120) uses a field whitelist. Keep it that way. Do not add a plate field to it.call_vehiclesandinc_vehiclesfrom the INFO line atincident_correlator.py:1950-1952, or log counts only.caplogtest plus acorr_debugschema test asserting a known test plate never appears in either.corr_debugis last-write-wins on multi-scene calls (#96). Any plate-match path stat read from it is tainted for those calls.4. DPPA, E&O and legal. I am not counsel. Counsel confirmation goes in #47.
drb-correlation-review. A plate match should corroborate, not stand alone, and "orphan in doubt" still applies. Any display, search, watchlist or alert changes this and needs E&O first.drb-frontend/app/privacy/page.tsx:23is still a TODO(legal).5. Gates.
Could not verify from here
vehiclesfield and transcripts already carry plates, and whether summaries do.c2-serverdatabase.Recommendation (smallest fix first)
incident_correlator.py:1950-1952) and add thecaplogpluscorr_debugtests. Smallest and no dependencies.vehiclesand summaries (intelligence.py,summarizer.py) and add the plate class to #43's redaction pass.plate_key()(HMAC, per-org HKDF),plate_keyscollection with a deny rule, 2h TTL with a 6h clamp and a sweep loop. Review withdrb-correlation-reviewbefore anything uses it as a link signal.PLATE_HMAC_KEYto vault and env templates without printing it; create the Firestore TTL policy.Needs a CEO ruling
vehiclesand summaries on merge?Status label:
status/needs-decisionis kept. The conditions above need CEO acceptance. No code is in progress.