210 lines
6.7 KiB
Markdown
210 lines
6.7 KiB
Markdown
# 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
|
|
|
|
```http
|
|
POST /init
|
|
Content-Type: application/json
|
|
```
|
|
|
|
```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`
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```http
|
|
POST /hb
|
|
Content-Type: application/json
|
|
```
|
|
|
|
```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 || 1` |
|
|
| `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:
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```json
|
|
{
|
|
"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.
|