Files
nx9-chronoseal-rs/docs/API.md
T

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.