OUTRIDER docs
v1.0.0
Live demo Get help
● Ship · One server

Deployment
one server, one disk.

OUTRIDER is a long-running process that writes to its data folder. Host it on one small server with a persistent disk, keep the dashboard on localhost, and put a reverse proxy with HTTPS in front if you want to reach it from a browser.

01What the desk needs from a host

  • One always-on process. A VPS or a home server. Serverless platforms do not fit: the desk loops forever, keeps WebSockets open and stores its console in SQLite on disk.
  • A persistent disk for data/: state, journal, recordings, wallet lists and the console database.
  • Node.js 22+ and outbound internet. Run node src/cli.mjs doctor on the server: some APIs answer differently from data-centre addresses.
  • One instance per data folder. Two desks on the same data/ would write over each other.

02Install on the server

  1. Copy the folderUpload the unzipped package, for example to /opt/outrider, and create config.json (Configuration).
  2. Install exact versions
    cd /opt/outrider
    npm ci
    npm test
    node src/cli.mjs doctor
  3. Set the console secret keyRequired on a server. Generate it once and store it in your process manager's environment, never in git:
    openssl rand -hex 32
    If you skip it, the desk writes one to data/.admin-secret-key. Either way, lose the key and the secrets stored in the console cannot be read.

03Keys in .env

Copy .env.example to .env next to package.json and fill in what you use. The desk loads it on start; a variable already set in the environment wins. .env is in .gitignore and .dockerignore: never commit it.

cp .env.example .env
openssl rand -hex 32        # paste as ADMIN_SECRET_KEY

04Docker

The package includes a Dockerfile and docker-compose.yml. The port is published on the host's loopback only. The Docker files were written to the standard Node pattern but not built on every platform; tell us if your host needs a change.

  1. Keys
    cp .env.example .env                 # set ADMIN_SECRET_KEY at least
  2. Your settings (optional) Create config.json from config.example.json and uncomment the ./config.json line under volumes in docker-compose.yml. Without it the container uses the defaults: .dockerignore keeps your local config.json out of the image.
  3. Start
    docker compose up -d --build
    docker compose logs -f outrider      # the console setup code (printed on every start until the owner exists)
    # open http://127.0.0.1:8790/admin (through an SSH tunnel on a server) and create the owner
    docker compose down                  # stops cleanly: the desk saves its state first

Data lives in the named volume outrider-data, created with the right owner, so it works on any host. Back it up with docker compose cp outrider:/app/data ./backup. Inside the container the dashboard listens on 0.0.0.0 (DASH_HOST); that is allowed only because the operator console guards it. Memory: plan for 2 GB; the research refresh briefly needs more than the desk itself.

05PM2

npm i -g pm2
pm2 start ecosystem.config.cjs
pm2 logs outrider                    # setup code on the first start
pm2 save && pm2 startup             # start again after a reboot

The included ecosystem.config.cjs gives the desk 30 seconds to save on stop and restarts it if it exits.

06Run it as a service (systemd)

An example systemd unit (adjust the user, paths and variables; not part of the package):

[Unit]
Description=OUTRIDER desk
After=network-online.target

[Service]
User=outrider
WorkingDirectory=/opt/outrider
Environment=ADMIN_SECRET_KEY=<64 hex characters>
Environment=TELEGRAM_BOT_TOKEN=<token>
Environment=TELEGRAM_CHAT_ID=<chat id>
ExecStart=/usr/bin/npm start
Restart=on-failure
KillSignal=SIGINT
TimeoutStopSec=30

[Install]
WantedBy=multi-user.target

On stop the desk finishes its cycle, saves state and closes sockets. Read the first log after starting for the one-time setup code (journalctl -u outrider).

07Create the owner before you open it

Run directly or under PM2, the dashboard and console listen on 127.0.0.1 only. In Docker they listen on 0.0.0.0 inside the container, and docker-compose.yml publishes the port on the host's 127.0.0.1 only. Either way, reach them through an SSH tunnel and create the owner account first:

ssh -L 8790:127.0.0.1:8790 <user>@<server>
# then open http://127.0.0.1:8790/admin on your own computer
Why the tunnel

Until the owner exists, the desk answers only requests whose Host is a loopback address (in Docker: every dashboard request is refused until the owner exists), so a browser coming through a public domain gets 403 create the owner at /admin first on the dashboard. /admin itself stays reachable, and creating the owner needs the one-time setup code printed in the server log. Creating the owner through the tunnel is the simplest path. Consider requiring two-factor for owners and managers right after.

08Reverse proxy with HTTPS

If you want the dashboard on a domain, put a reverse proxy on the same server. Three things matter:

  • Allowed hosts. The desk rejects requests whose Host is not a loopback address with 403 host not allowed (protection against DNS rebinding). List your public domain(s) in DASH_ALLOWED_HOSTS, comma-separated, for example DASH_ALLOWED_HOSTS=desk.example.com. The proxy passes the original Host header.
  • HTTPS. Send X-Forwarded-Proto: https so the session cookie is marked Secure.
  • Streaming. The page uses Server-Sent Events on /api/stream; turn off response buffering.

Add the variable to the service (above) and restart:

Environment=DASH_ALLOWED_HOSTS=desk.example.com

nginx example:

server {
  listen 443 ssl;
  server_name desk.example.com;
  # ssl_certificate … (for example from certbot)
  location / {
    proxy_pass http://127.0.0.1:8790;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_buffering off;
    proxy_read_timeout 1h;
  }
}

Caddy example:

desk.example.com {
  reverse_proxy 127.0.0.1:8790 {
    header_up X-Forwarded-Proto https
    flush_interval -1
  }
}

These proxy files are examples and were not tested for this release (unverified). Caddy passes the original Host header by default. The host rule itself was tested with DASH_ALLOWED_HOSTS=desk.example.com before the owner existed: Host 127.0.0.1 got the dashboard, Host desk.example.com got 403 on the dashboard and 200 on /admin/, and any other Host got 403 on both.

09Backups

WhatWhy
data/journal.jsonl, data/state.jsonYour paper track record and the desk's state. The journal is hash-chained; a restored copy still verifies.
data/admin.sqlite*Console users, settings, history and audit log (SQLite with -wal and -shm files).
ADMIN_SECRET_KEY or data/.admin-secret-keyWithout it, secrets stored in the console cannot be decrypted. Keep it separate from the data backup.
config.jsonYour settings; may contain your RPC key.
data/pump-events/, gecko-trades/, wallet filesRecordings and wallet books. Large; back up if you want to replay later.

The simplest safe copy: stop the service, copy data/ and config.json, start it again.

10A public read-only demo

node scripts/export-demo.mjs demo-site                  # sample paper book only
node scripts/export-demo.mjs demo-site --real data     # plus your real liquidation map, wallets, leaderboard and calls
node scripts/export-demo.mjs demo-site --frame-origin https://your-landing.example   # allow your landing page to embed it

It writes a static site (index.html, demo-shim.js, api-static/*.json, SNAPSHOT.txt and a vercel.json with security headers) that shows the real dashboard page fed from snapshots. The paper book is sample data with the banner; the kill switch answers that it only works on your own desk. Host the folder on any static host.

By default the generated vercel.json lets the demo be framed only by its own site (frame-ancestors 'self'). To show it in an iframe on your landing page, pass that page's origin with --frame-origin (an https:// origin).

Before you publish it

With --real, the copied files contain real wallet addresses and positions from public chains; publish only what you are comfortable showing.

11Updating

  1. Read the changelogCheck Changelog for migration notes.
  2. Stop and back upStop the service and copy data/ and config.json.
  3. Replace the codeCopy the new version over the old folder, keeping data/ and config.json.
  4. Install and check
    npm ci
    npm test
    node src/cli.mjs doctor
    node src/cli.mjs verify
  5. StartStart the service and open the dashboard.