6.7 KiB
ChronoSeal API Reference
ChronoSeal defines a small, deterministic API surface for browser attestation and heartbeat verification.
Base URL
All endpoints are relative to the server root. In development: http://localhost:3000. In production: the HTTPS origin of the protected site.
POST /init
Initialise a new browser session.
Request
POST /init
Content-Type: application/json
{
"public_key": "hex-encoded 32-byte Ed25519 verifying key"
}
| Field | Type | Description |
|---|---|---|
public_key |
string |
Hex-encoded 32-byte Ed25519 public key generated by the WASM runtime |
Response 200 OK
{
"session_id": "64-char hex string",
"salt": "32-char hex string",
"opcodes_b64": "base64-encoded VM program",
"initial_hash": "64-char hex string",
"expires_at": 1234567890123,
"heartbeat_min_interval_ms": 12000,
"heartbeat_max_interval_ms": 25000,
"gene_size": 512,
"mutation_step": 1,
"mutation_order_b64": "base64-encoded mutation program"
}
| Field | Type | Description |
|---|---|---|
session_id |
string |
Opaque session identifier for the current browser session |
salt |
string |
Random 16-byte salt used to seed the hash chain |
opcodes_b64 |
string |
Base64-encoded randomized VM program executed on every heartbeat |
initial_hash |
string |
Initial chain hash H(0) used as prev_hash for the first heartbeat |
expires_at |
number |
Unix timestamp in milliseconds after which the session expires |
heartbeat_min_interval_ms |
number |
Minimum heartbeat interval in milliseconds |
heartbeat_max_interval_ms |
number |
Maximum heartbeat interval in milliseconds |
gene_size |
number |
Size of the initial synthetic gene buffer |
mutation_step |
number |
Initial mutation step expected on the first heartbeat |
mutation_order_b64 |
string |
Base64-encoded mutation order for gene commitment preview |
Error
POST /init returns 500 Internal Server Error only for server-side failures such as invalid public key length or persistence errors. No detailed error information is exposed to callers.
POST /hb
Submit a heartbeat to continue the session.
Request
POST /hb
Content-Type: application/json
{
"session_id": "64-char hex",
"prev_hash": "64-char hex",
"timestamp": 1234567890123,
"entropy_data": {
"events": [
{ "x": 412.0, "y": 308.5, "t": 1234.567 }
]
},
"stack_state": {
"stack": [2971406957, 1234567890],
"ip": 42
},
"fingerprint": {
"aspectRatio": "1.7777777778",
"devicePixelRatio": 2,
"hardwareConcurrency": 8
},
"mutation_step": 1,
"gene_commitment": "64-char hex",
"signature": "128-char hex"
}
| Field | Type | Description |
|---|---|---|
session_id |
string |
Session ID from /init |
prev_hash |
string |
Previous hash chain head (initial_hash on first heartbeat) |
timestamp |
number |
Date.now() in milliseconds |
entropy_data.events |
array |
Mouse event list since the previous heartbeat |
stack_state.stack |
array |
VM stack contents after program execution |
stack_state.ip |
number |
VM instruction pointer after execution |
fingerprint.aspectRatio |
string |
screen.width / screen.height to 10 decimal places |
fingerprint.devicePixelRatio |
number |
window.devicePixelRatio |
fingerprint.hardwareConcurrency |
number |
`navigator.hardwareConcurrency |
mutation_step |
number |
Current mutation step sent by the client |
gene_commitment |
string |
Gene commitment produced by the WASM preview mutation engine |
signature |
string |
Hex-encoded 64-byte Ed25519 signature over the canonical payload |
Canonical Signing Payload
The client signs a canonical JSON object with top-level keys sorted alphabetically:
{
"entropyData": { "events": [{ "t": ..., "x": ..., "y": ... }] },
"fingerprint": {
"aspectRatio": "...",
"devicePixelRatio": ...,
"hardwareConcurrency": ...
},
"geneCommitment": "...",
"mutationStep": ...,
"prevHash": "...",
"sessionId": "...",
"stackState": { "ip": ..., "stack": [...] },
"timestamp": ...
}
Note: the signed payload uses camelCase while the transport request uses snake_case.
Response 200 OK — Accepted
{
"status": "ok",
"next_salt": "32-char hex string",
"next_mutation_step": 2,
"next_mutation_order_b64": "base64-encoded mutation program"
}
| Field | Type | Description |
|---|---|---|
status |
string |
Always ok |
next_salt |
string |
Next server salt for the following heartbeat |
next_mutation_step |
number |
Next mutation step to apply after acceptance |
next_mutation_order_b64 |
string |
Base64-encoded next mutation program |
Response 200 OK — Rejected
{
"status": "ok"
}
A rejected heartbeat omits next_salt, next_mutation_step, and next_mutation_order_b64.
This silent rejection model avoids giving attackers distinct failure signals.
Validation Rules
Heartbeats are rejected silently when any validation step fails:
- session missing or expired
- signature invalid
- hash chain mismatch
- mutation step mismatch
- gene commitment mismatch
- timestamp outside ±30 seconds
- insufficient mouse events
- insufficient mouse movement
- unrealistic speed profile
- missing pause intervals
- invalid fingerprint values
WASM Runtime Exports
The WASM module exports the following functions to JavaScript:
| Function | Signature | Description |
|---|---|---|
generate_keypair() |
() -> string |
Generate a new Ed25519 keypair and return the public key hex |
get_public_key() |
() -> string |
Return the current public key hex |
sign_message(msg) |
(string) -> string |
Sign a UTF-8 string payload and return the hex signature |
compute_next_hash(prev, ts, entropy, stack, salt) |
(string, u64, string, string, string) -> string |
Compute the next Blake3 hash chain value |
run_program(b64) |
(string) -> JsValue |
Execute a base64 VM program and return stack state |
init_gene_state(gene_size) |
(u32) -> bool |
Initialise the synthetic gene buffer in WASM memory |
preview_gene_commitment(order_b64) |
(string) -> string |
Preview the next gene commitment from a mutation order |
commit_gene_preview() |
() -> bool |
Commit the previewed mutation after an accepted heartbeat |
discard_gene_preview() |
() -> void |
Discard the previewed mutation after rejection or error |
current_gene_commitment() |
() -> string |
Return the current committed gene commitment |
String-returning functions return "" on error. Callers must handle empty values and boolean failures gracefully.