Skip to main content

Hub Federation — PSAP Agents

PSAPLink PSAP instances register with Cloud via the command_connections table.

ColumnDescription
psap_agency_idThe PSAP agency this instance serves
api_key_hashSHA-256 of the raw API key
api_key_prefixFirst 8 characters for display
ack_modepoll or callback
public_urlPSAP's public URL (required for callback mode)
last_seen_atUpdated 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:

  1. JSON body encrypted with ChannelEncryptionService (XSalsa20-Poly1305)
  2. HMAC-SHA256 computed over the ciphertext
  3. X-PSAPLink-Encrypted: 1 header 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.