Security & Encryption
Learn how WakeLink keeps the relay blind by separating relay authentication from EWSP encryption and by verifying every encrypted packet end to end.
Threat model
| Asset | Protected by | Notes |
|---|---|---|
| Account access | JWT access token, wl_ API token (Hashed) | Used by dashboard, Android app, and CLI for REST calls. |
| Agent relay access | Per-agent wla_ token (Hashed) | Lets exactly one firmware agent authenticate to /wss. |
| Wake payload plaintext | EWSP session keys | Never visible to the relay, database, or reverse proxy. |
| Firmware updates | HTTPS transport, Ed25519 signature | Verified on-agent before rebooting into the new image. |
Blind relay
The relay only needs enough metadata to forward frames between a logged-in client session and the correct firmware connection. The outer EWSP envelope exposes the negotiated protocol version, an 8-byte session identifier, a monotonic sequence number, and the ciphertext blob. The encrypted inner payload containing commands such as wake, info, or ota is never decrypted server-side.
{
"v": "1.0",
"sid": "a1b2c3d4e5f6a7b8",
"seq": 42,
"p": "<base64(ciphertext || 16-byte tag)>"
}EWSP cryptography
EWSP authenticates the opening handshake with HMAC-SHA256, derives a fresh 32-byte encryption key with HKDF-SHA256, and protects each packet with XChaCha20-Poly1305. Sequence counters and replay bitmaps on the agent prevent old ciphertext from being accepted again.
master_key = sha256(agent_token_plaintext)
ikm = master_key + client_random + device_random
salt = session_id
info = b"ewsp_enc"
enc_key = hkdf_sha256(salt=salt, ikm=ikm, info=info, length=32)| Field | Value |
|---|---|
| Handshake auth | HMAC-SHA256(master_key, client_random) |
| Session ID | 8 random bytes returned in session_ready |
| Cipher | XChaCha20-Poly1305 with 24-byte nonce |
| Replay defence | Per-session monotonic seq counters plus replay bitmap |
| Session lifetime | Up to 24 hours, with idle timeout and auth-failure limits |
Token separation
| Token | Prefix | Used by | Purpose |
|---|---|---|---|
| Account API token | wl_ | Dashboard, Android, CLI | REST authentication (Hashed on relay). |
| Agent Relay token | wla_ | ESP32 firmware | Relay WebSocket auth (Hashed on relay). |
| EWSP E2E Secret | (none) | Client + agent only | Derive session keys; client-generated. |
wl_… account token onto the ESP32. The agent should store only its per-agent wla_… relay token and the separate EWSP agent_token (E2E Secret). Operational practices
- Terminate public traffic with HTTPS and WSS, even when you self-host behind nginx.
- Rotate
wl_…account tokens after suspected compromise and rotatewla_…tokens per agent when decommissioning or reprovisioning hardware. - Store account tokens in platform credential stores such as Android Keystore or secure CLI environment variables.
- Require signed OTA payloads for unattended rollouts; the firmware verifies Ed25519 signatures.
- Review firmware serial logs and relay logs together when diagnosing repeated auth failures or replay errors.