From 0ed3cb444de35c6515a1bd980261a1049635277c Mon Sep 17 00:00:00 2001
From: Sunil Thakares
Date: Fri, 29 May 2026 21:55:08 +0530
Subject: [PATCH] Refactor attestation engine and synchronize project
documentation
- Refine session and storage lifecycle handling
- Improve VM extension architecture across server, shared, and WASM runtimes
- Enhance synthetic gene mutation engine integration and parity guarantees
- Align deterministic state progression between server and browser execution paths
- Update configuration examples and deployment guidance
- Expand architecture, API, threat model, privacy, and WASM build documentation
- Refresh README with comprehensive project overview, operational workflows,
browser integration details, storage backend documentation, and security model
- Document v0.6.0 refactoring outcomes and design rationale
- Improve consistency across documentation, configuration, and implementation
This commit consolidates the v0.6.0 architectural refactoring effort,
strengthening deterministic browser/server parity while improving
maintainability, operational clarity, and project documentation.
---
README.md | 814 ++++++++++++++++++++++++++++-------
docs/API.md | 327 ++++++++++----
docs/ARCHITECTURE.md | 592 ++++++++++++++++++++-----
docs/DEPLOYMENT.md | 376 ++++++++++------
docs/DESIGN-PHILOSOPHY.md | 139 ++++--
docs/PRIVACY POLICY.md | 116 +++--
docs/REFRACTORING-v0.6.0.md | 229 ++++++----
docs/THREAT_MODEL.md | 257 +++++++----
docs/WASM_BUILD.md | 156 ++++---
docs/chronoseal.example.toml | 22 +-
server/src/session.rs | 7 +-
server/src/storage.rs | 24 +-
shared/src/gene.rs | 2 +-
shared/src/vm_extensions.rs | 37 +-
wasm/src/vm_extensions.rs | 56 ++-
15 files changed, 2357 insertions(+), 797 deletions(-)
diff --git a/README.md b/README.md
index e1eddbb..4bd65a1 100644
--- a/README.md
+++ b/README.md
@@ -9,15 +9,15 @@
- Privacy-first • Deterministic WASM parity • Silent rejection • Low overhead
+ Privacy-first | Deterministic WASM parity | Silent rejection | Low overhead
-
-
+
+
-
+
@@ -27,200 +27,722 @@
---
-ChronoSeal is a mature Unix-native cryptographic attestation daemon for browser session continuity and anti-automation defense.
+ChronoSeal is a Linux-native cryptographic attestation service for browser session continuity and anti-automation defense. It runs as a small daemon, serves a browser WASM runtime, and validates signed heartbeat requests through deterministic server/client state progression.
-It provides a low-overhead, privacy-respecting proof-of-runtime system built around a deterministic **Synthetic Gene Mutation Engine** and a silent, replay-resistant heartbeat protocol.
+The core idea is simple: a real browser session should be able to keep advancing a private cryptographic state, a hash chain, and a deterministic synthetic gene mutation chain. Basic HTTP clients, stale replay attempts, and incomplete automation should fail without receiving a useful failure reason.
-v0.6.0 introduces the core innovation: a deterministic synthetic gene mutation chain with server/WASM parity, stronger liveness guarantees, and a domain-separated mutation commitment handshake.
+ChronoSeal is a cost-raising attestation layer. It is not a CAPTCHA replacement, identity provider, fingerprinting product, or perfect bot blocker.
----
+## Contents
-## What ChronoSeal Provides
+- [Features](#features)
+- [How It Works](#how-it-works)
+- [Repository Layout](#repository-layout)
+- [Quick Start](#quick-start)
+- [Build From Source](#build-from-source)
+- [Run Locally](#run-locally)
+- [Install as a Service](#install-as-a-service)
+- [Docker](#docker)
+- [CLI Reference](#cli-reference)
+- [Configuration](#configuration)
+- [HTTP API](#http-api)
+- [Browser Integration](#browser-integration)
+- [Storage Backends](#storage-backends)
+- [Operations](#operations)
+- [Security Model](#security-model)
+- [Development](#development)
+- [Troubleshooting](#troubleshooting)
+- [Further Reading](#further-reading)
+- [License](#license)
-* Native Linux daemon with hardened `systemd` integration
-* Deterministic mutation engine running in both server Rust and client WASM
-* Silent rejection semantics for attacker resilience
-* Multi-backend storage: `sqlite-in-memory`, `sqlite-disk`, and `valkey`
-* CLI-first operation with rich subcommands
-* Structured logging, PID file management, graceful shutdown
-* Prometheus-compatible metrics and runtime statistics
-* Lightweight browser runtime with WASM-based attestation
-* Privacy-first design with ephemeral session state and no persistent tracking
+## Features
----
+- Native Unix daemon with `systemd` service support.
+- Axum-based HTTP service exposing `/init`, `/hb`, `/health`, `/metrics`, and `/stats`.
+- Rust/WASM browser runtime for key generation, signing, VM execution, and mutation preview.
+- Deterministic Synthetic Gene Mutation Engine shared by the server and WASM crates.
+- Ed25519 signatures over canonical heartbeat payloads.
+- Blake3 hash chain continuity across accepted heartbeats.
+- Silent rejection semantics: invalid heartbeats still return `{"status":"ok"}`.
+- Configurable behavioral trust checks for mouse movement, pauses, timing, and browser signals.
+- Runtime storage abstraction with `sqlite-in-memory`, `sqlite-in-disk`, and `valkey` modes.
+- CLI-first lifecycle, status, health, metrics, stats, config validation, key generation, and shell completions.
+- Privacy-oriented design based on ephemeral session state rather than long-term identity tracking.
-## Why ChronoSeal
+## How It Works
-ChronoSeal raises the operational cost of automation by combining:
+ChronoSeal creates a short-lived browser attestation session and advances it through signed heartbeats.
-* cryptographic session continuity
-* deterministic VM execution
-* behavioral entropy validation
-* mutation commitment parity
-* silent, ambiguous rejection behavior
+1. The browser loads the generated WASM package from `frontend/pkg`.
+2. The WASM runtime generates an Ed25519 keypair and returns the public key to JavaScript.
+3. The browser calls `POST /init` with the public key.
+4. The server creates a session and returns:
+ - `session_id`
+ - initial salt
+ - initial hash chain head
+ - VM opcode program
+ - gene size
+ - mutation step
+ - mutation order
+ - heartbeat timing bounds
+5. For each heartbeat, the browser:
+ - executes the VM opcode program
+ - collects entropy and browser signal data
+ - previews the next synthetic gene commitment in WASM
+ - signs the canonical heartbeat payload
+ - posts the request to `POST /hb`
+6. The server validates:
+ - session existence and expiration
+ - rate limit
+ - timestamp drift
+ - Ed25519 signature
+ - hash chain continuity
+ - behavioral trust checks
+ - mutation step
+ - gene commitment parity
+7. If the heartbeat is accepted, the server advances session state and returns the next salt and mutation order.
+8. If the heartbeat is rejected, the server returns only `{"status":"ok"}`.
-This is not a fingerprinting or surveillance platform. ChronoSeal is designed to make automation expensive, not to collect user identities.
+The silent failure model deliberately avoids exposing which validation failed.
----
+## Repository Layout
+
+```text
+.
+├── Cargo.toml # Rust workspace
+├── server/ # chronoseal daemon and CLI
+├── shared/ # shared protocol, gene model, mutation engine
+├── wasm/ # WASM runtime crate
+├── frontend/ # static browser integration files
+├── docs/ # architecture, API, deployment, threat model
+├── scripts/ # build, install, release, dev helpers
+├── chronoseal.service # systemd unit
+├── Dockerfile # container image
+└── docker-compose.yml # local container orchestration
+```
+
+Workspace members:
+
+- `chronoseal-server`: daemon binary crate. Binary name: `chronoseal`.
+- `shared`: common cryptographic and mutation logic used by server and WASM.
+- `chronoseal-wasm`: browser runtime compiled with `wasm-pack`.
## Quick Start
-### Install
+For a full native install:
```bash
sudo bash scripts/install.sh
```
-### Verify status
+Check the daemon:
```bash
chronoseal status --format json
-```
-
-### Health probe
-
-```bash
chronoseal health
-```
-
-### View metrics
-
-```bash
chronoseal metrics
-```
-
-### Follow logs
-
-```bash
sudo journalctl -u chronoseal -f
```
----
+For local development without installing:
-## CLI Overview
+```bash
+bash scripts/build.sh
+cargo run -p chronoseal-server --bin chronoseal -- run \
+ --bind 127.0.0.1:3000 \
+ --frontend-dir frontend
+```
+
+Then open:
+
+```text
+http://127.0.0.1:3000
+```
+
+## Build From Source
+
+### Requirements
+
+| Tool | Minimum | Purpose |
+|---|---:|---|
+| Rust | 1.87 stable | Build server, shared crate, and tests |
+| wasm-pack | 0.13 | Build browser WASM package |
+| wasm32 target | current stable | WASM compilation target |
+| systemd | 248+ | Optional native service management |
+| Docker | 24.x | Optional container workflow |
+| Docker Compose | 2.x | Optional local orchestration |
+
+Install the WASM target and `wasm-pack`:
+
+```bash
+rustup target add wasm32-unknown-unknown
+cargo install wasm-pack
+```
+
+Build everything needed for the browser and server:
+
+```bash
+bash scripts/build.sh
+```
+
+That script:
+
+1. Builds `wasm/` with `wasm-pack build --target web --release`.
+2. Replaces `frontend/pkg` with the generated WASM package.
+3. Builds the `chronoseal` release binary.
+
+Manual build:
+
+```bash
+wasm-pack build wasm --target web --release
+rm -rf frontend/pkg
+mv wasm/pkg frontend/pkg
+
+cargo build -p chronoseal-server --bin chronoseal --release
+```
+
+The binary is written to:
+
+```text
+target/release/chronoseal
+```
+
+## Run Locally
+
+Run the daemon directly from Cargo:
+
+```bash
+cargo run -p chronoseal-server --bin chronoseal -- run \
+ --bind 127.0.0.1:3000 \
+ --frontend-dir frontend
+```
+
+Probe it:
+
+```bash
+curl http://127.0.0.1:3000/health
+curl http://127.0.0.1:3000/stats
+curl http://127.0.0.1:3000/metrics
+```
+
+Use the CLI wrappers:
+
+```bash
+cargo run -p chronoseal-server --bin chronoseal -- status --bind 127.0.0.1:3000
+cargo run -p chronoseal-server --bin chronoseal -- health --bind 127.0.0.1:3000
+cargo run -p chronoseal-server --bin chronoseal -- stats --bind 127.0.0.1:3000 --format json
+```
+
+## Install as a Service
+
+The installer builds the project, installs the binary, copies frontend assets, installs the `systemd` unit, and starts the service.
+
+```bash
+sudo bash scripts/install.sh
+```
+
+Installed paths:
+
+| Path | Purpose |
+|---|---|
+| `/usr/local/bin/chronoseal` | daemon and CLI binary |
+| `/opt/chronoseal/frontend` | static frontend assets copied by installer |
+| `/etc/systemd/system/chronoseal.service` | service unit |
+| `/run/chronoseal.pid` | default PID file |
+
+Service commands:
+
+```bash
+sudo systemctl status chronoseal
+sudo systemctl restart chronoseal
+sudo systemctl disable --now chronoseal
+sudo journalctl -u chronoseal -f
+```
+
+## Docker
+
+Build and run with Compose:
+
+```bash
+docker compose up -d --build
+```
+
+The provided container exposes port `3000`.
+
+```bash
+curl http://127.0.0.1:3000/health
+```
+
+Important: the Dockerfile copies `frontend/` from the working tree. Build the WASM package into `frontend/pkg` before building the container if you need the browser runtime inside the image:
+
+```bash
+bash scripts/build.sh
+docker compose up -d --build
+```
+
+## CLI Reference
+
+Run:
```bash
chronoseal --help
```
-### Available commands
+Global options:
-| Command | Description |
-| ------------ | ------------------------------------------ |
-| `run` | Run the ChronoSeal daemon |
-| `status` | Report daemon status |
-| `health` | Perform daemon health probe |
-| `config` | Validate and print effective configuration |
-| `generate` | Generate operational material |
-| `db-type` | List database backend support status |
-| `metrics` | Output Prometheus metrics |
-| `stats` | Print runtime statistics |
-| `completion` | Generate shell completions |
-| `version` | Print version/build information |
+| Option | Environment | Description |
+|---|---|---|
+| `--config ` | `CHRONOSEAL_CONFIG` | Explicit TOML config path |
+| `--format ` | - | Output format for machine-readable commands |
+| `--output ` | - | Alias for `--format` |
+| `--log ` | `CHRONOSEAL_LOG` | Tracing filter, for example `info` or `chronoseal=debug` |
----
+Commands:
-## Example
+| Command | Description |
+|---|---|
+| `chronoseal run` | Run the daemon |
+| `chronoseal status` | Check configured daemon reachability and PID state |
+| `chronoseal health` | Perform an HTTP health probe |
+| `chronoseal config check` | Validate and print effective configuration |
+| `chronoseal generate keypair` | Generate an Ed25519 keypair |
+| `chronoseal version` | Print version/build information |
+| `chronoseal db-type` | List database backend support status |
+| `chronoseal metrics` | Fetch Prometheus metrics from the running daemon |
+| `chronoseal stats` | Fetch service statistics from the running daemon |
+| `chronoseal completion ` | Generate shell completions |
+
+Examples:
+
+```bash
+chronoseal run --bind 127.0.0.1:3000 --frontend-dir frontend
+chronoseal run --db-type sqlite-in-memory
+chronoseal status --format json
+chronoseal health --config /etc/chronoseal/config.toml
+chronoseal config check --output yaml
+chronoseal generate keypair --format json
+chronoseal completion bash > chronoseal.bash
+```
+
+## Configuration
+
+Configuration precedence:
+
+1. CLI flags
+2. `CHRONOSEAL_*` environment variables
+3. TOML config file
+4. built-in defaults
+
+Default config discovery:
+
+1. path from `CHRONOSEAL_CONFIG`, if it exists
+2. `/etc/chronoseal/config.toml`
+3. `$XDG_CONFIG_HOME/chronoseal/config.toml`
+4. `~/.config/chronoseal/config.toml`
+
+Example:
+
+```toml
+bind = "0.0.0.0:3000"
+db_type = "sqlite-in-memory"
+pid_file = "/run/chronoseal.pid"
+db_path = "/var/lib/chronoseal/chronoseal.sqlite"
+frontend_dir = "/usr/share/chronoseal/frontend"
+log_file = "/var/log/chronoseal/chronoseal.jsonl"
+
+heartbeat_min_interval_ms = 12000
+heartbeat_max_interval_ms = 25000
+expiration_minutes = 30
+rate_limit_count = 5
+rate_limit_window_secs = 10
+max_timestamp_drift_ms = 30000
+
+min_mouse_total_dist = 10.0
+max_mouse_avg_speed = 2.0
+min_pause_count = 1
+require_mouse_activity = true
+
+gene_size = 512
+mutation_rounds = 4
+```
+
+Common environment variables:
+
+| Variable | Description |
+|---|---|
+| `CHRONOSEAL_CONFIG` | Config file path |
+| `CHRONOSEAL_BIND` | Bind address, for example `127.0.0.1:3000` |
+| `CHRONOSEAL_DB_TYPE` | `sqlite-in-memory`, `sqlite-in-disk`, or `valkey` |
+| `CHRONOSEAL_DB_PATH` | SQLite database path |
+| `CHRONOSEAL_FRONTEND_DIR` | Static frontend directory served at `/` |
+| `CHRONOSEAL_PID_FILE` | PID file path |
+| `CHRONOSEAL_LOG` | Tracing filter |
+| `CHRONOSEAL_LOG_FILE` | Optional JSON log file |
+| `CHRONOSEAL_STATE_DIR` | Base state directory used for default `db_path` |
+| `CHRONOSEAL_HEARTBEAT_MIN_INTERVAL_MS` | Minimum accepted heartbeat interval |
+| `CHRONOSEAL_HEARTBEAT_MAX_INTERVAL_MS` | Maximum accepted heartbeat interval |
+| `CHRONOSEAL_EXPIRATION_MINUTES` | Session lifetime |
+| `CHRONOSEAL_RATE_LIMIT_COUNT` | Requests allowed in the rate-limit window |
+| `CHRONOSEAL_RATE_LIMIT_WINDOW_SECS` | Rate-limit window length |
+| `CHRONOSEAL_MAX_TIMESTAMP_DRIFT_MS` | Accepted client timestamp drift |
+| `CHRONOSEAL_MIN_MOUSE_TOTAL_DIST` | Minimum mouse movement distance |
+| `CHRONOSEAL_MAX_MOUSE_AVG_SPEED` | Maximum average mouse speed |
+| `CHRONOSEAL_MIN_PAUSE_COUNT` | Minimum detected pause count |
+| `CHRONOSEAL_REQUIRE_MOUSE_ACTIVITY` | Enable or disable mouse activity requirement |
+| `CHRONOSEAL_GENE_SIZE` | Synthetic gene buffer size |
+| `CHRONOSEAL_MUTATION_ROUNDS` | Mutation rounds per program |
+
+Validate configuration:
+
+```bash
+chronoseal config check --format yaml
+```
+
+## HTTP API
+
+ChronoSeal exposes a small HTTP surface:
+
+| Method | Path | Purpose |
+|---|---|---|
+| `POST` | `/init` | Start a browser attestation session |
+| `POST` | `/hb` | Submit a signed heartbeat |
+| `GET` | `/health` | Health probe |
+| `GET` | `/metrics` | Prometheus metrics |
+| `GET` | `/stats` | Runtime statistics |
+| `GET` | `/` | Static frontend files from `frontend_dir` |
+
+### `POST /init`
+
+Request:
+
+```json
+{
+ "public_key": "hex-encoded 32-byte Ed25519 verifying key"
+}
+```
+
+Response:
+
+```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"
+}
+```
+
+### `POST /hb`
+
+Request:
+
+```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"
+}
+```
+
+Accepted response:
+
+```json
+{
+ "status": "ok",
+ "next_salt": "32-char hex string",
+ "next_mutation_step": 2,
+ "next_mutation_order_b64": "base64-encoded mutation program"
+}
+```
+
+Rejected response:
+
+```json
+{
+ "status": "ok"
+}
+```
+
+Detailed API semantics are documented in [docs/API.md](docs/API.md).
+
+## Browser Integration
+
+The frontend imports the generated WASM module from `frontend/pkg`.
+
+```js
+import init, {
+ generate_keypair,
+ get_public_key,
+ sign_message,
+ compute_next_hash,
+ run_program,
+ init_gene_state,
+ preview_gene_commitment,
+ commit_gene_preview,
+ discard_gene_preview,
+ current_gene_commitment
+} from './pkg/chronoseal_wasm.js';
+```
+
+Call `await init()` before invoking exported functions.
+
+WASM exports:
+
+| Function | Purpose |
+|---|---|
+| `generate_keypair()` | Generate a browser-local Ed25519 keypair and return public key hex |
+| `get_public_key()` | Return current public key hex |
+| `sign_message(msg)` | Sign a canonical UTF-8 payload and return signature hex |
+| `compute_next_hash(prev, ts, entropy, stack, salt)` | Compute next Blake3 hash-chain value |
+| `run_program(b64)` | Execute a base64 VM program and return stack state |
+| `init_gene_state(gene_size)` | Initialize the synthetic gene buffer |
+| `preview_gene_commitment(order_b64, session_id, mutation_step, rounds)` | Preview next gene commitment |
+| `commit_gene_preview()` | Commit the previewed gene mutation after accepted heartbeat |
+| `discard_gene_preview()` | Discard previewed mutation after rejection or error |
+| `current_gene_commitment(session_id, mutation_step)` | Return current committed gene commitment |
+
+String-returning WASM functions return an empty string on error. Boolean-returning functions indicate success or failure directly.
+
+## Storage Backends
+
+Set storage mode with `db_type` or `CHRONOSEAL_DB_TYPE`.
+
+| Backend | Description |
+|---|---|
+| `sqlite-in-memory` | Default ephemeral session storage. State is lost on restart. |
+| `sqlite-in-disk` | SQLite database persisted at `db_path`. |
+| `valkey` | Valkey-compatible backend mode. |
+
+For Valkey mode, the server reads `CHRONOSEAL_VALKEY_ADDR` and defaults to `127.0.0.1:6666` when it is not set. If the Valkey connection fails, the current implementation falls back to in-memory SQLite and logs a warning.
+
+## Operations
+
+### Health
+
+```bash
+chronoseal health
+curl http://127.0.0.1:3000/health
+```
+
+### Status
```bash
chronoseal status --format json
```
-```json
-{
- "running": true,
- "healthy": true,
- "bind": "0.0.0.0:3000",
- "pid_file": "/run/chronoseal.pid",
- "pid": 79459
+### Metrics
+
+```bash
+chronoseal metrics
+curl http://127.0.0.1:3000/metrics
+```
+
+### Stats
+
+```bash
+chronoseal stats --format json
+curl http://127.0.0.1:3000/stats
+```
+
+### Logs
+
+```bash
+sudo journalctl -u chronoseal -f
+```
+
+Use `RUST_LOG=info` or `CHRONOSEAL_LOG=info` for normal production operation. Avoid debug logging in production because internal identifiers may appear in logs.
+
+### Reverse Proxy
+
+ChronoSeal should run behind TLS in production. Terminate HTTPS at a reverse proxy such as nginx, Caddy, HAProxy, or a cloud load balancer, then proxy to the local daemon.
+
+Example nginx location:
+
+```nginx
+location / {
+ proxy_pass http://127.0.0.1:3000;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
}
```
----
-
-## Architecture Summary
-
-ChronoSeal is composed of three primary runtime components:
-
-* `shared/` — shared cryptographic primitives, hash chaining, gene model, and mutation engine used by both server and WASM
-* `server/` — Axum-based Unix-native daemon, session lifecycle, storage, trust evaluation, and `POST /init` / `POST /hb` routes
-* `wasm/` — browser runtime for key generation, signature creation, VM execution, and mutation preview/commit lifecycle
-
-### Key innovations in v0.6.0
-
-* Synthetic Gene Mutation Engine with deterministic, shared opcode semantics
-* Server-side gene commitment validation on every heartbeat
-* `mutation_step` and `mutation_order_b64` handshake in init and heartbeat responses
-* `db_type` runtime backend selection with SQLite and Valkey support
-
----
-
-## How ChronoSeal Works
-
-ChronoSeal establishes continuity by chaining signed heartbeats between client and server.
-
-### Session flow
-
-1. Client loads the WASM runtime and generates an Ed25519 keypair in WASM memory.
-2. Client calls `POST /init` with the public key.
-3. Server creates an ephemeral session and returns a `session_id`, initial salt, VM program, and mutation order metadata.
-4. Client executes the VM program, collects browser entropy, previews the mutation commitment, signs the heartbeat payload, and sends `POST /hb`.
-5. Server verifies signature, hash chain continuity, behavioral sanity, mutation step parity, and gene commitment before returning the next salt and mutation order.
-
-### Silent failure model
-
-Invalid heartbeats are returned as `{"status":"ok"}` without mutation fields. This avoids giving attackers explicit feedback.
-
----
-
-## Storage Backends
-
-ChronoSeal supports multiple runtime storage backends configured via `db_type`:
-
-* `sqlite-in-memory` — default ephemeral session storage
-* `sqlite-disk` — persisted SQLite storage on disk
-* `valkey` — alternative backend compatibility mode for future high-performance storage
-
----
-
-## Deployment
-
-ChronoSeal is intended to run as a systemd-managed Unix daemon with strict sandboxing and observable metrics.
-
-See [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) for build, installation, and production deployment guidance.
-
----
-
## Security Model
-ChronoSeal is a cost-raising attestations layer, not a perfect bot blocker.
+ChronoSeal is designed to raise the cost of automation and replay attacks.
-It protects against:
+It helps defend against:
-* replay attacks
-* session cloning
-* invalid signature injection
-* broken hash chain continuity
-* mutation tampering
-* simple synthetic mouse and browser automation
+- replayed heartbeats
+- stale hash chain state
+- forged heartbeat signatures
+- mutation commitment tampering
+- session cloning using only a stolen `session_id`
+- simple scripted clients that do not run the WASM runtime
+- basic browser automation with weak interaction simulation
-It does not attempt to protect against:
+It does not claim to stop:
-* real users acting as bots
-* server-side application vulnerabilities
-* fully resourced adversaries with real browsers and hardware input devices
+- real users intentionally acting as bots
+- fully resourced browser farms
+- attackers with complete control of a real browser and realistic input
+- server-side application vulnerabilities
+- account abuse outside ChronoSeal's attestation boundary
+- long-term identity or fraud decisions by itself
-See [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md) for the full threat model.
+Privacy posture:
----
+- Sessions are short-lived.
+- The private key is generated in the browser runtime and is not sent to the server.
+- ChronoSeal validates continuity and plausibility rather than creating persistent user identities.
+- Invalid heartbeats are rejected silently to reduce oracle feedback.
+
+Read the full model in [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md).
+
+## Development
+
+Run the standard checks:
+
+```bash
+cargo fmt --check
+cargo check --workspace
+cargo test --workspace
+cargo clippy --workspace --all-targets -- -D warnings
+```
+
+Format:
+
+```bash
+cargo fmt
+```
+
+Build WASM during frontend/runtime changes:
+
+```bash
+wasm-pack build wasm --target web
+rm -rf frontend/pkg
+mv wasm/pkg frontend/pkg
+```
+
+Build release artifacts:
+
+```bash
+bash scripts/build.sh
+```
+
+Generate shell completion:
+
+```bash
+chronoseal completion bash > chronoseal.bash
+chronoseal completion zsh > _chronoseal
+```
+
+## Troubleshooting
+
+### Browser fails to load WASM
+
+Rebuild the WASM package and make sure `frontend/pkg/chronoseal_wasm.js` and the `.wasm` file exist:
+
+```bash
+bash scripts/build.sh
+```
+
+The `.wasm` file must be served as `application/wasm`. The built-in static file service handles this for normal ChronoSeal deployments.
+
+### `chronoseal health` fails
+
+Check that the daemon is running and that the CLI is probing the correct bind address:
+
+```bash
+sudo systemctl status chronoseal
+chronoseal health --bind 127.0.0.1:3000
+curl http://127.0.0.1:3000/health
+```
+
+### Heartbeats return only `{"status":"ok"}`
+
+That is the expected response for rejected heartbeats. Common causes include:
+
+- invalid signature
+- stale `prev_hash`
+- stale `mutation_step`
+- mismatched `gene_commitment`
+- timestamp drift beyond the configured window
+- insufficient mouse movement or pause data
+- rate limiting
+- expired session
+
+Use local development logs and tests to debug integration issues. Avoid debug logs in production.
+
+### SQLite disk mode cannot persist state
+
+Ensure the daemon user can create and write the configured database path:
+
+```bash
+sudo mkdir -p /var/lib/chronoseal
+sudo chown -R chronoseal:chronoseal /var/lib/chronoseal
+```
+
+Use:
+
+```toml
+db_type = "sqlite-in-disk"
+db_path = "/var/lib/chronoseal/chronoseal.sqlite"
+```
+
+### Config changes do not apply
+
+Check precedence. CLI flags override environment variables, environment variables override config files, and config files override built-in defaults.
+
+Print the effective config:
+
+```bash
+chronoseal config check --format yaml
+```
## Further Reading
-* [Architecture](docs/ARCHITECTURE.md)
-* [API Reference](docs/API.md)
-* [Deployment](docs/DEPLOYMENT.md)
-* [Threat Model](docs/THREAT_MODEL.md)
-* [Design Philosophy](docs/DESIGN-PHILOSOPHY.md)
-* [Privacy Policy](docs/PRIVACY%20POLICY.md)
-* [WASM Build](docs/WASM_BUILD.md)
-* [Refactoring v0.6.0](docs/REFRACTORING-v0.6.0.md)
+- [Architecture](docs/ARCHITECTURE.md)
+- [API Reference](docs/API.md)
+- [Deployment Guide](docs/DEPLOYMENT.md)
+- [Threat Model](docs/THREAT_MODEL.md)
+- [Design Philosophy](docs/DESIGN-PHILOSOPHY.md)
+- [Privacy Policy](docs/PRIVACY%20POLICY.md)
+- [WASM Build Guide](docs/WASM_BUILD.md)
+- [Refactoring v0.6.0](docs/REFRACTORING-v0.6.0.md)
+- [Contributing](CONTRIBUTING.md)
+- [Security Policy](SECURITY.md)
+
+## License
+
+ChronoSeal is licensed under either of:
+
+- MIT, see [LICENSE](LICENSE)
+- Apache-2.0, see [LICENSE-APACHE](LICENSE-APACHE)
+
+at your option.
diff --git a/docs/API.md b/docs/API.md
index 5bdebbb..6339575 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -1,16 +1,56 @@
# ChronoSeal API Reference
-ChronoSeal defines a small, deterministic API surface for browser attestation and heartbeat verification.
+ChronoSeal exposes a small HTTP API for browser attestation, heartbeat verification, health checks, metrics, and runtime statistics.
+
+This document describes the wire format and acceptance semantics. The internal state model is covered in [ARCHITECTURE.md](ARCHITECTURE.md).
## Base URL
-All endpoints are relative to the server root. In development: `http://localhost:3000`. In production: the HTTPS origin of the protected site.
+All paths are relative to the ChronoSeal server root.
----
+- Development default: `http://127.0.0.1:3000`
+- Production: the HTTPS origin or reverse-proxy path used by the protected site
-## POST /init
+Production deployments should use HTTPS. The daemon itself can run behind a local reverse proxy.
-Initialise a new browser session.
+## Content Type
+
+JSON endpoints expect:
+
+```http
+Content-Type: application/json
+```
+
+Responses are JSON except `/metrics`, which returns Prometheus text format.
+
+## Endpoint Summary
+
+| Method | Path | Purpose |
+|---|---|---|
+| `POST` | `/init` | Create a browser attestation session |
+| `POST` | `/hb` | Submit and verify a signed heartbeat |
+| `GET` | `/health` | Return daemon health |
+| `GET` | `/stats` | Return storage/session statistics |
+| `GET` | `/metrics` | Return Prometheus-compatible metrics |
+| `GET` | `/` | Serve static frontend assets from `frontend_dir` |
+
+## Data Types
+
+Common encodings:
+
+| Value | Encoding |
+|---|---|
+| Ed25519 public key | 32 raw bytes encoded as 64 hex characters |
+| Ed25519 signature | 64 raw bytes encoded as 128 hex characters |
+| `session_id` | 32 random bytes encoded as 64 hex characters |
+| `salt` | 16 random bytes encoded as 32 hex characters |
+| `initial_hash`, `prev_hash`, `gene_commitment` | 32-byte digest encoded as 64 hex characters |
+| `opcodes_b64`, `mutation_order_b64` | standard base64 |
+| timestamps | Unix time in milliseconds unless otherwise stated |
+
+## `POST /init`
+
+Creates a new attestation session.
### Request
@@ -25,11 +65,18 @@ Content-Type: application/json
}
```
-| Field | Type | Description |
-|---|---|---|
-| `public_key` | `string` | Hex-encoded 32-byte Ed25519 public key generated by the WASM runtime |
+| Field | Type | Required | Description |
+|---|---|---:|---|
+| `public_key` | string | yes | Browser-generated Ed25519 public key as 64 hex characters |
-### Response `200 OK`
+The private key is generated and retained by the browser WASM runtime. It is not sent to the server.
+
+### Successful Response
+
+```http
+HTTP/1.1 200 OK
+Content-Type: application/json
+```
```json
{
@@ -48,26 +95,26 @@ Content-Type: application/json
| 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 |
+| `session_id` | string | Opaque session identifier |
+| `salt` | string | Current server salt for the first heartbeat hash computation |
+| `opcodes_b64` | string | Randomized VM program executed by the browser runtime |
+| `initial_hash` | string | Initial chain head used as `prev_hash` for the first heartbeat |
+| `expires_at` | number | Session expiration timestamp in milliseconds |
+| `heartbeat_min_interval_ms` | number | Minimum heartbeat delay recommended by the server |
+| `heartbeat_max_interval_ms` | number | Maximum heartbeat delay recommended by the server |
+| `gene_size` | number | Initial synthetic gene buffer size |
+| `mutation_step` | number | Mutation step expected on the first heartbeat |
+| `mutation_order_b64` | string | Server-authored mutation order for the first heartbeat |
-### Error
+### Error Behavior
-`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.
+`/init` uses normal route-level error handling for invalid payloads or server failures. Invalid public key length, invalid configured gene size, or storage failure can prevent session creation.
----
+Unlike `/hb`, initialization failures are not part of the silent heartbeat rejection model.
-## POST /hb
+## `POST /hb`
-Submit a heartbeat to continue the session.
+Submits one heartbeat for an existing session.
### Request
@@ -92,7 +139,7 @@ Content-Type: application/json
},
"fingerprint": {
"aspectRatio": "1.7777777778",
- "devicePixelRatio": 2,
+ "devicePixelRatio": "2",
"hardwareConcurrency": 8
},
"mutation_step": 1,
@@ -101,45 +148,61 @@ Content-Type: application/json
}
```
-| 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 |
+| Field | Type | Required | Description |
+|---|---|---:|---|
+| `session_id` | string | yes | Session ID from `/init` |
+| `prev_hash` | string | yes | Current browser view of the accepted hash-chain head |
+| `timestamp` | number | yes | Browser wall-clock timestamp in milliseconds |
+| `entropy_data.events` | array | yes | Mouse samples since the previous heartbeat |
+| `entropy_data.events[].x` | number | yes | Mouse x coordinate |
+| `entropy_data.events[].y` | number | yes | Mouse y coordinate |
+| `entropy_data.events[].t` | number | yes | Event timestamp in milliseconds relative to the browser sampling window |
+| `stack_state.stack` | array | yes | VM stack output as unsigned 32-bit values |
+| `stack_state.ip` | number | yes | VM instruction pointer as an unsigned 16-bit value |
+| `fingerprint.aspectRatio` | string | yes | Screen aspect ratio; server accepts numeric strings in range `0.5..=3.0` |
+| `fingerprint.devicePixelRatio` | string | yes | Device pixel ratio; server accepts numeric strings in range `(0, 5]` |
+| `fingerprint.hardwareConcurrency` | number | yes | Positive hardware concurrency value |
+| `mutation_step` | number | yes | Mutation step currently expected by the server |
+| `gene_commitment` | string | yes | Context-bound commitment produced by the WASM mutation preview |
+| `signature` | string | yes | Ed25519 signature over the canonical payload |
### Canonical Signing Payload
-The client signs a canonical JSON object with top-level keys sorted alphabetically:
+The signature covers a canonical JSON object with sorted top-level keys:
```json
{
- "entropyData": { "events": [{ "t": ..., "x": ..., "y": ... }] },
+ "entropyData": { "events": [{ "t": 1234.567, "x": 412.0, "y": 308.5 }] },
"fingerprint": {
- "aspectRatio": "...",
- "devicePixelRatio": ...,
- "hardwareConcurrency": ...
+ "aspectRatio": "1.7777777778",
+ "devicePixelRatio": "2",
+ "hardwareConcurrency": 8
},
- "geneCommitment": "...",
- "mutationStep": ...,
- "prevHash": "...",
- "sessionId": "...",
- "stackState": { "ip": ..., "stack": [...] },
- "timestamp": ...
+ "geneCommitment": "64-char hex",
+ "mutationStep": 1,
+ "prevHash": "64-char hex",
+ "sessionId": "64-char hex",
+ "stackState": { "ip": 42, "stack": [2971406957, 1234567890] },
+ "timestamp": 1234567890123
}
```
-Note: the signed payload uses camelCase while the transport request uses snake_case.
+Important details:
-### Response `200 OK` — Accepted
+- The transport payload uses snake_case for several fields.
+- The signed payload uses camelCase names.
+- Top-level keys must be serialized deterministically in lexical order.
+- The `signature` field is not part of the signed payload.
+- Nested serialization must match the server's `serde_json` representation.
+
+The server reconstructs the canonical message from the received request before verifying the Ed25519 signature.
+
+### Accepted Response
+
+```http
+HTTP/1.1 200 OK
+Content-Type: application/json
+```
```json
{
@@ -152,12 +215,19 @@ Note: the signed payload uses camelCase while the transport request uses snake_c
| 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 |
+| `status` | string | Always `ok` |
+| `next_salt` | string | Server salt for the next heartbeat |
+| `next_mutation_step` | number | Mutation step expected on the next heartbeat |
+| `next_mutation_order_b64` | string | Server-authored mutation order for the next heartbeat |
-### Response `200 OK` — Rejected
+Clients should treat the heartbeat as accepted only when all next-state fields are present.
+
+### Rejected Response
+
+```http
+HTTP/1.1 200 OK
+Content-Type: application/json
+```
```json
{
@@ -165,45 +235,132 @@ Note: the signed payload uses camelCase while the transport request uses snake_c
}
```
-A rejected heartbeat omits `next_salt`, `next_mutation_step`, and `next_mutation_order_b64`.
+Rejected heartbeats omit:
-This silent rejection model avoids giving attackers distinct failure signals.
+- `next_salt`
+- `next_mutation_step`
+- `next_mutation_order_b64`
----
+This response shape is intentional. The server does not reveal which validation stage failed.
-## Validation Rules
+### Heartbeat Validation Order
-Heartbeats are rejected silently when any validation step fails:
+The server currently validates heartbeats in this order:
-* 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
+1. Rate-limit check in the route handler.
+2. Load session by `session_id`.
+3. Check session expiration.
+4. Verify Ed25519 signature.
+5. Compare `prev_hash` with stored `last_hash`.
+6. Compare `mutation_step` with stored `pending_mutation_step`.
+7. Apply stored pending mutation to a cloned gene state.
+8. Compare expected and submitted `gene_commitment`.
+9. Enforce timestamp drift.
+10. Validate mouse entropy.
+11. Validate fingerprint fields.
+12. Compute next hash-chain head.
+13. Generate next mutation order and salt.
+14. Persist advanced session state.
----
+Any failure after route-level JSON decoding returns the silent rejection body.
+
+## `GET /health`
+
+Returns a basic health response.
+
+```http
+GET /health
+```
+
+```json
+{
+ "status": "healthy"
+}
+```
+
+## `GET /stats`
+
+Returns storage-derived session statistics.
+
+```http
+GET /stats
+```
+
+```json
+{
+ "sessions": 1,
+ "expired_sessions": 0,
+ "max_chain_length": 4
+}
+```
+
+| Field | Type | Description |
+|---|---|---|
+| `sessions` | number | Stored session count |
+| `expired_sessions` | number | Expired sessions not yet purged |
+| `max_chain_length` | number | Highest stored heartbeat chain length |
+
+## `GET /metrics`
+
+Returns Prometheus-compatible text.
+
+```http
+GET /metrics
+```
+
+```text
+# HELP chronoseal_sessions Active ChronoSeal sessions
+# TYPE chronoseal_sessions gauge
+chronoseal_sessions 1
+# HELP chronoseal_expired_sessions Expired sessions not yet removed
+# TYPE chronoseal_expired_sessions gauge
+chronoseal_expired_sessions 0
+# HELP chronoseal_max_chain_length Maximum heartbeat chain length
+# TYPE chronoseal_max_chain_length gauge
+chronoseal_max_chain_length 4
+```
+
+## Client State Rules
+
+After `/init`, the client stores:
+
+- `session_id`
+- `initial_hash` as the first `prev_hash`
+- current `salt`
+- VM opcode program
+- committed gene state
+- pending mutation step
+- pending mutation order
+
+On accepted `/hb`:
+
+1. Commit the local gene preview.
+2. Compute the next local hash using the old salt that was active when the heartbeat was sent.
+3. Replace current salt with `next_salt`.
+4. Replace pending mutation step and order with server-provided values.
+
+On rejected `/hb`:
+
+1. Discard the local gene preview.
+2. Do not advance hash-chain state.
+3. Do not advance mutation state.
+4. Treat the session as suspect or restart attestation.
## WASM Runtime Exports
-The WASM module exports the following functions to JavaScript:
+The generated `chronoseal_wasm` package exposes:
| 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 |
+| `generate_keypair()` | `() -> string` | Generate an Ed25519 keypair and return public key hex |
+| `get_public_key()` | `() -> string` | Return current public key hex, or `""` if no keypair exists |
+| `sign_message(msg)` | `(string) -> string` | Sign a UTF-8 payload and return hex signature, or `""` on failure |
+| `compute_next_hash(prev, ts, entropy, stack, salt)` | `(string, u64, string, string, string) -> string` | Compute next Blake3 chain hash |
| `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 |
+| `init_gene_state(gene_size)` | `(u32) -> bool` | Initialize the browser gene state |
+| `preview_gene_commitment(order_b64, session_id, mutation_step, rounds)` | `(string, string, u64, u8) -> string` | Preview next mutation commitment |
+| `commit_gene_preview()` | `() -> bool` | Commit the preview after accepted heartbeat |
+| `discard_gene_preview()` | `() -> void` | Discard preview after rejection or error |
+| `current_gene_commitment(session_id, mutation_step)` | `(string, u64) -> string` | Return current committed gene commitment |
-String-returning functions return `""` on error. Callers must handle empty values and boolean failures gracefully.
+String-returning functions use `""` to signal failure. Callers must handle empty strings explicitly.
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 2a03630..5018b30 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -1,145 +1,533 @@
# ChronoSeal Architecture
-ChronoSeal is a Unix-native cryptographic attestation daemon that validates browser session continuity through deterministic VM execution, chained cryptographic state, and a shared Synthetic Gene Mutation Engine.
+ChronoSeal is a Unix-native browser attestation daemon. It validates browser session continuity by combining signed heartbeats, Blake3 hash-chain progression, deterministic VM execution, behavioral sanity checks, and a shared Synthetic Gene Mutation Engine that runs on both the server and the browser WASM runtime.
-## Overview
+This document describes the system architecture, state model, validation pipeline, trust boundaries, and operational assumptions. The API wire format is documented separately in [API.md](API.md), and deployment guidance is documented in [DEPLOYMENT.md](DEPLOYMENT.md).
-ChronoSeal is designed as a production-grade infrastructure component, not as a consumer-facing widget. It is a lightweight daemon that can be operated, monitored, and integrated like any other native Linux service.
+## Architectural Goals
-Key characteristics:
+ChronoSeal is designed as infrastructure software rather than a consumer-facing widget. The main goals are:
-* Unix-native daemon with systemd-compatible lifecycle
-* CLI-first control plane and configuration
-* Shared Rust/WASM runtime for server/client parity
-* Modular storage backend abstraction (`sqlite-in-memory`, `sqlite-disk`, `valkey`)
-* Silent rejection semantics for attacker resilience
-* Privacy-preserving ephemeral session state
+- Keep the server small, inspectable, and operable as a normal Unix daemon.
+- Use deterministic client/server computation so the server can verify browser-side progression without trusting browser claims blindly.
+- Make replay, stale state reuse, and incomplete automation expensive.
+- Preserve privacy by using short-lived session state instead of persistent identity tracking.
+- Avoid attacker feedback oracles by returning indistinguishable success-shaped responses for rejected heartbeats.
+- Keep browser integration lightweight: static JavaScript plus a Rust-generated WASM package.
-## Core Components
+ChronoSeal does not attempt to prove that a human is present. It attempts to prove that a client is maintaining the expected live browser-side cryptographic and mutation state.
+
+## System Context
+
+```text
+Protected browser origin
+ |
+ | static files and API calls
+ v
++------------------------------+
+| Browser |
+| - frontend JavaScript |
+| - chronoseal_wasm runtime |
+| - Ed25519 session key |
+| - VM and gene state |
++---------------+--------------+
+ |
+ | POST /init
+ | POST /hb
+ v
++------------------------------+
+| ChronoSeal daemon |
+| - Axum HTTP routes |
+| - session verifier |
+| - storage abstraction |
+| - metrics and health |
++---------------+--------------+
+ |
+ | SessionRecord
+ v
++------------------------------+
+| Storage backend |
+| - sqlite-in-memory |
+| - sqlite-in-disk |
+| - valkey |
++------------------------------+
+```
+
+ChronoSeal can serve the frontend files itself or sit behind a reverse proxy. TLS termination should happen before traffic reaches the daemon in production.
+
+## Workspace Components
+
+The repository is a Rust workspace with three runtime crates and one static frontend directory.
### `shared/`
-Shared protocol and runtime primitives used by both the server and the browser runtime:
+`shared/` contains protocol and deterministic runtime code used by both the server and WASM crates.
-* Cryptographic primitives: Blake3, Ed25519
-* Hash chain logic and session commitment handling
-* Synthetic gene model and deterministic mutation engine
-* Serialization, encoding, and canonical signing helpers
+Responsibilities:
+
+- wire protocol structs for `/init` and `/hb`
+- Blake3 hash-chain helpers
+- synthetic gene state representation
+- environment encoding and validation
+- mutation program generation, encoding, decoding, and execution
+- deterministic VM extension opcode semantics
+
+Important files:
+
+| File | Responsibility |
+|---|---|
+| `protocol.rs` | `InitRequest`, `InitResponse`, `HeartbeatRequest`, `HeartbeatResponse`, and supporting payload types |
+| `hashing.rs` | initial and next hash-chain computation |
+| `gene.rs` | gene state, environment records, validation, and context-bound commitment |
+| `vm_extensions.rs` | mutation order generation, opcode interpreter, execution tracing, and tests |
+| `constants.rs` | protocol and execution bounds |
+
+`shared/` is the determinism boundary. Any logic that must agree between server and browser belongs here rather than in server-only or frontend-only code.
### `server/`
-The server crate implements the runtime daemon:
+`server/` builds the `chronoseal` binary. It owns daemon lifecycle, HTTP routing, session verification, storage, metrics, configuration, and CLI behavior.
-* `routes/init.rs` — session initialization API
-* `routes/heartbeat.rs` — heartbeat verification API
-* `session.rs` — session lifecycle, mutation parity, and heartbeat validation
-* `storage.rs` — backend abstraction and persistence
-* `crypto.rs` — signature verification and key handling
-* `trust.rs` — behavioral entropy and sanity validation
-* `ratelimit.rs` — per-session request throttling
-* `cleanup.rs` — expiration and eviction tasks
-* `runtime.rs` — daemon bootstrap, metrics, and state management
+Important files:
+
+| File | Responsibility |
+|---|---|
+| `main.rs` | CLI command dispatch |
+| `cli.rs` | command, flag, and environment variable definitions |
+| `config.rs` | defaults, TOML loading, environment overrides, validation |
+| `runtime.rs` | daemon startup, Axum router, health, metrics, stats, graceful shutdown |
+| `routes/init.rs` | `POST /init` handler |
+| `routes/heartbeat.rs` | `POST /hb` handler and silent rejection response shape |
+| `session.rs` | session creation, heartbeat verification, state advancement |
+| `crypto.rs` | canonical signing payload and Ed25519 signature verification |
+| `storage.rs` | `DbPool`, SQLite, Valkey compatibility, session persistence, stats |
+| `trust.rs` | mouse entropy validation |
+| `fingerprint.rs` | browser signal validation |
+| `ratelimit.rs` | per-session rate limiting |
+| `cleanup.rs` | expired session removal |
+
+The server treats the browser as untrusted. Browser-supplied values are accepted only after signature, continuity, timing, behavioral, and mutation checks pass.
### `wasm/`
-The client runtime crate compiles to WebAssembly and powers attestation in the browser.
+`wasm/` compiles to the browser runtime package with `wasm-pack --target web`.
-* `crypto.rs` — in-WASM signing and hash computation
-* `vm.rs` — randomized opcode VM execution
-* `vm_extensions.rs` — synthetic gene mutation preview and commit lifecycle
+Responsibilities:
+
+- generate and hold the browser-local Ed25519 keypair
+- sign canonical heartbeat payloads
+- compute hash-chain values used by the browser integration
+- execute randomized VM programs
+- maintain committed and preview synthetic gene state
+- preview mutation commitments before a heartbeat is submitted
+- commit or discard preview state after server response
+
+Important files:
+
+| File | Responsibility |
+|---|---|
+| `crypto.rs` | key generation, public key export, message signing |
+| `vm.rs` | base VM program execution |
+| `vm_extensions.rs` | gene initialization, mutation preview, commit, discard, current commitment |
+
+The WASM runtime is not a trusted execution environment. It is useful because it forces a browser client to implement the same state transitions as the server and makes simple HTTP automation insufficient.
### `frontend/`
-Static browser integration code that loads the WASM module, orchestrates init/heartbeat flow, and collects browser entropy.
+`frontend/` contains static JavaScript and browser assets. It loads `frontend/pkg/chronoseal_wasm.js`, calls `/init`, periodically sends `/hb`, and coordinates browser-side state transitions.
-## v0.6.0 Innovation
+The frontend is intentionally thin. Durable protocol rules live in Rust, not in handwritten JavaScript.
-The primary innovation in v0.6.0 is the **Synthetic Gene Mutation Engine**.
+## Runtime Topology
-This layer adds a deterministic, shared server/WASM mutation handshake to the existing heartbeat continuity model.
+The daemon builds a single Axum application with:
-Key v0.6.0 behavior:
+| Route | Method | Purpose |
+|---|---|---|
+| `/init` | `POST` | create a new attestation session |
+| `/hb` | `POST` | verify and advance a heartbeat |
+| `/health` | `GET` | health probe |
+| `/metrics` | `GET` | Prometheus-compatible metrics |
+| `/stats` | `GET` | storage/session statistics |
+| `/` | `GET` | static frontend assets from `frontend_dir` |
-* `mutation_order_b64` is issued at session initialization and after every accepted heartbeat
-* `mutation_step` is tracked on both client and server
-* `gene_commitment` is computed locally in WASM and validated by the server
-* mutation state is persisted per session and advanced only on accepted heartbeats
-* scalar mutation programs are deterministic and bounded in cost
+Shared runtime state is held in `AppState`:
-This makes replay and tampering attacks significantly more expensive while preserving the existing privacy-first and silent-failure semantics.
+- `db_pool`: storage backend handle
+- `rate_limiter`: process-local rate limiter
+- `config`: runtime configuration snapshot behind an `RwLock`
-## Architecture Diagram
+Configuration is resolved in this order:
-```
-Browser Server
- ┌──────────────────────────────────────────────────────────────┐
- │ frontend/ + WASM runtime │
- │ - generate_keypair() │
- │ - sign_message() │
- │ - compute_next_hash() │
- │ - run_program() │
- │ - preview_gene_commitment() │
- │ - commit_gene_preview() │
- │ │
- │ POST /init -> │
- │ POST /hb -> │
- └──────────────────────────────────────────────────────────────┘
- │
- ▼
- ┌──────────────────────────────────────────────────────────────┐
- │ server/ │
- │ - signature validation │
- │ - hash chain continuity │
- │ - rate limiting │
- │ - behavioral trust checks │
- │ - mutation step validation │
- │ - gene commitment verification │
- │ - session persistence │
- │ - metrics and health │
- └──────────────────────────────────────────────────────────────┘
- │
- ▼
- ┌──────────────────────────────────────────────────────────────┐
- │ storage backends │
- │ - sqlite-in-memory │
- │ - sqlite-disk │
- │ - valkey compatibility mode │
- └──────────────────────────────────────────────────────────────┘
+1. CLI flags
+2. `CHRONOSEAL_*` environment variables
+3. TOML configuration file
+4. built-in defaults
+
+## Session State Model
+
+The server persists one `SessionRecord` per active session.
+
+| Field | Meaning |
+|---|---|
+| `session_id` | random 32-byte session identifier encoded as hex |
+| `public_key` | browser-generated Ed25519 verifying key |
+| `salt` | current server salt for hash-chain progression |
+| `last_hash` | current accepted hash-chain head |
+| `chain_length` | number of accepted chain states including initialization |
+| `created_at` | creation timestamp in milliseconds |
+| `last_seen` | timestamp of last accepted heartbeat |
+| `expires_at` | session expiration timestamp in milliseconds |
+| `gene` | committed synthetic gene byte buffer |
+| `environment` | encoded environment records |
+| `pending_mutation` | server-issued mutation program for the next heartbeat |
+| `pending_mutation_step` | mutation step expected on the next heartbeat |
+
+The committed server state advances only after a heartbeat passes all validation checks. Failed heartbeats do not update `last_hash`, `salt`, `gene`, `environment`, `pending_mutation`, or `pending_mutation_step`.
+
+## Initialization Flow
+
+```text
+Browser/WASM Server
+------------ ------
+generate_keypair()
+public key
+ |
+ | POST /init { public_key }
+ v
+ validate public key length
+ create GeneState
+ generate session_id
+ generate salt
+ compute initial_hash
+ generate VM opcodes
+ generate mutation step 1
+ persist SessionRecord
+ ^
+ | InitResponse
+ |
+store session_id, salt,
+initial_hash, opcodes,
+gene_size, mutation order
```
-## Storage Backends
+Initialization creates the first server-side commitment state but does not prove liveness. Liveness begins with accepted heartbeats.
-ChronoSeal supports pluggable backend modes using the `db_type` configuration option.
+The initial response contains:
-* `sqlite-in-memory` — default ephemeral session storage. No persistence across restarts.
-* `sqlite-disk` — persisted SQLite database on disk through `db_path`.
-* `valkey` — compatibility mode for alternative storage backends, currently supported alongside SQLite compatibility semantics.
+- `session_id`
+- `salt`
+- `opcodes_b64`
+- `initial_hash`
+- `expires_at`
+- heartbeat interval bounds
+- `gene_size`
+- `mutation_step`
+- `mutation_order_b64`
-## Runtime Philosophy
+## Heartbeat Flow
-ChronoSeal is intentionally designed to behave like traditional Unix infrastructure software:
+```text
+Browser/WASM Server
+------------ ------
+execute VM program
+collect entropy and fingerprint data
+preview pending gene mutation
+build canonical signing payload
+sign with Ed25519 private key
+ |
+ | POST /hb HeartbeatRequest
+ v
+ load session
+ check expiration
+ verify signature
+ check hash continuity
+ check mutation step
+ apply pending mutation
+ compare gene commitment
+ check timestamp drift
+ validate mouse entropy
+ validate fingerprint
+ compute next hash
+ generate next mutation
+ generate next salt
+ persist advanced state
+ ^
+ | accepted: status + next salt + next mutation
+ | rejected: { "status": "ok" }
+ |
+commit preview on accepted response
+discard or stop on rejected response
+```
-* explicit CLI operations (`run`, `status`, `health`, `config`, `metrics`, `stats`, `db-type`)
-* structured logging for `journalctl`
-* PID file management and graceful shutdown
-* systemd sandbox support
-* runtime configuration via TOML and CLI overrides
-* clear separation of protocol, persistence, and runtime concerns
+Accepted heartbeats return `next_salt`, `next_mutation_step`, and `next_mutation_order_b64`.
-## Integration Points
+Rejected heartbeats return only:
-* Browser clients consume the WASM module and call `/init` and `/hb`
-* Existing sites can proxy these API routes through their own web server
-* Frontend assets can be served by ChronoSeal directly or mounted in a sidecar deployment
-* TLS termination should be handled by a reverse proxy in production
+```json
+{
+ "status": "ok"
+}
+```
-## Operating Assumptions
+This silent rejection behavior is part of the security model. It prevents the API from acting as an oracle for signature, timing, mutation, or behavior failures.
-ChronoSeal is not a general-purpose authentication service. It is a cryptographic attestation and anti-automation layer intended to be integrated with existing site logic.
+## Verification Pipeline
-It assumes:
+Heartbeat verification occurs in `server/src/session.rs`.
-* browser clients can execute WASM
-* heartbeats will arrive every 12–25 seconds
-* session state can be safely persisted in SQLite or Valkey
-* service operators want Unix-native systemd deployment and observability
+The current validation order is:
+
+1. Load the session by `session_id`.
+2. Reject if the session is missing.
+3. Reject if `now > expires_at`.
+4. Verify the Ed25519 signature over the canonical payload.
+5. Decode and compare `prev_hash` with the stored `last_hash`.
+6. Compare request `mutation_step` with stored `pending_mutation_step`.
+7. Decode the stored gene environment.
+8. Apply the stored `pending_mutation` to a cloned server gene state.
+9. Compute the expected `gene_commitment` with session and step context.
+10. Compare the request `gene_commitment` with the expected commitment.
+11. Enforce timestamp drift bounds.
+12. Validate mouse entropy.
+13. Validate browser fingerprint fields.
+14. Compute the next hash-chain value.
+15. Generate the next mutation order.
+16. Generate the next salt.
+17. Persist the advanced session state.
+
+The verifier performs state mutation only after validation succeeds. This preserves replay resistance and avoids desynchronizing the server after invalid requests.
+
+## Canonical Signing Boundary
+
+The heartbeat signature covers a canonical JSON payload built from:
+
+- `entropyData`
+- `fingerprint`
+- `geneCommitment`
+- `mutationStep`
+- `prevHash`
+- `sessionId`
+- `stackState`
+- `timestamp`
+
+The server constructs this payload using a `BTreeMap`, which orders top-level keys deterministically before serializing. The transport request uses snake_case field names, while the signed payload uses camelCase names that match the browser-side canonical message.
+
+The signature does not cover the `signature` field itself.
+
+## Hash-Chain Boundary
+
+Each accepted heartbeat advances a Blake3 hash chain.
+
+Inputs include:
+
+- previous hash-chain head
+- heartbeat timestamp
+- entropy data
+- VM stack state
+- current server salt
+
+The server stores only the current accepted head as `last_hash`. A replayed heartbeat with an old `prev_hash` fails because the stored `last_hash` has already advanced.
+
+The salt rotates after every accepted heartbeat. The next salt is returned only on acceptance, so rejected clients do not receive the material needed for the next valid chain step.
+
+## Synthetic Gene Mutation Engine
+
+The Synthetic Gene Mutation Engine provides an additional deterministic continuity check.
+
+Core concepts:
+
+- `GeneState`: committed gene byte buffer plus environment records.
+- `MutationOrder`: mutation step plus encoded mutation program.
+- `pending_mutation`: the server-authored program expected on the next heartbeat.
+- `gene_commitment`: context-bound commitment over the candidate gene state, `session_id`, and `mutation_step`.
+
+The server and WASM runtime both execute the same mutation semantics from `shared/vm_extensions.rs`.
+
+Mutation lifecycle:
+
+1. Server stores a pending mutation program and step.
+2. Browser previews that mutation against its committed gene state.
+3. Browser sends the resulting `gene_commitment`.
+4. Server applies the same mutation to a clone of its committed gene state.
+5. Server compares the expected commitment with the browser commitment.
+6. On success, server commits the candidate state and issues the next mutation.
+7. Browser commits its preview only after receiving an accepted response.
+
+This design prevents a client from advancing mutation state independently of the server. The mutation order is server-authored, step-bound, and accepted only once.
+
+## Behavioral Trust Checks
+
+ChronoSeal includes lightweight behavioral checks. These checks are not a complete human verification system; they are an automation cost signal.
+
+Current checks include:
+
+- minimum mouse activity, when enabled
+- minimum total mouse movement distance
+- maximum average mouse speed
+- minimum pause count
+- timestamp drift bound
+- basic fingerprint field validation
+
+The checks are intentionally bounded and configurable. They should be treated as one layer in the attestation pipeline, not as the primary security primitive.
+
+## Storage Architecture
+
+Storage is abstracted by `DbPool`.
+
+| Backend | `db_type` | Characteristics |
+|---|---|---|
+| SQLite memory | `sqlite-in-memory` | default, process-local, ephemeral |
+| SQLite disk | `sqlite-in-disk` | persisted SQLite file at `db_path` |
+| Valkey | `valkey` | Valkey-compatible session store |
+
+The storage layer must support:
+
+- insert session
+- load session
+- update session
+- delete expired sessions
+- report statistics
+
+`valkey` mode reads `CHRONOSEAL_VALKEY_ADDR`, defaulting to `127.0.0.1:6666`. If connection setup fails, the current implementation logs a warning and falls back to in-memory SQLite.
+
+## Metrics and Observability
+
+ChronoSeal exposes two operational surfaces:
+
+- CLI commands: `status`, `health`, `metrics`, `stats`, `config check`
+- HTTP endpoints: `/health`, `/metrics`, `/stats`
+
+The metrics endpoint reports storage-derived counters including:
+
+- active sessions
+- expired sessions
+- maximum observed chain length
+
+The daemon uses structured tracing and can log to journald through normal systemd operation. Operators should avoid debug logging in production because internal identifiers may appear in logs.
+
+## Trust Boundaries
+
+### Browser Boundary
+
+The browser is untrusted. It may lie about entropy, fingerprint values, VM output, mutation commitment, timing, and session identifiers.
+
+Mitigation:
+
+- signature verification binds payloads to the browser session key
+- hash-chain checks reject stale state
+- mutation commitment checks reject incorrect gene progression
+- timing and behavioral checks reject implausible requests
+
+### WASM Boundary
+
+WASM code runs in the browser and is therefore not trusted as secure enclave code.
+
+Mitigation:
+
+- the server independently recomputes critical deterministic state
+- private key custody raises automation cost but is not treated as hardware-backed secrecy
+- failures do not reveal detailed reasons to callers
+
+### Storage Boundary
+
+Storage is trusted for session continuity. If storage is lost, sessions cannot continue. If storage is tampered with, attestation integrity can be affected.
+
+Mitigation:
+
+- use proper filesystem permissions for SQLite disk mode
+- deploy Valkey on a trusted network or protected socket
+- keep ChronoSeal behind normal host and service hardening
+
+### Network Boundary
+
+ChronoSeal expects production traffic to be protected by TLS. Plaintext deployment weakens confidentiality and makes traffic analysis easier.
+
+Mitigation:
+
+- terminate TLS at a reverse proxy or load balancer
+- keep `/init` and `/hb` same-origin with protected content when possible
+- avoid exposing internal metrics broadly
+
+## Failure Semantics
+
+ChronoSeal intentionally separates transport success from attestation success.
+
+| Failure class | HTTP behavior | State mutation |
+|---|---|---|
+| malformed route-level request | normal HTTP error handling | no session advancement |
+| invalid heartbeat semantics | `200 OK` with `{"status":"ok"}` | no session advancement |
+| rejected heartbeat | `200 OK` with `{"status":"ok"}` | no session advancement |
+| accepted heartbeat | `200 OK` with next-state fields | session state advances from the verifier's perspective |
+
+This ambiguity reduces attacker feedback. Application integrations must check for the presence of `next_salt`, `next_mutation_step`, and `next_mutation_order_b64` rather than treating any `status: ok` as an accepted heartbeat.
+
+## Invariants
+
+The architecture relies on these invariants:
+
+- A session has exactly one expected `pending_mutation_step` at a time.
+- A pending mutation is consumed only by an accepted heartbeat.
+- `last_hash` changes only after a heartbeat passes verification.
+- `salt` changes only after a heartbeat passes verification.
+- `gene` and `environment` change only after mutation commitment validation succeeds.
+- The next mutation order is generated only from an accepted candidate state.
+- Rejected heartbeats do not reveal the failed validation stage.
+- Browser-side preview state is committed only after an accepted heartbeat response.
+
+Breaking these invariants can introduce replay acceptance, client/server desynchronization, or oracle behavior.
+
+## Concurrency Notes
+
+ChronoSeal currently verifies a heartbeat by loading a session, computing candidate state, and writing the updated record back to storage. The intended operational model is one live heartbeat stream per browser session.
+
+Concurrent heartbeats for the same `session_id` should naturally collapse to at most one accepted progression because both requests present the same `prev_hash` and `mutation_step`; after the first accepted update, the second request becomes stale. Storage backends must preserve update visibility strongly enough for this assumption to hold.
+
+## Deployment Shape
+
+Typical production topology:
+
+```text
+Internet
+ |
+ v
+TLS reverse proxy
+ |
+ v
+chronoseal daemon on 127.0.0.1:3000
+ |
+ v
+SQLite disk or Valkey storage
+```
+
+Recommended deployment properties:
+
+- run under systemd with a dedicated service user
+- bind to localhost behind a reverse proxy unless direct exposure is required
+- serve over HTTPS
+- keep debug logs disabled
+- monitor `/health`, `/metrics`, and `/stats`
+- use `sqlite-in-memory` for ephemeral local sessions
+- use `sqlite-in-disk` or `valkey` when sessions must survive process restarts
+
+## Limitations
+
+ChronoSeal is not:
+
+- a user authentication system
+- a CAPTCHA
+- a fraud scoring engine
+- a hardware attestation system
+- a persistent identity framework
+- a complete defense against fully resourced browser farms
+
+It is a protocol layer that makes browser automation and replay more expensive by requiring correct, continuous, stateful execution.
+
+## Related Documents
+
+- [API Reference](API.md)
+- [Deployment Guide](DEPLOYMENT.md)
+- [Threat Model](THREAT_MODEL.md)
+- [WASM Build Guide](WASM_BUILD.md)
+- [Design Philosophy](DESIGN-PHILOSOPHY.md)
+- [Privacy Policy](PRIVACY%20POLICY.md)
diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md
index 1984431..b95ec9a 100644
--- a/docs/DEPLOYMENT.md
+++ b/docs/DEPLOYMENT.md
@@ -1,160 +1,244 @@
# ChronoSeal Deployment Guide
-ChronoSeal v0.6.0 is designed for production deployment as a Unix-native daemon with hardened systemd support, lightweight WASM client runtime, and flexible backend storage.
+ChronoSeal is intended to run as a small Unix daemon behind TLS, with static browser assets served either by the daemon or by the same protected origin. This guide covers native, service, and container deployment.
-## Prerequisites
+## Deployment Model
-| Tool | Minimum version | Purpose |
-|---|---|---|
-| Rust | 1.87 stable | Server and WASM compilation |
-| wasm-pack | 0.13 | WASM build and packaging |
-| Docker | 24.x | Optional container deployment |
-| docker-compose | 2.x | Optional local orchestration |
-| systemd | 248+ | Service management |
+Typical production topology:
-Install Rust: [https://rustup.rs](https://rustup.rs)
-Install wasm-pack: `cargo install wasm-pack`
+```text
+Internet
+ |
+ v
+TLS reverse proxy
+ |
+ v
+chronoseal daemon on 127.0.0.1:3000
+ |
+ v
+sqlite-in-disk or valkey storage
+```
----
+For local evaluation, the daemon can bind directly to `0.0.0.0:3000` or `127.0.0.1:3000`.
-## Build Steps
+## Requirements
-### 1. Build the WASM module
+| Tool | Minimum | Purpose |
+|---|---:|---|
+| Rust | 1.87 stable | Build server and shared crates |
+| `wasm32-unknown-unknown` target | current stable | Compile WASM runtime |
+| `wasm-pack` | 0.13 | Generate browser WASM package |
+| systemd | 248+ | Native service management |
+| Docker | 24.x | Optional container image |
+| Docker Compose | 2.x | Optional local orchestration |
+
+Install Rust from rustup, then install the WASM tooling:
```bash
rustup target add wasm32-unknown-unknown
cargo install wasm-pack
-wasm-pack build wasm --target web --release
-rm -rf frontend/pkg
-mv wasm/pkg frontend/pkg
```
-This produces the browser runtime assets required by the frontend and the server static file handler.
+## Build
-### 2. Build the server binary
-
-```bash
-cargo build -p server --release
-```
-
-Binary output: `target/release/server`
-
-### 3. Convenience script
+Use the repository build script:
```bash
bash scripts/build.sh
```
-This script builds the WASM module, moves the generated package into `frontend/pkg`, and builds the server binary.
+The script:
----
+1. Builds `wasm/` with `wasm-pack build --target web --release`.
+2. Replaces `frontend/pkg` with the generated package.
+3. Builds the release daemon binary.
-## Deploying as a Native Service
+Manual equivalent:
-ChronoSeal is intended to run as a proper Unix daemon managed by systemd.
+```bash
+wasm-pack build wasm --target web --release
+rm -rf frontend/pkg
+mv wasm/pkg frontend/pkg
-### Install
+cargo build -p chronoseal-server --bin chronoseal --release
+```
+
+Release binary:
+
+```text
+target/release/chronoseal
+```
+
+## Native Install
+
+The installer builds, installs, enables, and starts the service:
```bash
sudo bash scripts/install.sh
```
-This installer should perform the following tasks:
+Installer actions:
-* create a system user for `chronoseal`
-* install the server binary into `/usr/local/bin/chronoseal`
-* install static frontend assets into `/opt/chronoseal/frontend`
-* install `chronoseal.service` into `/etc/systemd/system/`
-* enable and start the service
+- create the `chronoseal` system user if missing
+- build WASM and server artifacts
+- install `target/release/chronoseal` to `/usr/local/bin/chronoseal`
+- copy `frontend/` to `/opt/chronoseal/frontend`
+- install `chronoseal.service` to `/etc/systemd/system/chronoseal.service`
+- reload systemd
+- enable and start the service
-### Verify the service
+Verify:
```bash
sudo systemctl status chronoseal
+chronoseal status --format json
+chronoseal health
+sudo journalctl -u chronoseal -f
+```
+
+## Running Without Install
+
+For local development:
+
+```bash
+bash scripts/build.sh
+cargo run -p chronoseal-server --bin chronoseal -- run \
+ --bind 127.0.0.1:3000 \
+ --frontend-dir frontend
+```
+
+Probe the daemon:
+
+```bash
+curl http://127.0.0.1:3000/health
+curl http://127.0.0.1:3000/stats
+curl http://127.0.0.1:3000/metrics
+```
+
+## Configuration
+
+ChronoSeal resolves configuration in this order:
+
+1. CLI flags
+2. `CHRONOSEAL_*` environment variables
+3. TOML config file
+4. built-in defaults
+
+Default config discovery:
+
+1. `CHRONOSEAL_CONFIG`, if it points to an existing file
+2. `/etc/chronoseal/config.toml`
+3. `$XDG_CONFIG_HOME/chronoseal/config.toml`
+4. `~/.config/chronoseal/config.toml`
+
+Validate effective configuration:
+
+```bash
+chronoseal config check --format yaml
+```
+
+Example:
+
+```toml
+bind = "127.0.0.1:3000"
+db_type = "sqlite-in-disk"
+pid_file = "/run/chronoseal.pid"
+db_path = "/var/lib/chronoseal/chronoseal.sqlite"
+frontend_dir = "/usr/share/chronoseal/frontend"
+log_file = "/var/log/chronoseal/chronoseal.jsonl"
+
+heartbeat_min_interval_ms = 12000
+heartbeat_max_interval_ms = 25000
+expiration_minutes = 30
+rate_limit_count = 5
+rate_limit_window_secs = 10
+max_timestamp_drift_ms = 30000
+
+min_mouse_total_dist = 10.0
+max_mouse_avg_speed = 2.0
+min_pause_count = 1
+require_mouse_activity = true
+
+gene_size = 512
+mutation_rounds = 4
+```
+
+## Storage Backends
+
+| Backend | `db_type` | Use case |
+|---|---|---|
+| SQLite memory | `sqlite-in-memory` | ephemeral local or stateless deployment |
+| SQLite disk | `sqlite-in-disk` | persisted session continuity across restarts |
+| Valkey | `valkey` | external session storage |
+
+For disk persistence:
+
+```bash
+sudo mkdir -p /var/lib/chronoseal
+sudo chown -R chronoseal:chronoseal /var/lib/chronoseal
+```
+
+For Valkey:
+
+```bash
+export CHRONOSEAL_DB_TYPE=valkey
+export CHRONOSEAL_VALKEY_ADDR=127.0.0.1:6666
+```
+
+If Valkey connection setup fails, the current implementation logs a warning and falls back to in-memory SQLite.
+
+## systemd
+
+The supplied service file is intended as the baseline unit. Keep the daemon under a dedicated user and restrict filesystem access to the paths it needs.
+
+Useful commands:
+
+```bash
+sudo systemctl daemon-reload
+sudo systemctl enable --now chronoseal
+sudo systemctl restart chronoseal
+sudo systemctl status chronoseal
sudo journalctl -u chronoseal -f
```
-### Recommended runtime options
+Recommended hardening properties include:
-Use structured info-level logging in production:
+- `NoNewPrivileges=true`
+- `PrivateTmp=true`
+- `ProtectSystem=strict`
+- `ProtectHome=true`
+- `ProtectKernelTunables=true`
+- `ProtectKernelModules=true`
+- `ProtectControlGroups=true`
+- `MemoryDenyWriteExecute=true`
+- `RestrictRealtime=true`
+- `RestrictSUIDSGID=true`
+- `SystemCallArchitectures=native`
-```bash
-export RUST_LOG=info
-sudo systemctl restart chronoseal
-```
+Any hardening must still allow access to:
-Avoid `RUST_LOG=debug` in production because debug logs can expose internal session identifiers.
-
----
-
-## systemd Integration
-
-The supplied `chronoseal.service` is designed for hardened Unix-native operation.
-
-Recommended service options:
-
-* `NoNewPrivileges=true`
-* `PrivateTmp=true`
-* `ProtectSystem=strict`
-* `ProtectHome=true`
-* `ProtectKernelTunables=true`
-* `ProtectKernelModules=true`
-* `ProtectControlGroups=true`
-* `MemoryDenyWriteExecute=true`
-* `RestrictRealtime=true`
-* `RestrictSUIDSGID=true`
-* `SystemCallArchitectures=native`
-
-These options reduce the host attack surface and keep the daemon constrained to its required runtime privileges.
-
----
-
-## Configuration
-
-ChronoSeal reads configuration from a TOML file, environment variables, and CLI overrides. Use `chronoseal config` to validate the effective configuration.
-
-Example runtime configuration options:
-
-```toml
-bind = "0.0.0.0:3000"
-pid_file = "/run/chronoseal.pid"
-log_level = "info"
-db_type = "sqlite-in-memory"
-db_path = "/var/lib/chronoseal/chronoseal.db"
-```
-
-### Supported `db_type`
-
-* `sqlite-in-memory`
-* `sqlite-disk`
-* `valkey`
-
-`sqlite-in-memory` is the default and preserves ephemeral session semantics.
-
-`sqlite-disk` will persist session state to a file specified by `db_path`.
-
-`valkey` selects the Valkey-compatible backend mode and may be useful for future deployment scenarios.
-
----
+- the binary
+- frontend assets
+- PID file directory
+- optional log file directory
+- SQLite database directory, if using `sqlite-in-disk`
## Reverse Proxy and TLS
-ChronoSeal should be served over HTTPS in production. The heartbeat protocol includes timestamps and entropy data; serving that traffic in plaintext weakens security and allows easier traffic analysis.
+ChronoSeal should be served over HTTPS in production. Terminate TLS at a reverse proxy or load balancer and proxy to the local daemon.
-### nginx example
+Minimal nginx example:
```nginx
server {
listen 443 ssl http2;
- server_name your.domain.com;
+ server_name example.com;
- ssl_certificate /etc/letsencrypt/live/your.domain.com/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/your.domain.com/privkey.pem;
- ssl_protocols TLSv1.3;
- ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
+ ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
- proxy_read_timeout 35s;
- proxy_send_timeout 10s;
+ proxy_read_timeout 35s;
+ proxy_send_timeout 10s;
location / {
proxy_pass http://127.0.0.1:3000;
@@ -168,41 +252,79 @@ server {
server {
listen 80;
- server_name your.domain.com;
+ server_name example.com;
return 301 https://$host$request_uri;
}
```
-### Docker deployment
+Keep `/init`, `/hb`, and frontend assets on the same origin when possible. If you split origins, configure CORS and cookie/application policy deliberately.
+
+## Docker
+
+Build and run:
```bash
+bash scripts/build.sh
docker compose up -d --build
```
-The supplied `docker-compose.yml` is intended for local evaluation and development. It mounts `frontend/` and exposes port `3000`.
+The Compose file exposes port `3000`.
-Note: build the WASM package before container startup, or mount a pre-built `frontend/pkg/` volume.
+```bash
+curl http://127.0.0.1:3000/health
+```
----
+The Dockerfile copies `frontend/` from the working tree. Build `frontend/pkg` before building the image when the browser WASM runtime is required inside the container.
-## Production Best Practices
+## Observability
-* Use TLS termination at the perimeter
-* Run ChronoSeal behind a reverse proxy or firewall
-* Keep `RUST_LOG` at `info` or `warn`
-* Use `systemctl` for lifecycle management
-* Monitor `chronoseal` metrics with Prometheus
-* Place the frontend under the same origin as the protected pages or configure CORS carefully
+CLI:
----
+```bash
+chronoseal status --format json
+chronoseal health
+chronoseal stats --format json
+chronoseal metrics
+```
-## Health and Metrics
+HTTP:
-ChronoSeal exposes runtime endpoints for health and metrics.
+```bash
+curl http://127.0.0.1:3000/health
+curl http://127.0.0.1:3000/stats
+curl http://127.0.0.1:3000/metrics
+```
-* `chronoseal health` — health probe
-* `chronoseal metrics` — Prometheus metrics output
-* `chronoseal status` — runtime status report
-* `chronoseal stats` — runtime statistics
+Prometheus metrics:
-These endpoints are accessible locally from the daemon and may be proxied or scraped by monitoring infrastructure.
+- `chronoseal_sessions`
+- `chronoseal_expired_sessions`
+- `chronoseal_max_chain_length`
+
+## Logging
+
+Use info-level logs for production:
+
+```bash
+CHRONOSEAL_LOG=info chronoseal run
+```
+
+or with systemd:
+
+```bash
+sudo systemctl edit chronoseal
+```
+
+Avoid debug logging in production because internal identifiers may be written to logs.
+
+## Production Checklist
+
+- Build `frontend/pkg` before packaging.
+- Serve ChronoSeal traffic over HTTPS.
+- Bind the daemon to localhost behind a reverse proxy unless direct exposure is required.
+- Use a dedicated service user.
+- Keep debug logs disabled.
+- Choose storage intentionally: `sqlite-in-memory`, `sqlite-in-disk`, or `valkey`.
+- Protect SQLite and log directories with correct ownership.
+- Monitor `/health`, `/stats`, and `/metrics`.
+- Verify `chronoseal config check` after environment or config changes.
diff --git a/docs/DESIGN-PHILOSOPHY.md b/docs/DESIGN-PHILOSOPHY.md
index 1f0fc98..48c9fe3 100644
--- a/docs/DESIGN-PHILOSOPHY.md
+++ b/docs/DESIGN-PHILOSOPHY.md
@@ -1,58 +1,125 @@
# ChronoSeal Design Philosophy
-ChronoSeal is built for operators who value clarity, stability, and Unix-native infrastructure.
+ChronoSeal is designed for operators who want a local, inspectable, Unix-native browser attestation layer rather than a hosted anti-bot black box.
-## Core Philosophy
+## Core Position
-ChronoSeal is a Unix-first, CLI-first cryptographic attestation daemon. It is intentionally designed to feel like infrastructure software such as `nginx`, `redis-server`, or `systemd` itself.
+ChronoSeal is infrastructure software. It should feel closer to `nginx`, `redis-server`, or a small system daemon than to a third-party analytics platform.
-### Design priorities
+Design priorities:
-* **Unix-native operation** — systemd integration, PID files, structured logs, and predictable lifecycle semantics.
-* **CLI as source of truth** — all runtime operations available through the command line.
-* **Minimal opacity** — no hidden telemetry, no opaque fingerprinting database.
-* **Privacy-first** — ephemeral session state and no persistent user profiling.
-* **Deterministic runtime behavior** — shared Rust/WASM implementation for the mutation engine and heartbeat protocol.
-* **Incremental cost escalation** — make automation painful to scale without claiming impossible security.
-* **Operational transparency** — expose health, metrics, status, and config as first-class artifacts.
+- CLI-first operation
+- explicit configuration
+- deterministic protocol behavior
+- small runtime surface
+- privacy-preserving state
+- observable health and metrics
+- no hidden telemetry
+- no persistent user profiling
-## Execution Model
+## What ChronoSeal Optimizes For
-ChronoSeal emphasizes deterministic, stateless request validation with a lightweight server-side session store.
+### Operator Control
-* The server persists only the small session state required for continuity.
-* The client executes a deterministic WASM runtime for every heartbeat.
-* The protocol is intentionally ambiguous on rejection to avoid leaking validation rules.
+Operators should be able to build, run, inspect, configure, monitor, and stop the service with ordinary Unix tools.
+
+This is why ChronoSeal provides:
+
+- `chronoseal run`
+- `chronoseal status`
+- `chronoseal health`
+- `chronoseal config check`
+- `chronoseal metrics`
+- `chronoseal stats`
+- shell completions
+- systemd integration
+
+### Determinism
+
+The protocol depends on deterministic agreement between server Rust and browser WASM.
+
+Shared logic belongs in `shared/` when divergence would create security or correctness risk. This includes:
+
+- protocol structs
+- hash-chain semantics
+- synthetic gene model
+- mutation opcode behavior
+- mutation order encoding
+
+### Cost Escalation
+
+ChronoSeal does not claim impossible security. It raises the cost of automation by making clients maintain:
+
+- a browser-local signing key
+- a signed canonical heartbeat payload
+- a Blake3 hash chain
+- VM execution output
+- server-issued mutation progression
+- plausible timing and interaction signals
+
+The objective is to make cheap automation brittle and expensive automation more complex.
+
+### Silent Rejection
+
+Heartbeat rejection is intentionally ambiguous. Invalid heartbeats receive the same `status` value as accepted heartbeats, but accepted responses include next-state fields.
+
+This avoids turning the API into a validation oracle. Integrators must check for `next_salt`, `next_mutation_step`, and `next_mutation_order_b64`.
+
+### Privacy
+
+ChronoSeal should not become a surveillance system.
+
+It avoids:
+
+- long-term user identifiers
+- browser history
+- cross-site identity graphs
+- fingerprint databases
+- behavioral profiling as a product feature
+
+It stores only the session state required for continuity.
## Non-Goals
-ChronoSeal does not aim to be:
+ChronoSeal is not:
-* a tracking platform
-* a browser fingerprinting database
-* a long-term behavioral analytics engine
-* a platform for user profiling
-* a SaaS or cloud-first service
-
-Instead, ChronoSeal aims to be an infrastructure layer that raises attacker cost while leaving legitimate users unobstructed.
+- a CAPTCHA
+- a fraud scoring engine
+- an authentication provider
+- a hosted SaaS product
+- a persistent fingerprinting system
+- a replacement for authorization checks
+- a complete defense against real browser farms
## Operational Assumptions
ChronoSeal assumes:
-* the host environment is Linux
-* systemd is available for service management
-* TLS is used in production
-* browser clients can execute WASM
-* operators can manage native binaries and configuration files
+- Linux or a Unix-like host
+- systemd for production service management
+- TLS in production
+- browser clients can execute WASM
+- operators can manage config files and service users
+- application owners decide how attestation status gates protected resources
-## Privacy and Trust
+## Engineering Biases
-The project is designed so that the verification mechanism is:
+When the project faces tradeoffs, prefer:
-* ephemeral
-* difficult to reverse-engineer at scale
-* not based on personal identifiers
-* not dependent on long-term user history
+- explicit configuration over implicit magic
+- server-side recomputation over browser trust
+- bounded deterministic execution over unbounded heuristics
+- clear CLI output over hidden dashboards
+- local deployment over mandatory cloud dependencies
+- privacy by data minimization over privacy by policy alone
-These choices reflect the belief that the best anti-automation system is one that can be operated without becoming a surveillance platform.
+## Success Criteria
+
+ChronoSeal is succeeding when:
+
+- legitimate browser sessions advance without user friction
+- simple scrapers cannot pass the protocol
+- automation requires a full stateful implementation
+- operators can debug deployments with normal Unix tools
+- stored data remains minimal and short-lived
+- documentation reflects the implementation precisely
diff --git a/docs/PRIVACY POLICY.md b/docs/PRIVACY POLICY.md
index 3144f08..467f370 100644
--- a/docs/PRIVACY POLICY.md
+++ b/docs/PRIVACY POLICY.md
@@ -1,66 +1,106 @@
# ChronoSeal Privacy Policy
-ChronoSeal is a privacy-first cryptographic attestation system. It is intentionally designed to avoid long-term profiling, tracking, and persistent identity storage.
+ChronoSeal is a privacy-oriented browser attestation system. It is designed to validate short-lived session continuity without creating persistent user profiles.
-## What ChronoSeal Collects
+This document describes what ChronoSeal itself collects and stores. Applications that integrate ChronoSeal may collect additional data under their own policies.
-ChronoSeal only collects the minimum ephemeral data required to validate a live browser session:
+## Data ChronoSeal Processes
-* `session_id` — ephemeral session identifier
-* `prev_hash` / `initial_hash` — cryptographic chain state
-* `timestamp` — heartbeat timing information
-* `entropy_data` — recent mouse event samples for behavioral plausibility
-* `stack_state` — VM execution result for heartbeat uniqueness
-* `fingerprint` signals — basic browser sanity values such as aspect ratio, DPR, and hardware concurrency
-* `mutation_step` / `gene_commitment` — synthetic mutation parity values for protocol continuity
+ChronoSeal processes the minimum protocol data needed to validate a live browser session.
-## What ChronoSeal Does Not Store
+| Data | Purpose |
+|---|---|
+| `session_id` | Opaque session lookup key |
+| public key | Verify signed heartbeats for the session |
+| `salt` | Hash-chain progression |
+| `initial_hash` / `prev_hash` / `last_hash` | Replay-resistant continuity |
+| `timestamp` | Drift and liveness validation |
+| mouse event samples | Behavioral plausibility checks |
+| VM stack state | Input to hash-chain progression |
+| basic fingerprint fields | Sanity validation |
+| gene bytes and environment records | Mutation continuity |
+| pending mutation program and step | Next heartbeat verification |
+| expiration and last-seen timestamps | Session lifecycle and cleanup |
-ChronoSeal does not store or persist:
+Basic fingerprint fields currently include:
-* IP addresses as a core artifact
-* browser history
-* user identifiers
-* personal data
-* device fingerprint databases
-* long-term behavioral profiles
-* cross-session tracking records
+- aspect ratio
+- device pixel ratio
+- hardware concurrency
-If you need browser telemetry or user profiling, ChronoSeal is not the right tool.
+## Data ChronoSeal Does Not Intentionally Collect
-## Session Ephemerality
+ChronoSeal does not intentionally collect or build:
-By default, ChronoSeal uses `sqlite-in-memory` storage. Sessions are ephemeral and are expected to be recreated after process restarts.
+- browser history
+- page content history
+- account identity
+- email addresses
+- names
+- payment data
+- location history
+- cross-site tracking identifiers
+- persistent fingerprint databases
+- long-term behavioral profiles
-Persistent state is only stored when the operator explicitly configures `sqlite-disk` or `valkey`.
+ChronoSeal is not intended for analytics, advertising, or identity graph construction.
+
+## Session Lifetime
+
+Sessions are short-lived and expire according to `expiration_minutes`, which defaults to 30 minutes.
+
+Expired sessions are removed by cleanup behavior. In-memory storage is lost when the process exits.
+
+## Storage Modes and Persistence
+
+| Mode | Persistence |
+|---|---|
+| `sqlite-in-memory` | process lifetime only |
+| `sqlite-in-disk` | persisted to the configured SQLite file |
+| `valkey` | persisted according to the Valkey deployment configuration |
+
+Persistent state is operator-selected. The default backend is `sqlite-in-memory`.
## Client-Side Key Handling
-The Ed25519 signing keypair is generated inside the WASM runtime and is never serialized or transmitted in full.
+The browser WASM runtime generates an Ed25519 keypair for the session.
-* Private key: stays inside WASM linear memory
-* Public key: transmitted once during session initialization
+- The public key is sent to `/init`.
+- The private key is not sent to the server.
+- Heartbeat payloads are signed in the browser runtime.
-This design minimizes the amount of sensitive material exposed outside the browser runtime.
+This is a continuity mechanism, not a long-term identity mechanism.
-## Intentional Silent Rejection
+## Silent Rejection
-ChronoSeal intentionally returns a uniform `{"status":"ok"}` response for invalid heartbeats.
+ChronoSeal returns the same basic heartbeat status for accepted and rejected heartbeat requests:
-This is a privacy-preserving decision: it avoids emitting detailed rejection reasons that could be used to fingerprint or probe clients.
+```json
+{
+ "status": "ok"
+}
+```
-## Data Retention
+Accepted responses additionally include next-state fields. Rejected responses omit them.
-Session state is retained only as long as it is needed for heartbeat continuity.
+This reduces attacker feedback and avoids returning detailed failure classifications to clients.
-Expired sessions are purged automatically by cleanup tasks. Ephemeral backend modes do not write state to disk beyond the current process lifetime.
+## Logs
-## Transparency
+Operators control logging through `CHRONOSEAL_LOG`, `RUST_LOG`, and optional log-file configuration.
-The source code is open and the verification model is documented. Operators can inspect exactly what ChronoSeal stores and validates.
+Production deployments should avoid debug logging because internal session identifiers or validation context may appear in logs.
+
+## Operator Responsibilities
+
+Operators should:
+
+- serve traffic over HTTPS
+- protect SQLite, Valkey, and log storage
+- restrict access to metrics and stats endpoints
+- choose persistence mode deliberately
+- disclose any application-level data collection separately
## Summary
-ChronoSeal is designed to provide anti-automation defense without becoming a tracking or surveillance platform.
-
-It is a privacy-aware, ephemeral attestation layer with strong operational guardrails.
+ChronoSeal validates live session continuity using short-lived cryptographic and deterministic state. It is designed to raise automation cost without becoming a persistent tracking or profiling system.
diff --git a/docs/REFRACTORING-v0.6.0.md b/docs/REFRACTORING-v0.6.0.md
index dea8b73..b831188 100644
--- a/docs/REFRACTORING-v0.6.0.md
+++ b/docs/REFRACTORING-v0.6.0.md
@@ -1,118 +1,191 @@
-# ChronoSeal v0.6.0 — Refactoring and System Upgrade
+# ChronoSeal v0.6.0 Refactoring and System Upgrade
-ChronoSeal v0.6.0 is a major architecture and protocol update that transforms the project from a lightweight heartbeat service into a mature Unix-native attestation daemon with deterministic mutation parity and pluggable storage backends.
+ChronoSeal v0.6.0 changed the project from a lightweight heartbeat prototype into a Unix-native attestation daemon with shared server/WASM protocol logic, deterministic mutation parity, operational CLI commands, and pluggable storage modes.
-## Summary of Changes
+This document summarizes the architectural changes introduced in the v0.6.0 line.
-* Introduced the **Synthetic Gene Mutation Engine** for deterministic mutation parity across server and WASM.
-* Added server-side validation of `mutation_step` and `gene_commitment`.
-* Centralized shared protocol logic in `shared/` for server/WASM parity.
-* Added support for multiple storage backend modes: `sqlite-in-memory`, `sqlite-disk`, and `valkey` compatibility.
-* Hardened runtime architecture with `systemd` readiness, graceful shutdown, PID file support, and structured logging.
-* Expanded CLI with rich subcommands and effective runtime configuration.
-* Preserved silent rejection semantics while improving anti-replay and liveness guarantees.
+## Summary
-## Why This Refactor?
+Major changes:
-The previous model relied on heartbeat continuity and behavioral entropy alone. v0.6.0 strengthens the protocol by adding a second, deterministic state progression channel:
+- introduced the Synthetic Gene Mutation Engine
+- added server-side validation of `mutation_step` and `gene_commitment`
+- moved protocol and deterministic mutation logic into `shared/`
+- added `chronoseal-wasm` browser runtime support for mutation preview and commit
+- expanded persisted session state with gene and pending mutation fields
+- added storage modes: `sqlite-in-memory`, `sqlite-in-disk`, and `valkey`
+- added health, metrics, stats, config, status, completion, and version CLI surfaces
+- added PID file handling, structured logging, and graceful shutdown behavior
+- preserved silent heartbeat rejection semantics
-* each heartbeat now includes a mutation step and commitment
-* the server authoritatively selects the next mutation program
-* the client must preview and commit the same state locally in WASM
-* the server rejects any mismatch silently
+## Motivation
-This raises the cost of developing a successful automation attack because the attacker must now maintain both a valid chain and a valid mutation progression state.
+The earlier model relied mainly on:
-## Core Architecture Changes
+- heartbeat timing
+- behavioral entropy
+- hash-chain continuity
+- signature verification
-### Shared Protocol Code
+v0.6.0 added a second deterministic state channel: a server-authored synthetic gene mutation sequence. This makes successful automation maintain both:
-`shared/` now contains:
+- the cryptographic hash/signature chain
+- the synthetic mutation state expected by the server
-* gene model and commitment hashing
-* mutation opcode semantics
-* request/response payload structures
-* canonical signing support
-* VM execution logic shared by server and WASM
+## Shared Crate Refactor
-Moving mutation semantics into `shared/` eliminates subtle server/client divergence bugs and enables deterministic cross-runtime testing.
+`shared/` now owns the parts of the protocol that must remain identical across server and browser runtime:
-### Mutation Handshake
+- request and response structs
+- hashing helpers
+- synthetic gene state
+- mutation environment encoding
+- mutation order generation and encoding
+- opcode execution semantics
+- protocol constants
-v0.6.0 adds the following data to the protocol:
+This reduces the risk of server/WASM drift.
-* `mutation_step`
-* `mutation_order_b64`
-* `gene_commitment`
-* `next_mutation_step`
-* `next_mutation_order_b64`
+## Mutation Handshake
-These fields are now part of the session initialization and heartbeat exchange.
+New protocol fields:
-### Server Session State
+- `gene_size`
+- `mutation_step`
+- `mutation_order_b64`
+- `gene_commitment`
+- `next_mutation_step`
+- `next_mutation_order_b64`
-The session schema now stores:
+Lifecycle:
-* committed gene bytes
-* committed environment records
-* pending mutation order
-* pending mutation step
+1. `/init` returns mutation step 1 and a server-authored mutation order.
+2. The browser previews the mutation in WASM.
+3. The browser signs and submits the resulting `gene_commitment`.
+4. The server applies the same pending mutation to its committed state.
+5. The server compares commitments.
+6. On success, server commits the candidate state and issues the next mutation.
+7. The browser commits its preview only after receiving the accepted response.
-The server advances this state only after a heartbeat is accepted.
+## Session Schema Changes
-### Deterministic WASM Preview
+The persisted session record now includes:
-The WASM runtime exposes:
+- committed gene bytes
+- encoded environment records
+- pending mutation program
+- pending mutation step
-* `init_gene_state()`
-* `preview_gene_commitment()`
-* `commit_gene_preview()`
-* `discard_gene_preview()`
-* `current_gene_commitment()`
+State advances only after a heartbeat is accepted. Rejected heartbeats do not rotate salt, update hash state, commit gene state, or consume the pending mutation.
-This makes the client-side mutation lifecycle explicit and deterministic.
+## WASM Runtime Changes
-### Backend Abstraction
+The WASM crate now supports:
-The server runtime now supports a configurable `db_type`.
+- `generate_keypair()`
+- `get_public_key()`
+- `sign_message()`
+- `compute_next_hash()`
+- `run_program()`
+- `init_gene_state()`
+- `preview_gene_commitment(order_b64, session_id, mutation_step, rounds)`
+- `commit_gene_preview()`
+- `discard_gene_preview()`
+- `current_gene_commitment(session_id, mutation_step)`
-* `sqlite-in-memory` — default runtime storage with ephemeral session semantics
-* `sqlite-disk` — persistent SQLite storage for stateful deployments
-* `valkey` — compatibility mode for alternative storage backends
+The generated package uses the `chronoseal_wasm` prefix.
-This abstraction makes ChronoSeal easier to operate in both stateless and stateful environments.
+## Storage Refactor
-### CLI and Service Integration
+The storage layer is abstracted behind `DbPool`.
-v0.6.0 improves the CLI surface with operational commands and service introspection.
+Supported modes:
-* `chronoseal run`
-* `chronoseal status`
-* `chronoseal health`
-* `chronoseal config`
-* `chronoseal metrics`
-* `chronoseal stats`
-* `chronoseal db-type`
-* `chronoseal completion`
-* `chronoseal version`
+| Mode | Behavior |
+|---|---|
+| `sqlite-in-memory` | default ephemeral in-process SQLite |
+| `sqlite-in-disk` | persisted SQLite database at `db_path` |
+| `valkey` | Valkey-compatible external store |
-The runtime now includes PID file handling and graceful termination.
+The storage interface supports insert, load, update, delete expired sessions, and stats.
-## Testing and Validation
+## CLI and Runtime Changes
-The refactor includes extensive tests for:
+The `chronoseal` binary now provides:
-* server/WASM parity across mutation sequences
-* malformed mutation payload rejection
-* replay attack rejection
-* mutation step mismatch rejection
-* stateful session update semantics
-* runtime database mode validation
+- `run`
+- `status`
+- `health`
+- `config check`
+- `generate keypair`
+- `version`
+- `db-type`
+- `metrics`
+- `stats`
+- `completion`
-The codebase now supports deterministic table-driven tests and fuzz-style random program validation.
+The daemon exposes:
+
+- `POST /init`
+- `POST /hb`
+- `GET /health`
+- `GET /metrics`
+- `GET /stats`
+- static frontend serving at `/`
+
+## Validation Improvements
+
+The heartbeat verifier now checks:
+
+- session presence
+- expiration
+- signature
+- hash-chain continuity
+- mutation step
+- mutation commitment parity
+- timestamp drift
+- behavioral mouse checks
+- fingerprint ranges
+- rate limiting at the route layer
+
+Accepted heartbeats return next-state fields. Rejected heartbeats return only `{"status":"ok"}`.
+
+## Testing Impact
+
+The refactor added or strengthened tests for:
+
+- gene environment encoding and validation
+- mutation opcode behavior
+- mutation order round-trips
+- deterministic mutation generation with seeded RNG
+- server/client mutation parity
+- random program divergence resistance
+- replay rejection
+- mutation step mismatch rejection
+- mutation commitment tamper rejection
+- storage backend stats
+- route-level silent rejection behavior
## Operational Impact
-This release makes ChronoSeal suitable for production deployment in Linux environments and for integration into existing web application stacks.
+v0.6.0 makes ChronoSeal more suitable for deployment as a real service:
-The combination of deterministic mutation parity and shared protocol implementation improves both security and maintainability.
+- explicit daemon lifecycle
+- CLI-first operations
+- systemd-oriented install path
+- health and metrics endpoints
+- configurable persistence
+- shared protocol implementation
+- clearer docs and threat model
+
+## Compatibility Notes
+
+Important names in the current implementation:
+
+- binary: `chronoseal`
+- server crate: `chronoseal-server`
+- WASM crate: `chronoseal-wasm`
+- generated WASM module prefix: `chronoseal_wasm`
+- persistent SQLite mode: `sqlite-in-disk`
+
+Older docs or integrations may refer to `sqlite-disk`, `server`, or `antibot_wasm`; those names are stale for the current codebase.
diff --git a/docs/THREAT_MODEL.md b/docs/THREAT_MODEL.md
index 5d4feef..2f9767d 100644
--- a/docs/THREAT_MODEL.md
+++ b/docs/THREAT_MODEL.md
@@ -1,137 +1,240 @@
# ChronoSeal Threat Model
-ChronoSeal is a cost-raising cryptographic attestation daemon. It increases the burden on automated clients while preserving privacy, determinism, and operational transparency.
+ChronoSeal is a cost-raising browser attestation layer. It makes replay, stale state reuse, and incomplete automation more expensive by requiring signed, continuous, deterministic browser-side state progression.
-## Purpose
+It is not a perfect bot blocker, CAPTCHA replacement, hardware attestation system, fraud engine, or identity provider.
-ChronoSeal protects web resources by making browser automation and replay attacks more expensive and fragile. It is not intended to be a perfect bot blocker.
+## Security Objectives
+
+ChronoSeal aims to:
+
+- reject stale or replayed heartbeat payloads
+- reject heartbeats that do not maintain the server-issued mutation sequence
+- bind heartbeat payloads to a browser-local Ed25519 session key
+- make basic HTTP clients insufficient
+- make browser automation maintain multiple synchronized state channels
+- avoid detailed rejection feedback
+- preserve privacy by avoiding persistent user identity state
## Protected Assets
| Asset | Protection focus |
|---|---|
-| Page content | Prevent automated scraping and replay of protected content |
-| API responses | Reduce scripted access to sensitive endpoints |
-| Server compute | Increase attacker resource costs |
-| Session continuity | Enforce live session progression |
-| Behavioral integrity | Validate plausible browser activity |
+| Protected page/API access | Require live attestation before allowing continued access |
+| Session continuity | Ensure each accepted heartbeat advances from the last accepted state |
+| Server compute | Rate-limit and reject invalid clients without expensive application work |
+| Protocol state | Protect hash-chain, salt, and mutation progression |
+| User privacy | Avoid long-term tracking and detailed failure disclosure |
-## Attacker Profiles
+## Trust Assumptions
-### Level 1 — Commodity Scraper
+ChronoSeal assumes:
-* Tools: `curl`, `requests`, headless HTTP clients
-* Capability: no WASM execution, no browser engine
+- the server host and daemon process are trusted
+- storage is trusted for session continuity
+- TLS protects traffic in production
+- browser clients can run JavaScript and WASM
+- operators configure reverse proxy, filesystem permissions, and logs appropriately
-ChronoSeal response:
+ChronoSeal does not assume:
-* cannot initialize a session
-* no `session_id` is produced
-* content remains protected behind the attestation layer
+- the browser is honest
+- WASM is a secure enclave
+- mouse data proves human presence
+- fingerprint values are unforgeable
+- attackers cannot run a full browser
-### Level 2 — Headless Browser Operator
+## Attacker Levels
-* Tools: Playwright, Puppeteer, Selenium
-* Capability: browser engine available, but automation is not indistinguishable from a real user
+### Level 1: Commodity HTTP Client
-ChronoSeal response:
+Examples:
-* mouse entropy and pause checks become active barriers
-* hash chain continuity requires per-session state tracking
-* synthetic heartbeats become expensive to maintain at scale
+- `curl`
+- `requests`
+- scraper scripts without browser or WASM execution
-### Level 3 — Stealth Automation
+Expected result:
-* Tools: browser stealth plugins, CDP patching, synthetic event injection
-* Capability: can execute JavaScript and WASM, may spoof some browser signals
+- cannot produce valid signatures
+- cannot maintain hash-chain state
+- cannot execute mutation preview
+- cannot produce accepted heartbeats
-ChronoSeal response:
+### Level 2: Basic Headless Browser
-* signature, hash chain, and mutation commitment require correct WASM execution
-* private key is generated per page load and never exposes raw key material
-* silent rejection hides validation rules from attacker feedback
+Examples:
-### Level 4 — Sophisticated Operator
+- Playwright
+- Puppeteer
+- Selenium
-* Tools: real browser farms, hardware input devices, custom chain management
-* Capability: high engineering investment and real device scale
+Expected result:
-ChronoSeal response:
+- can load JavaScript and WASM
+- must preserve keypair, hash chain, salt, VM, and mutation state
+- must generate plausible timing and mouse event windows
+- silent rejection complicates debugging and scaling
-* significantly increases operational cost and complexity
-* forces a full protocol implementation rather than best-effort scraping
-* is not designed to stop such adversaries completely
+### Level 3: Stealth Automation
+
+Examples:
+
+- patched browser runtime
+- synthetic event generation
+- custom protocol client with WASM or Rust reimplementation
+
+Expected result:
+
+- can attempt full protocol implementation
+- must still match canonical signing, hash progression, mutation parity, and timing
+- must handle changing server-issued mutation programs
+- receives limited failure feedback
+
+### Level 4: Resourced Browser Farm
+
+Examples:
+
+- real browsers
+- realistic input devices
+- human-assisted workflows
+- distributed session management
+
+Expected result:
+
+- ChronoSeal raises cost and complexity
+- ChronoSeal does not claim complete prevention
+- additional application-level controls are required
## Attack Vectors and Mitigations
-### Replay Attack
+### Replay
-**Attack:** resend a previously observed heartbeat.
+Attack: resend a previously accepted heartbeat.
-**Mitigations:**
+Mitigations:
-* timestamp window enforcement (±30 seconds)
-* chained Blake3 hash continuity
-* server-issued salt rotation
-* mutation step progression
+- stored `last_hash` must match request `prev_hash`
+- accepted heartbeats rotate salt
+- mutation step advances after acceptance
+- timestamp drift is bounded
### Signature Forgery
-**Attack:** forge a heartbeat without the private key.
+Attack: submit a heartbeat without the browser session private key.
-**Mitigations:**
+Mitigations:
-* Ed25519 signature over the canonical payload
-* private key generated and stored inside WASM memory only
-* signature verification occurs on every heartbeat
+- Ed25519 signature over canonical payload
+- public key registered during `/init`
+- signature verified on every heartbeat
+- signature covers mutation step and gene commitment
+
+### Hash-Chain Desynchronization
+
+Attack: submit a heartbeat from stale client state.
+
+Mitigations:
+
+- server compares request `prev_hash` to stored `last_hash`
+- server computes the next hash only after all validation passes
+- rejected heartbeats do not advance server state
### Mutation Tampering
-**Attack:** send an invalid or stale mutation commitment.
+Attack: forge or skip synthetic gene mutations.
-**Mitigations:**
+Mitigations:
-* server recomputes the gene commitment from server-authored mutation orders
-* heartbeat request includes `mutation_step` and `gene_commitment`
-* mismatched commitment causes silent rejection
+- server stores the pending mutation program
+- request must include the expected `mutation_step`
+- server applies the mutation independently
+- commitment includes candidate gene state, `session_id`, and step
+- mismatch causes silent rejection
-### Session Hijacking
+### Session Identifier Theft
-**Attack:** steal a valid `session_id` and reuse it.
+Attack: reuse a stolen `session_id`.
-**Mitigations:**
+Mitigations:
-* `session_id` alone is insufficient
-* attacker also needs current `prev_hash` and private key
-* keypair is generated per browser session in WASM
+- `session_id` alone is insufficient
+- attacker also needs current private key, hash state, salt, mutation step, and mutation state
+- stale attempts fail after the real session advances
-### Fingerprint Enumeration
+### Failure Oracle Probing
-**Attack:** probe the API with malformed requests to discover validation logic.
+Attack: send malformed requests and inspect responses to infer validation rules.
-**Mitigations:**
+Mitigations:
-* all invalid heartbeats return `{"status":"ok"}`
-* no explicit error messages are exposed
-* silent rejection removes oracle behavior
+- heartbeat semantic failures return `200 OK` with `{"status":"ok"}`
+- accepted heartbeats are distinguished only by next-state fields
+- detailed validation errors are not returned to the client
+
+### Storage Tampering
+
+Attack: alter persisted session state.
+
+Mitigations:
+
+- run the daemon under a dedicated user
+- restrict SQLite database permissions
+- protect Valkey behind trusted network boundaries
+- use normal host hardening and backups where persistence matters
+
+Storage is trusted. If an attacker can modify storage, they can affect session continuity.
+
+## Behavioral Checks
+
+ChronoSeal validates:
+
+- minimum event count
+- minimum movement distance
+- maximum average speed
+- pause count
+- timestamp drift
+- basic fingerprint field ranges
+
+These checks are cost signals. They are not proof of humanity and should not be the only security layer for high-risk actions.
+
+## Privacy Constraints
+
+ChronoSeal intentionally avoids:
+
+- persistent user identifiers
+- browser history collection
+- device fingerprint databases
+- cross-session identity graphs
+- long-term behavioral profiles
+
+Session data is short-lived by default. Persistent storage is operator-selected through `sqlite-in-disk` or `valkey`.
## Limitations
ChronoSeal does not protect against:
-* real users intentionally acting as bots
-* server-side application vulnerabilities
-* full browser farm operators with real input devices
-* persistent fingerprinting or identity profiling
-* pre-signed session payload reuse after a legitimate success if the attacker also has the current salt and key
+- real users intentionally automating or abusing access
+- complete browser farms with realistic input
+- compromised server hosts
+- tampered storage
+- server-side application vulnerabilities
+- credential theft outside ChronoSeal
+- policy decisions that require identity, risk scoring, or business context
-## Operational Security Notes
+## Operational Security
-* Do not use `RUST_LOG=debug` in production; it may expose internal identifiers.
-* Always serve ChronoSeal traffic over HTTPS.
-* Use `sqlite-in-memory` for ephemeral sessions when persistence is not required.
-* Use `sqlite-disk` or `valkey` when session state needs to survive restarts.
+Recommended:
+
+- serve all traffic over HTTPS
+- keep `/init` and `/hb` same-origin with protected content when possible
+- run behind a reverse proxy
+- keep debug logs disabled in production
+- protect storage and log directories
+- monitor health and metrics
+- use `sqlite-in-memory` for ephemeral sessions
+- use `sqlite-in-disk` or `valkey` only when persistence is required
## Disclosure
-See [SECURITY.md](../SECURITY.md) for the vulnerability disclosure policy.
+See [../SECURITY.md](../SECURITY.md) for the vulnerability disclosure policy.
diff --git a/docs/WASM_BUILD.md b/docs/WASM_BUILD.md
index 82c510f..542ff91 100644
--- a/docs/WASM_BUILD.md
+++ b/docs/WASM_BUILD.md
@@ -1,22 +1,22 @@
# ChronoSeal WASM Build Guide
-ChronoSeal uses a Rust-based WASM runtime to power browser-side attestation logic, signing, hash chaining, VM execution, and mutation commitment preview.
+ChronoSeal uses a Rust-generated WASM package for browser-side attestation. The package is built from `wasm/` and copied into `frontend/pkg`.
-## Why WASM
+## Responsibilities
-The WASM runtime provides a deterministic, sandboxed environment for the following tasks:
+The WASM runtime:
-* generate Ed25519 keypairs in-browser
-* sign canonical heartbeat payloads
-* execute randomized VM opcode programs
-* compute Blake3 hash chain progression
-* preview and commit synthetic gene mutations
+- generates a browser-local Ed25519 keypair
+- signs canonical heartbeat payloads
+- computes Blake3 hash-chain progression
+- executes server-issued VM opcode programs
+- initializes synthetic gene state
+- previews gene mutation commitments
+- commits or discards preview state after heartbeat response
-This enables server/client parity and prevents the private key from leaving the browser runtime.
+The WASM runtime is not treated as a secure enclave. The server independently recomputes deterministic state.
-## Build Requirements
-
-Install the Rust WASM target and `wasm-pack`:
+## Requirements
```bash
rustup target add wasm32-unknown-unknown
@@ -29,7 +29,7 @@ Verify:
wasm-pack --version
```
-## Build the WASM Module
+## Build
From the repository root:
@@ -39,45 +39,41 @@ rm -rf frontend/pkg
mv wasm/pkg frontend/pkg
```
-`--target web` produces an ES module compatible with the existing frontend JavaScript.
+`--target web` emits native ES modules compatible with the static frontend.
-`--release` enables optimizations for runtime performance and size.
+Development build:
-## Output
+```bash
+wasm-pack build wasm --target web
+rm -rf frontend/pkg
+mv wasm/pkg frontend/pkg
+```
-After a successful build, `frontend/pkg/` contains:
+Full project build:
-* `antibot_wasm.js`
-* `antibot_wasm_bg.wasm`
-* `antibot_wasm_bg.js`
-* `antibot_wasm.d.ts`
-* `antibot_wasm_bg.d.ts`
-* `package.json`
+```bash
+bash scripts/build.sh
+```
-The frontend expects the WASM package under `frontend/pkg/`.
+## Output Files
-## Runtime Exports
+The package name comes from the crate name `chronoseal-wasm`, so generated files use the `chronoseal_wasm` prefix.
-The WASM module exports the following functions:
+Expected `frontend/pkg/` contents include:
-* `generate_keypair()` — generate a new Ed25519 keypair and return public key hex
-* `get_public_key()` — return the current public key hex
-* `sign_message(msg)` — sign a UTF-8 payload and return the hex signature
-* `compute_next_hash(prev, ts, entropy, stack, salt)` — compute the next Blake3 chain hash
-* `run_program(b64)` — execute a base64 VM program and return stack state
-* `init_gene_state(gene_size)` — initialise the synthetic gene buffer
-* `preview_gene_commitment(order_b64)` — preview the next gene commitment from a mutation order
-* `commit_gene_preview()` — commit the previewed mutation after successful heartbeat
-* `discard_gene_preview()` — discard the previewed mutation after rejection or error
-* `current_gene_commitment()` — return the current committed gene commitment
+- `chronoseal_wasm.js`
+- `chronoseal_wasm_bg.wasm`
+- `chronoseal_wasm.d.ts`
+- `package.json`
-## Browser Integration
+Generated files in `wasm/pkg/` and `frontend/pkg/` are build artifacts and should be regenerated during release.
-The frontend imports the generated module like this:
+## Browser Import
```js
import init, {
generate_keypair,
+ get_public_key,
sign_message,
compute_next_hash,
run_program,
@@ -86,48 +82,82 @@ import init, {
commit_gene_preview,
discard_gene_preview,
current_gene_commitment
-} from './pkg/antibot_wasm.js';
+} from './pkg/chronoseal_wasm.js';
```
-`await init()` must be called before invoking any other exported function.
+Call `await init()` before using any exported function.
-## Deployment Note
+## Exported Functions
-The `.wasm` binary must be served with the correct MIME type:
+| Function | Signature | Failure value |
+|---|---|---|
+| `generate_keypair()` | `() -> string` | `""` only on unexpected failure |
+| `get_public_key()` | `() -> string` | `""` if no keypair exists |
+| `sign_message(msg)` | `(string) -> string` | `""` if no keypair exists or signing fails |
+| `compute_next_hash(prev, ts, entropy, stack, salt)` | `(string, u64, string, string, string) -> string` | panic/error path should be avoided by valid inputs |
+| `run_program(b64)` | `(string) -> JsValue` | returns empty/default stack state on invalid execution path |
+| `init_gene_state(gene_size)` | `(u32) -> bool` | `false` |
+| `preview_gene_commitment(order_b64, session_id, mutation_step, rounds)` | `(string, string, u64, u8) -> string` | `""` |
+| `commit_gene_preview()` | `() -> bool` | `false` |
+| `discard_gene_preview()` | `() -> void` | none |
+| `current_gene_commitment(session_id, mutation_step)` | `(string, u64) -> string` | `""` if no committed state exists |
+`rounds = 0` in `preview_gene_commitment` selects the shared default mutation round count.
+
+## Mutation State Lifecycle
+
+The browser must keep two gene states:
+
+- committed state: the last accepted state
+- preview state: candidate state for the heartbeat currently being sent
+
+Expected sequence:
+
+1. Call `init_gene_state(gene_size)` after `/init`.
+2. Call `preview_gene_commitment(order_b64, session_id, mutation_step, rounds)` before signing `/hb`.
+3. Include the returned commitment and mutation step in the signed heartbeat.
+4. If the response contains next-state fields, call `commit_gene_preview()`.
+5. If the heartbeat is rejected or errors, call `discard_gene_preview()`.
+
+Never commit preview state before the server accepts the heartbeat.
+
+## Hash-Chain Ordering
+
+After an accepted heartbeat, compute the next local hash with the salt that was active when the heartbeat was sent. Then replace the local salt with `next_salt`.
+
+Correct order:
+
+```js
+const sentSalt = currentSalt;
+currentSalt = resp.next_salt;
+prevHash = compute_next_hash(prevHash, timestamp, entropyJson, stackStateJson, sentSalt);
```
+
+This mirrors the server, which computes and stores the new hash before rotating to the next salt.
+
+## Serving WASM
+
+The `.wasm` file must be served with:
+
+```text
Content-Type: application/wasm
```
-The built-in Axum static file handler already sets the appropriate MIME type for `.wasm` files.
+ChronoSeal's built-in static file service handles this for normal deployments.
-## Build Script
+## Validation
-Use the convenience script:
+Recommended checks after WASM changes:
```bash
-bash scripts/build.sh
+cargo test --workspace
+cargo clippy --workspace --all-targets -- -D warnings
+wasm-pack build wasm --target web
```
-This builds the WASM package, moves it into `frontend/pkg/`, and builds the server binary.
-
-## Recommended Development Flow
-
-* For WASM-only changes:
+Then refresh `frontend/pkg`:
```bash
-wasm-pack build wasm --target web
rm -rf frontend/pkg
mv wasm/pkg frontend/pkg
```
-
-* For server-only changes:
-
-```bash
-cargo build -p server
-```
-
-## Notes
-
-Generated files in `wasm/pkg/` and `frontend/pkg/` are not tracked in source control.
-They are build artifacts and should be regenerated as part of the release workflow.
diff --git a/docs/chronoseal.example.toml b/docs/chronoseal.example.toml
index ac0982c..52df9f0 100644
--- a/docs/chronoseal.example.toml
+++ b/docs/chronoseal.example.toml
@@ -1,9 +1,27 @@
bind = "0.0.0.0:3000"
-# sqlite-in-memory (default), sqlite-in-disk, valkey (v0.6.0 compatibility mode)
+
+# Storage backend: sqlite-in-memory (default), sqlite-in-disk, or valkey.
db_type = "sqlite-in-memory"
+
pid_file = "/run/chronoseal.pid"
db_path = "/var/lib/chronoseal/chronoseal.sqlite"
frontend_dir = "/usr/share/chronoseal/frontend"
log_file = "/var/log/chronoseal/chronoseal.jsonl"
-# synthetic gene size (1..=65536), default 512
+
+heartbeat_min_interval_ms = 12000
+heartbeat_max_interval_ms = 25000
+expiration_minutes = 30
+rate_limit_count = 5
+rate_limit_window_secs = 10
+max_timestamp_drift_ms = 30000
+
+min_mouse_total_dist = 10.0
+max_mouse_avg_speed = 2.0
+min_pause_count = 1
+require_mouse_activity = true
+
+# Synthetic gene size. Current valid range: 1..=4096. Default: 512.
gene_size = 512
+
+# Current valid range: 1..=10. Default: 4.
+mutation_rounds = 4
diff --git a/server/src/session.rs b/server/src/session.rs
index 2159829..6927809 100644
--- a/server/src/session.rs
+++ b/server/src/session.rs
@@ -132,11 +132,8 @@ pub fn verify_heartbeat(
config.mutation_rounds,
)
.map_err(|e| crate::errors::VerificationError::MutationProgram(e.to_string()))?;
- let expected_gene_commitment = gene::commitment_hex_with_context(
- &candidate_state,
- &req.session_id,
- req.mutation_step,
- );
+ let expected_gene_commitment =
+ gene::commitment_hex_with_context(&candidate_state, &req.session_id, req.mutation_step);
if req.gene_commitment != expected_gene_commitment {
return Err(crate::errors::VerificationError::MutationCommitmentMismatch);
}
diff --git a/server/src/storage.rs b/server/src/storage.rs
index 9ea0441..96ba516 100644
--- a/server/src/storage.rs
+++ b/server/src/storage.rs
@@ -52,14 +52,17 @@ impl DbPool {
Ok(DbPool::Sqlite(pool))
}
crate::config::DbType::Valkey => {
- let addr = std::env::var("CHRONOSEAL_VALKEY_ADDR").unwrap_or_else(|_| "127.0.0.1:6666".to_string());
+ let addr = std::env::var("CHRONOSEAL_VALKEY_ADDR")
+ .unwrap_or_else(|_| "127.0.0.1:6666".to_string());
match ValkeyClient::connect(addr) {
Ok(client) => Ok(DbPool::Valkey(ValkeyStore {
client: Arc::new(Mutex::new(client)),
index_key: "sessions:ids".to_string(),
})),
Err(err) => {
- tracing::warn!("valkey connection failed, falling back to sqlite-in-memory: {err}");
+ tracing::warn!(
+ "valkey connection failed, falling back to sqlite-in-memory: {err}"
+ );
let pool = init_sqlite_pool(Path::new(":memory:"))?;
Ok(DbPool::Sqlite(pool))
}
@@ -99,7 +102,10 @@ impl DbPool {
}
}
- pub fn load_session(&self, session_id: &str) -> Result