- 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.
7.4 KiB
ChronoSeal Deployment Guide
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.
Deployment Model
Typical production topology:
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.
Requirements
| 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:
rustup target add wasm32-unknown-unknown
cargo install wasm-pack
Build
Use the repository build script:
bash scripts/build.sh
The script:
- Builds
wasm/withwasm-pack build --target web --release. - Replaces
frontend/pkgwith the generated package. - Builds the release daemon binary.
Manual equivalent:
wasm-pack build wasm --target web --release
rm -rf frontend/pkg
mv wasm/pkg frontend/pkg
cargo build -p chronoseal-server --bin chronoseal --release
Release binary:
target/release/chronoseal
Native Install
The installer builds, installs, enables, and starts the service:
sudo bash scripts/install.sh
Installer actions:
- create the
chronosealsystem user if missing - build WASM and server artifacts
- install
target/release/chronosealto/usr/local/bin/chronoseal - copy
frontend/to/opt/chronoseal/frontend - install
chronoseal.serviceto/etc/systemd/system/chronoseal.service - reload systemd
- enable and start the service
Verify:
sudo systemctl status chronoseal
chronoseal status --format json
chronoseal health
sudo journalctl -u chronoseal -f
Running Without Install
For local development:
bash scripts/build.sh
cargo run -p chronoseal-server --bin chronoseal -- run \
--bind 127.0.0.1:3000 \
--frontend-dir frontend
Probe the daemon:
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:
- CLI flags
CHRONOSEAL_*environment variables- TOML config file
- built-in defaults
Default config discovery:
CHRONOSEAL_CONFIG, if it points to an existing file/etc/chronoseal/config.toml$XDG_CONFIG_HOME/chronoseal/config.toml~/.config/chronoseal/config.toml
Validate effective configuration:
chronoseal config check --format yaml
Example:
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:
sudo mkdir -p /var/lib/chronoseal
sudo chown -R chronoseal:chronoseal /var/lib/chronoseal
For Valkey:
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:
sudo systemctl daemon-reload
sudo systemctl enable --now chronoseal
sudo systemctl restart chronoseal
sudo systemctl status chronoseal
sudo journalctl -u chronoseal -f
Recommended hardening properties include:
NoNewPrivileges=truePrivateTmp=trueProtectSystem=strictProtectHome=trueProtectKernelTunables=trueProtectKernelModules=trueProtectControlGroups=trueMemoryDenyWriteExecute=trueRestrictRealtime=trueRestrictSUIDSGID=trueSystemCallArchitectures=native
Any hardening must still allow access to:
- 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. Terminate TLS at a reverse proxy or load balancer and proxy to the local daemon.
Minimal nginx example:
server {
listen 443 ssl http2;
server_name example.com;
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;
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 example.com;
return 301 https://$host$request_uri;
}
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 scripts/build.sh
docker compose up -d --build
The Compose file exposes port 3000.
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.
Observability
CLI:
chronoseal status --format json
chronoseal health
chronoseal stats --format json
chronoseal metrics
HTTP:
curl http://127.0.0.1:3000/health
curl http://127.0.0.1:3000/stats
curl http://127.0.0.1:3000/metrics
Prometheus metrics:
chronoseal_sessionschronoseal_expired_sessionschronoseal_max_chain_length
Logging
Use info-level logs for production:
CHRONOSEAL_LOG=info chronoseal run
or with systemd:
sudo systemctl edit chronoseal
Avoid debug logging in production because internal identifiers may be written to logs.
Production Checklist
- Build
frontend/pkgbefore 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, orvalkey. - Protect SQLite and log directories with correct ownership.
- Monitor
/health,/stats, and/metrics. - Verify
chronoseal config checkafter environment or config changes.