Files
nx9-chronoseal-rs/docs/DEPLOYMENT.md
T
thakares 4b27a342d1 docs: comprehensive ARCHITECTURE, DEPLOYMENT, API, and THREAT_MODEL
ARCHITECTURE.md
- Full component map with ASCII diagram
- Complete session lifecycle (init + heartbeat + failure path)
- Cryptographic protocol spec (hash chain formula, canonical JSON)
- Stack machine instruction set table with stack effects
- Behavioral validation thresholds
- SQLite schema, threat model summary, module reference

DEPLOYMENT.md
- Build instructions (WASM + server + convenience script)
- native binary, systemd (with hardened sandbox notes), Docker
- nginx, Nginx Proxy Manager, and HAProxy reverse proxy configs
- Integration options (sidecar vs proxy-only)
- Full configuration table with all constants
- Observability (RUST_LOG levels), health check, security checklist

API.md
- Full /init and /hb request/response schemas with field tables
- Canonical signing payload specification
- Complete validation rules table (all 13 rejection conditions)
- Hash chain byte-level specification
- WASM exported function reference

THREAT_MODEL.md
- Four attacker profiles (script kiddie → sophisticated adversary)
- Eight attack vectors with mitigations (replay, forgery, hijack, DoS…)
- Explicit out-of-scope limitations
- Operational security notes (CORS, TLS, log level, SQLite)
2026-05-09 18:04:13 +05:30

8.4 KiB
Raw Blame History

ChronoSeal — Deployment Guide

Prerequisites

Tool Minimum version Purpose
Rust 1.87 stable Server + WASM compilation
wasm-pack 0.13 WASM build and packaging
Docker + Compose 24 / 2.x Container deployment
nginx / NPM / HAProxy any TLS termination, reverse proxy

Install Rust: https://rustup.rs
Install wasm-pack: cargo install wasm-pack


Build

1. Build the WASM module

wasm-pack build wasm --target web --release
mv wasm/pkg frontend/pkg

This produces frontend/pkg/antibot_wasm.js and frontend/pkg/antibot_wasm_bg.wasm, which are loaded by frontend/main.js at runtime.

2. Build the server

cargo build -p server --release

Binary output: target/release/server

3. Build both (convenience script)

bash scripts/build.sh

Running

Development

bash scripts/dev.sh

Runs the server with cargo run --release. The server serves the frontend/ directory statically at / via tower-http ServeDir.

Open http://localhost:3000 in a browser. Open DevTools console — heartbeats should appear every 12–25 seconds. No visible UI is rendered; the protection is entirely silent.

Production (native binary)

cargo build -p server --release
sudo cp target/release/server /usr/local/bin/chronoseal

Set environment variables before running:

export RUST_LOG=info      # or warn for quieter output
chronoseal

The server binds to 0.0.0.0:3000 by default. Place behind a reverse proxy for TLS — do not expose port 3000 directly.


systemd

Service file

The provided chronoseal.service includes hardened systemd sandboxing:

NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
MemoryDenyWriteExecute=true
RestrictRealtime=true
RestrictSUIDSGID=true
LockPersonality=true
SystemCallArchitectures=native

Install

# Create a dedicated system user
sudo useradd --system --no-create-home --shell /usr/sbin/nologin chronoseal

# Install binary and frontend
sudo cp target/release/server /usr/local/bin/chronoseal
sudo mkdir -p /opt/chronoseal/frontend
sudo cp -r frontend/ /opt/chronoseal/frontend/
sudo chown -R chronoseal:chronoseal /opt/chronoseal

# Install and enable service
sudo cp chronoseal.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now chronoseal

Verify

sudo systemctl status chronoseal
journalctl -u chronoseal -f

Docker

Build and run

docker compose up -d --build

docker-compose.yml overview

services:
  chronoseal:
    build: .
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      RUST_LOG: info
    tmpfs:
      - /tmp

The tmpfs mount ensures the in-memory SQLite database is never written to disk, even if Docker's storage driver were to flush the container filesystem.

Dockerfile stages

The Dockerfile uses a two-stage build:

  1. rust:1.87-bookworm — compiles the server binary
  2. debian:bookworm-slim — minimal runtime image with only ca-certificates

The WASM module and frontend must be built separately (wasm-pack requires a browser toolchain not present in the server image) and mounted or copied into the container at /opt/chronoseal/frontend/.

# Build WASM first
wasm-pack build wasm --target web --release
mv wasm/pkg frontend/pkg

# Then build and run the container
docker compose up -d --build

Or mount the pre-built frontend as a volume:

volumes:
  - ./frontend:/opt/chronoseal/frontend:ro

Reverse Proxy

ChronoSeal must be served over HTTPS. The heartbeat payload contains a timestamp; if traffic is observable in plaintext, timing attacks become easier. TLS 1.3 is strongly recommended.

nginx

server {
    listen 443 ssl http2;
    server_name your.domain.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;

    # Tight timeouts — heartbeat interval is 12–25s
    proxy_read_timeout  35s;
    proxy_send_timeout  10s;

    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;
    }
}

server {
    listen 80;
    server_name your.domain.com;
    return 301 https://$host$request_uri;
}

Nginx Proxy Manager

  1. Add a new Proxy Host pointing to http://chronoseal:3000
  2. Enable SSL, Request Let's Encrypt certificate
  3. Enable HTTP/2, Force SSL
  4. Under Advanced, add:
    proxy_read_timeout 35s;
    proxy_send_timeout 10s;
    

HAProxy

frontend https_front
    bind *:443 ssl crt /etc/haproxy/certs/your.domain.pem alpn h2,http/1.1
    default_backend chronoseal_back

backend chronoseal_back
    server chronoseal 127.0.0.1:3000 check
    timeout connect 5s
    timeout server  35s

Integration into an Existing Site

ChronoSeal is designed to run as a sidecar — its /init and /hb endpoints can be proxied from any existing web server. The frontend assets (pkg/) need to be served from the same origin as the protected page (or CORS must be configured).

Option A — Serve everything from ChronoSeal

ChronoSeal serves frontend/ statically. Put your protected HTML inside frontend/ and let ChronoSeal serve it directly.

Option B — Proxy only the API endpoints

Keep your existing server. Proxy /init and /hb to ChronoSeal, and serve the WASM and JS assets from your CDN or existing static file server.

# On your existing server:
location ~ ^/(init|hb)$ {
    proxy_pass http://127.0.0.1:3000;
}

Add to your protected pages:

<script type="module" src="/pkg/antibot_wasm.js"></script>
<script type="module" src="/main.js"></script>

Configuration

All parameters are in shared/src/constants.rs. Recompile after changes.

Constant Default Notes
SESSION_ID_LEN 32 bytes 256-bit entropy — do not reduce
SALT_LEN 16 bytes Per-heartbeat salt
HEARTBEAT_MIN_INTERVAL_MS 12 000 ms Increase to reduce server load
HEARTBEAT_MAX_INTERVAL_MS 25 000 ms Jitter upper bound
EXPIRATION_MINUTES 30 min Session TTL after last heartbeat
RATE_LIMIT_COUNT 5 Max heartbeats per window per session
RATE_LIMIT_WINDOW_SECS 10 s Rate limit window
MAX_TIMESTAMP_DRIFT_MS 30 000 ms Anti-replay window; account for NTP skew
MIN_MOUSE_TOTAL_DIST 10.0 px Lower for low-activity pages
MAX_MOUSE_AVG_SPEED 2.0 px/ms Raise if legitimate users are rejected
MIN_PAUSE_COUNT 1 Minimum natural pause events

Observability

ChronoSeal uses tracing with tracing-subscriber. Log levels:

Level Events
INFO Server start, request method + path + status
WARN Heartbeat validation failures (with session ID and reason)
DEBUG Rate limit hits
RUST_LOG=info   chronoseal   # production
RUST_LOG=debug  chronoseal   # development
RUST_LOG=warn   chronoseal   # minimal output

Log format is plain text to stdout. Pipe to journald, fluentd, or any log aggregator via stdout capture.


Health Check

The server has no dedicated /health endpoint. Use a TCP check on port 3000, or a lightweight HTTP check on GET / (which serves index.html).

# Docker health check (add to docker-compose.yml if needed)
healthcheck:
  test: ["CMD", "curl", "-sf", "http://localhost:3000/"]
  interval: 30s
  timeout: 5s
  retries: 3

Security Checklist

  • TLS 1.3 enabled, TLS 1.0/1.1 disabled
  • HTTP/2 enabled
  • Port 3000 not exposed to the public internet (only via reverse proxy)
  • RUST_LOG=warn or info in production (not debug — session IDs appear in logs)
  • systemd service running as chronoseal user with hardened sandbox
  • MemoryDenyWriteExecute=true in service file (prevents JIT in process)
  • CORS CorsLayer::permissive() replaced with origin-restricted policy for production
  • Frontend assets served over the same HTTPS origin as protected pages