Skip to content

Server Operator Guide

What The Server Operator Owns

tidal-server is the server operator CLI. It owns:

  • Alembic migrations
  • scanner execution
  • optional kick daemon execution
  • FastAPI serving
  • API key management
  • the canonical SQLite database

If this host does not have Tidal installed yet, start with Install.

The server should run close to both the database and the Ethereum RPC it depends on.

Minimal production deployment:

1 host / VM
  - SQLite database file
  - tidal-server scan daemon
  - tidal-server api serve
  - optional tidal-server kick daemon

Separate the CLI client wallet from this machine whenever possible.

First-Time Bootstrap

Install the tool, then scaffold the runtime home:

uv tool install /path/to/tidal
uv tool update-shell
tidal init

Then edit:

  • ~/.tidal/config.yaml
  • ~/.tidal/.env
  • ~/.tidal/auction_pricing_policy.yaml

Then run:

tidal-server db migrate
tidal-server auth create --label cli-client-name
tidal-server scan run
tidal-server api serve

If you plan to reconcile receipts in the API process, set RPC_URL so the background reconciler can start.

Example Linux Deployment

One simple production shape is:

  • host: electro
  • user: wavey
  • working directory: /home/wavey/tidal
  • API bind: 127.0.0.1:8020
  • reverse proxy: nginx terminating TLS at api.tidal.wavey.info

Example ~/.tidal/.env:

RPC_URL=http://127.0.0.1:8545
TOKEN_PRICE_AGG_KEY=...

Example ~/.tidal/config.yaml overrides:

db_path: state/tidal.db
tidal_api_host: 127.0.0.1
tidal_api_port: 8020
scan_interval_seconds: 300
scan_auto_settle_enabled: false

Long-Running Commands

Scanner daemon:

tidal-server scan daemon --interval-seconds 300

Kick daemon:

tidal-server kick daemon --broadcast --sender 0xYourAddress --account wavey3

API:

tidal-server api serve

The API host and port normally come from ~/.tidal/config.yaml:

  • tidal_api_host
  • tidal_api_port

systemd Example

API service:

[Unit]
Description=Tidal API Server (FastAPI/uvicorn)
After=network.target

[Service]
Type=simple
User=wavey
Group=wavey
Environment=TIDAL_HOME=/home/wavey/.tidal
EnvironmentFile=/home/wavey/.tidal/.env
ExecStart=/home/wavey/.local/bin/tidal-server api serve
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Scanner oneshot service:

[Unit]
Description=Tidal Scan (oneshot)
After=network.target

[Service]
Type=oneshot
User=wavey
Group=wavey
Environment=TIDAL_HOME=/home/wavey/.tidal
EnvironmentFile=/home/wavey/.tidal/.env
ExecStart=/home/wavey/.local/bin/tidal-server scan run

Pair the scan oneshot with a systemd timer or external scheduler.

Adjust /home/wavey/.local/bin/tidal-server to whatever command -v tidal-server returns on the target host.

Reverse Proxy Example

Minimal nginx shape:

server {
    listen 80;
    listen [::]:80;
    server_name api.tidal.wavey.info;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name api.tidal.wavey.info;

    location / {
        proxy_pass http://127.0.0.1:8020;
        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;
    }
}

DNS And TLS

For the API hostname, point an A record at your server before requesting certificates:

api.tidal.wavey.info A <server-ip>

Then issue the certificate with your normal ACME flow, for example certbot --nginx.

API Key Management

Create:

tidal-server auth create --label alice

List:

tidal-server auth list

Revoke:

tidal-server auth revoke alice

The API stores only SHA-256 hashes of keys. The plaintext key is shown once at creation time.

Database Notes

SQLite is the canonical datastore for this repo.

Runtime behavior:

  • journal mode: WAL
  • busy timeout: 30 seconds
  • synchronous mode: NORMAL

That configuration is set in tidal/persistence/db.py.

Operational implications:

  • keep the database on local disk
  • avoid multiple independent writers outside the app
  • expect occasional lock retries under write pressure
  • back up the .db, .db-wal, and .db-shm files consistently

Also ignore runtime data directories in git. A server-local data/ directory should never be committed. With the current layout, the canonical runtime home is ~/.tidal/, not a repo-local data/ directory.

Auth Model

Public endpoints:

  • dashboard
  • logs
  • kick inspect
  • deploy defaults
  • AuctionScan lookups
  • health

Authenticated endpoints:

  • kick prepare
  • auction prepare routes
  • action audit routes

Authentication is bearer-token based. Operator identity is currently the API key label.

Monitoring And Troubleshooting

Useful commands:

tidal-server logs scans
tidal-server logs kicks
tidal-server logs show <run_id>

Command Reference

Use these pages for the exact server operator command surfaces:

Useful symptoms:

  • no candidates: check scanner freshness, token prices, and auction mappings
  • repeated database is locked: investigate overlapping long-lived writes
  • API 503 No API keys configured: create at least one key with tidal-server auth create
  • missing receipt reconciliation: verify RPC_URL is present in the API process environment

Useful logs:

journalctl -u tidal-api -f
journalctl -u tidal-scan -f

Deployment Boundaries

Do not point multiple CLI clients directly at the SQLite database. The intended model is:

  • server owns DB and preparation logic
  • CLI client talks over HTTP
  • CLI client signs locally

That keeps schema changes and audit behavior centralized.