Skip to content

func-api-devices-backup-post

Description

Stores an encrypted configuration backup for a device. The caller (the FireFly Controller firmware) supplies the raw FFCE-format ciphertext blob produced on-device by AES-256-GCM with key_backup (HKDF-derived from the eFuse master key with label firefly-backup-v1). The Lambda validates the device-identity headers, then stores the blob in S3 keyed by the device UUID and records last_backup_date in DynamoDB.

This endpoint has no Cognito JWT authorizer — it is authenticated solely by the device's cryptographic signature.

Invocation

Invoked by API Gateway on an HTTP POST /devices/{uuid}/backup request (no JWT authorizer).

Sequence Diagram

Sequence Diagram

API Endpoints

MethodPathAuthDescription
POST/devices/{uuid}/backupDevice signature (headers)Store encrypted configuration backup

Request Headers

HeaderRequiredDescription
X-Device-UUIDYesMust match the {uuid} path parameter
X-Device-NonceYesBase64-encoded 32-byte random nonce
X-Device-TimestampYesISO 8601 UTC timestamp (e.g. 2025-05-09T12:00:00Z)
X-Device-SignatureYesBase64-encoded DER ECDSA P-256 signature over SHA-256(nonce || timestamp)
Content-TypeYesMust be application/octet-stream
ETagNoPlaintext SHA-256 hex digest of the backup content (read from /backup.etag on the device). Stored as S3 object metadata and returned by GET /devices/{uuid}/backup so the controller can restore /backup.etag after a restore. When absent, the 304 check is skipped.
If-None-MatchNoPlaintext SHA-256 ETag of the backup the client believes is already stored (as returned by a previous POST or GET). Only evaluated when the ETag request header is also present; returns 304 if it matches the stored S3 metadata ETag.

Request Body

Raw FFCE-format encrypted binary blob (max 512 KB). The blob starts with the 4-byte magic FFCE and contains the AES-256-GCM encrypted configuration.

Response Body

json
{
  "message": "Backup stored",
  "last_backup_date": "2025-05-09T12:00:00Z"
}

Response Codes

CodeReason
200 OKBackup stored successfully
304 Not ModifiedBackup content identical to existing — only returned when both ETag and If-None-Match request headers are present and If-None-Match matches the stored plaintext SHA-256 ETag
400 Bad RequestMissing/invalid headers, invalid Base64, or body not valid FFCE format
401 UnauthorizedDevice UUID not found, signature invalid, or timestamp outside the acceptance window
403 ForbiddenX-Device-UUID header does not match {uuid} path parameter
413 Payload Too LargeBody exceeds 512 KB limit
500 Internal Server ErrorUnhandled exception

See the API Reference for full schema documentation.

Deployment

See the deployment workflow documentation for workflow steps, infrastructure dependencies, and failure scenarios.