2026-05-12 19:34:31 +05:30
2026-05-07 21:57:47 +05:30
2026-05-07 21:57:47 +05:30
2026-05-07 21:57:47 +05:30
2026-05-07 21:57:47 +05:30
2026-05-07 21:57:47 +05:30
2026-05-09 18:35:56 +05:30
2026-05-12 20:05:59 +05:30
2026-05-07 21:57:47 +05:30

ChronoSeal

ChronoSeal Logo

Cryptographic anti-automation and browser attestation framework built with Rust, WASM, and behavioral continuity verification.

ChronoSeal makes it computationally expensive and operationally complex for AI scrapers, headless browsers, and automation tools to impersonate real users — while remaining completely invisible and frictionless to legitimate human visitors.


How It Works

ChronoSeal establishes a continuous, cryptographically verifiable proof-of-presence for every browser session. It is inspired by the heartbeat model used in IoT firmware (ESP32-class devices): the client must keep emitting signed, chained attestations, or the session is silently invalidated.

Browser                                    Server
  │                                           │
  │  WASM loads, generates Ed25519 keypair    │
  │  Private key never leaves WASM memory     │
  │                                           │
  ├──── POST /init  { public_key } ──────────►│  Store session, salt, initial hash
  │◄─── { session_id, salt, opcodes, H0 } ────┤
  │                                           │
  │  Every 12–25s (jittered):                 │
  │  ┌─ Collect mouse entropy                 │
  │  ├─ Execute VM opcodes → stack state      │
  │  ├─ Compute H(n) = Blake3(H(n-1) ║ …)     │
  │  └─ Sign payload with Ed25519             │
  │                                           │
  ├──── POST /hb  { session_id, sig, … } ────►│  Verify sig → chain → behavior → fingerprint
  │◄─── { status, next_salt } ────────────────┤  Rotate salt, advance chain
  │                                           │
  │ On failure: server returns {"status":"ok"}│  Silent rejection — indistinguishable

Security Model

What ChronoSeal protects against

Threat Mechanism
Playwright / Puppeteer Stealth Mouse entropy validation rejects synthetic or absent movement
Replay attacks Hash chain — each heartbeat references the previous hash; old payloads are invalid
Signature forgery Ed25519 private key generated inside WASM, never serialised or exposed to JS
Clock manipulation Server enforces ±30s timestamp window
Credential sharing Session is bound to a keypair generated fresh on every page load
Flooding with fake sessions Per-session rate limiting (5 req / 10s); stale entries evicted every 60s
Passive analysis of traffic Server always returns {"status":"ok"} — rejections are silent

What ChronoSeal does not claim

ChronoSeal is a cost-raising mechanism, not an impenetrable barrier. A sufficiently motivated adversary with a real browser, real input devices, and the patience to reverse the WASM can bypass it. The goal is to make scraping expensive and operationally complex enough to be impractical at scale.


Architecture

chronoseal-rs/
├── shared/          Shared types, Blake3 hash-chain logic, constants
├── server/          Axum HTTP server
│   ├── routes/      /init and /hb handlers
│   ├── session.rs   Session lifecycle: create, verify, advance chain
│   ├── crypto.rs    Ed25519 signature verification (BTreeMap canonical JSON)
│   ├── trust.rs     Behavioral signal validation (mouse speed, pauses)
│   ├── fingerprint  Browser fingerprint sanity checks
│   ├── vm.rs        Random opcode program generator
│   ├── ratelimit.rs Token-bucket rate limiter with periodic eviction
│   └── cleanup.rs   Background task: expire sessions + evict rate limiter
├── wasm/            Rust → WASM client module
│   ├── crypto.rs    Ed25519 keypair, signing, hash computation
│   └── vm.rs        Stack machine executor (PUSH/ADD/SUB/MUL/XOR/AND/OR/ROT/NOT/HASH)
└── frontend/        Vanilla JS glue
    ├── heartbeat.js Session init, heartbeat scheduling, chain advancement
    └── entropy.js   Mouse event collection

Stack Machine

The server generates a random program (8–16 opcodes) on session init. The client executes it on every heartbeat and includes the resulting stack state in the signed payload. This makes each heartbeat structurally unique without requiring any server round-trip.

Opcode Mnemonic Effect
0x00 PUSH u32 Push 4-byte little-endian literal
0x01 ADD Pop 2, push a + b (wrapping)
0x02 SUB Pop 2, push a - b (wrapping)
0x03 MUL Pop 2, push a * b (wrapping)
0x04 XOR Pop 2, push a ^ b
0x05 AND Pop 2, push a & b
0x06 OR Pop 2, push a | b
0x07 ROT Pop 2, push a.rotate_left(b % 32)
0x08 NOT Pop 1, push !a (unary)
0x09 HASH Blake3 of entire stack → single u32

Hash Chain

H(0) = Blake3( session_id ║ pub_key ║ salt₀ )

H(n) = Blake3( saltₙ₋₁ ║ H(n-1) ║ timestamp ║ Blake3(entropy_json) ║ Blake3(stack_json) )

Each heartbeat must present H(n-1) matching what the server stored. Forging a valid H(n) requires knowing the private key (for the signature), the salt (server-side only), and all prior state.

Signature Canonical Form

The client signs a JSON object with keys sorted alphabetically (matching JSON.stringify(obj, Object.keys(obj).sort())):

{
  "entropyData":  { "events": [ { "x": …, "y": …, "t": … } ] },
  "fingerprint":  { "aspectRatio": "…", "devicePixelRatio": "…", "hardwareConcurrency": … },
  "prevHash":     "hex…",
  "sessionId":    "hex…",
  "stackState":   { "stack": […], "ip": … },
  "timestamp":    1234567890123
}

The server reconstructs this using BTreeMap (alphabetical key order) before calling VerifyingKey::verify_strict.


SQLite Schema

CREATE TABLE IF NOT EXISTS sessions (
    session_id    TEXT     PRIMARY KEY,
    public_key    BLOB     NOT NULL,
    salt          BLOB     NOT NULL,
    last_hash     BLOB     NOT NULL,
    chain_length  INTEGER  NOT NULL DEFAULT 1,
    created_at    INTEGER  NOT NULL,
    last_seen     INTEGER  NOT NULL,
    expires_at    INTEGER  NOT NULL
);

Sessions are stored in an in-memory SQLite database. All session state is lost on server restart by design — clients re-initialise transparently.


Build

Prerequisites

WASM

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

Server

cargo build -p server --release

Dev (all-in-one)

bash scripts/dev.sh

The server serves the frontend/ directory statically at / and the API at /init and /hb.


Deployment

Native + systemd

cargo build -p server --release
sudo cp target/release/server /usr/local/bin/chronoseal
sudo cp chronoseal.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now chronoseal

Docker

docker compose up -d --build

Reverse Proxy

Place ChronoSeal behind nginx, Nginx Proxy Manager, or HAProxy. Enable:

  • TLS 1.3
  • HTTP/2
  • Aggressive upstream timeouts (the heartbeat interval is 12–25s)

Integration

Drop two lines into any protected page:

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

main.js calls initHeartbeat() which handles WASM loading, session init, and schedules all subsequent heartbeats automatically. There is no visible UI, no CAPTCHA, no user interaction required.


Configuration

All tunable constants are in shared/src/constants.rs:

Constant Default Description
SESSION_ID_LEN 32 bytes Session ID entropy
SALT_LEN 16 bytes Per-heartbeat salt size
HEARTBEAT_MIN_INTERVAL_MS 12 000 ms Minimum heartbeat interval
HEARTBEAT_MAX_INTERVAL_MS 25 000 ms Maximum heartbeat interval (uniform jitter)
EXPIRATION_MINUTES 30 min Session lifetime after last heartbeat
RATE_LIMIT_COUNT 5 Max heartbeats per window
RATE_LIMIT_WINDOW_SECS 10 s Rate limit window
MAX_TIMESTAMP_DRIFT_MS 30 000 ms Anti-replay timestamp window
MIN_MOUSE_TOTAL_DIST 10.0 px Minimum cumulative mouse travel
MAX_MOUSE_AVG_SPEED 2.0 px/ms Maximum average mouse speed
MIN_PAUSE_COUNT 1 Minimum mouse pause events

License

MIT OR Apache-2.0

S
Description
Privacy-first browser attestation framework for anti-bot and anti-AI scraping. Stateless, cryptographic, and Unix-native. Uses Ed25519 heartbeat chains, WASM attestation, and behavioral continuity validation without tracking users, storing IPs, or collecting telemetry.
https://chronoseal.nx9.in/
Readme MIT
4.4 MiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 46.4%
HTML 39.7%
JavaScript 6.8%
CSS 6.5%
Shell 0.4%
Other 0.2%