Public API
Message construction (normative)
The signed message MUST be:For Synheart Core’s cloud ingest, the runtime strips the
/ingest/ prefix from POSTs to /ingest/v1/... so signatures cover /v1/.... If you build clients that talk to those endpoints from outside Core, mirror this prefix-stripping rule.r||s (64 bytes) from its sign_bytes callback and the SDK wraps that in DER for the wire header.
Six required headers
The Dart
SignedHeaders.toMap() returns exactly these six keys.
Nonce policy
- UUID v4. Generated fresh per request; never reused across requests for the same
(app_id, device_id). - Server enforces replay protection on write endpoints (POST, PUT, PATCH, DELETE) within the 300-second freshness window.
- Replay protection is RECOMMENDED for read endpoints.
- Server stores nonces with a 300-second TTL keyed by
(device_id, nonce).
Timestamp policy
- Server-side freshness window: 300 seconds.
- SDK clock may drift;
correctClockSkew(serverTimestamp)learns an offset fromCLOCK_SKEWerrors and applies it on subsequent signs. - The SDK persists
clock_offset_msin local storage.
Server-side verification (RFC §8.4)
Backends that verify these signatures follow:- Extract the six headers; reject if any required header is missing.
- Reject if
abs(server_time - X-Synheart-Timestamp) > 300. - For write endpoints, reject if
(device_id, nonce)has been seen in the freshness window. - Reconstruct the message:
method + "\n" + path + "\n" + timestamp + "\n" + body_bytes. - Look up the public key by
(app_id, device_id). - Verify ECDSA P-256 signature over
SHA256(message). - Store the nonce with a 300-second TTL (write endpoints).
SignedHeaders type
Sig-Version
X-Synheart-Sig-Version is "1". The header is versioned so the signing scheme can evolve without breaking deployed clients.
Wiring through Synheart Core
Whensynheart-core consumes synheart-auth:
- Core constructs a
DeviceAuthProvideroverSynheartAuth. - Core registers the auth provider’s crypto callbacks with the Synheart runtime.
- The runtime’s request-signing path builds
X-Synheart-Proof(a compact JWS bound to the request URL+method) for HSI ingest. This is a different header fromX-Synheart-Signature— both are hardware-backed by the same ECDSA P-256 key but cover different surfaces. - For non-runtime traffic, hosts call
SynheartAuth.signRequest(...)directly and attach the six headers themselves.
What is not signed
- Query string components (signing is over
pathonly). Servers that include sensitive parameters in query strings are responsible for separate validation. - Headers other than the six listed above. The body and the path/method are the only signed surface.
Logging policy
Related
- Registration — how the public key reaches the server.
- Errors —
CLOCK_SKEW,NONCE_REPLAY,KEY_INVALIDATEDrecovery. - Cloud Protocol —
X-Synheart-Prooffor HSI ingest.