Hub Federation — PSAP Agents
PSAPLink PSAP instances register with Cloud via the command_connections table.
| Column | Description |
|---|---|
psap_agency_id | The PSAP agency this instance serves |
api_key_hash | SHA-256 of the raw API key |
api_key_prefix | First 8 characters for display |
ack_mode | poll or callback |
public_url | PSAP's public URL (required for callback mode) |
last_seen_at | Updated on each authenticated request |
Authentication
PSAP instances use the CommandPrincipal security token. Requests carry the raw API key in X-PSAPLink-Key. The authenticator hashes the key and compares against api_key_hash.
HMAC Event Signing
Every incident event from PSAPLink PSAP includes:
X-PSAPLink-Signature: hash_hmac('sha256', $rawJsonBody, $rawApiKey)
InboundEventController::verifyHmacSignature() recomputes and compares via hash_equals. Missing or mismatched signature returns 403.
Bootstrap Flow
A new PSAPLink PSAP instance calls GET /api/v1/core/psap-config to retrieve PSAP metadata and all active CFS configurations for self-configuration.
ACK Queue
GET /api/v1/core/ack-queue/{psapId}?after=<ISO8601> returns all ACKed notifications for that PSAP since the cursor timestamp. Defaults to 24 hours ago if no cursor supplied.
Channel Encryption
All PSAP→Cloud event POSTs use Encrypt-then-MAC:
- JSON body encrypted with
ChannelEncryptionService(XSalsa20-Poly1305) - HMAC-SHA256 computed over the ciphertext
X-PSAPLink-Encrypted: 1header signals encrypted body
Encryption subkey is derived from the raw API key using BLAKE2b — no additional environment variable needed.
Future: Hub-Split
When the platform needs to support self-hosted company Field instances, Cloud will split into Hub (routing engine) and Field (company dashboard). See docs/future/hub-split.md in the repo for the plan.