diff --git a/OPERATIONS.md b/OPERATIONS.md new file mode 100644 index 0000000..8d15936 --- /dev/null +++ b/OPERATIONS.md @@ -0,0 +1,153 @@ +# Operations + +Production server management for the st0x REST API. + +## Server Access + +```sh +ssh root@ +``` + +## Service Management + +The API runs as a systemd service called `rest-api.service`. + +```sh +# Status +systemctl status rest-api.service + +# Restart +systemctl restart rest-api.service + +# Stop / Start +systemctl stop rest-api.service +systemctl start rest-api.service +``` + +## Logs + +Live logs: + +```sh +journalctl -u rest-api.service -f +``` + +Historical logs (last hour, last 100 lines, since a date): + +```sh +journalctl -u rest-api.service --since "1 hour ago" +journalctl -u rest-api.service -n 100 +journalctl -u rest-api.service --since "2026-03-01" +``` + +### File-based logs + +All tracing output is also written as structured JSON to daily-rotating log files. The log directory is configured via `log_dir` in the config file. + +Production log directory: `/mnt/data/st0x-rest-api/logs` + +Files follow the pattern `st0x-rest-api.log.YYYY-MM-DD` — a new file is created each day: + +``` +st0x-rest-api.log.2026-03-08 +st0x-rest-api.log.2026-03-09 +st0x-rest-api.log.2026-03-10 +``` + +To browse them: + +```sh +ls -lt /mnt/data/st0x-rest-api/logs/ +tail -f /mnt/data/st0x-rest-api/logs/st0x-rest-api.log.$(date +%Y-%m-%d) +``` + +Since the files are JSON, you can use `jq` to filter: + +```sh +# All errors from today +cat /mnt/data/st0x-rest-api/logs/st0x-rest-api.log.$(date +%Y-%m-%d) | jq 'select(.level == "ERROR")' + +# Requests to a specific endpoint +cat /mnt/data/st0x-rest-api/logs/st0x-rest-api.log.$(date +%Y-%m-%d) | jq 'select(.fields.uri | contains("/v1/tokens"))' +``` + +Locally, the same file-based logs are written to `./logs/` (relative to where you run the server). + +## Configuration + +The production config is passed via the `ExecStart` line in the service file. To find the current config path: + +```sh +systemctl cat rest-api.service | grep ExecStart | grep -o '\-\-config [^ ]*' | cut -d' ' -f2 +``` + +Production database: `sqlite:///mnt/data/st0x-rest-api/st0x.db` + +## Production Key Management + +Key management commands on the server use the production config path. First, find it: + +```sh +CONFIG=$(systemctl cat rest-api.service | grep ExecStart | grep -o '\-\-config [^ ]*' | cut -d' ' -f2) +``` + +Then use the binary directly: + +```sh +BINARY=/nix/var/nix/profiles/per-service/rest-api/bin/st0x_rest_api + +# Create a standard key +$BINARY keys --config $CONFIG create --label "partner-name" --owner "contact@example.com" + +# Create an admin key (required for /admin/* endpoints) +$BINARY keys --config $CONFIG create --label "ops-admin" --owner "ops@example.com" --admin + +# List keys +$BINARY keys --config $CONFIG list + +# Revoke a key +$BINARY keys --config $CONFIG revoke + +# Delete a key +$BINARY keys --config $CONFIG delete +``` + +## Registry Updates + +The strategy registry URL can be updated at runtime via the admin API: + +```sh +curl -X PUT \ + -u "ADMIN_KEY_ID:ADMIN_SECRET" \ + -H "Content-Type: application/json" \ + -d '{"registry_url":"https://raw.githubusercontent.com/rainlanguage/rain.strategies//registry"}' \ + https:///admin/registry -k +``` + +Admin credentials are shared separately and should never be committed. + +There is no dedicated admin-key rotation command. Rotate admin credentials manually: + +1. Create a new admin key with `--admin` +2. Update the systems or operators using the old credentials +3. Revoke or delete the old admin key + +## Deployment + +Deployments are triggered manually via the **Deploy** workflow in GitHub Actions (`cd.yaml`, `workflow_dispatch`). To deploy: + +1. Go to the repo's **Actions** tab +2. Select the **Deploy** workflow +3. Click **Run workflow** on the target branch + +The workflow builds the Nix package and deploys it to the server via `deploy-rs`. + +## API Documentation + +User-facing API docs are built with mdbook from `docs/src/`. On the server, the built docs are served from `/var/lib/st0x-docs`. + +To update the docs: + +1. Edit files in `docs/src/` +2. Rebuild locally: `nix develop -c mdbook build docs` +3. Deploy (docs are included in the deployment) diff --git a/README.md b/README.md index ae6dafd..79ee313 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ REST API for st0x orderbook operations. Built with Rocket, backed by SQLite, and authenticated via API keys using HTTP Basic auth. +## Prerequisites + +- [Nix](https://nixos.org/download/) with flakes enabled + ## Setup ### 1. Clone and initialize submodules @@ -18,92 +22,89 @@ git submodule update --init --recursive nix develop -c bash prep.sh ``` -This writes `COMMIT_SHA` to `.env` and bootstraps the orderbook submodule. - -## Usage +This initializes submodules, runs the orderbook prep script, and builds the local mdBook into `docs/book` for the `/docs` route. Run it before the first `serve`. -The binary has two subcommands: +### 3. Configure -``` -st0x_rest_api serve Start the API server -st0x_rest_api keys Manage API keys -``` +The config file is at `config/dev.toml`. The only value that may need updating is `registry_url` — it points to a pinned GitHub raw URL so it should work out of the box. -### Starting the server +If it's stale, grab the latest from production: ```sh -nix develop -c cargo run serve +curl -u "KEY_ID:SECRET" https:///registry -k ``` -The server starts on `http://localhost:8000` by default. Swagger UI is available at `/swagger`. +and update `registry_url` in `config/dev.toml`. -### API key management - -All API routes (except `/health`) require HTTP Basic authentication. Use the `keys` subcommand to manage credentials. - -#### Create a key +### 4. Create an API key ```sh -nix develop -c cargo run keys create --label "partner-x" --owner "contact@example.com" +nix develop -c cargo run -- keys --config config/dev.toml create \ + --label "dev-key" \ + --owner "you@example.com" ``` -Output: - -``` -API key created successfully +This prints a **Key ID** (UUID) and **Secret** (base64 string). Save both — the secret is hashed with Argon2 and cannot be recovered. -Key ID: -Secret: -Label: partner-x -Owner: contact@example.com +### 5. Set log level (optional) -IMPORTANT: Store the secret securely. It will not be shown again. +```sh +export RUST_LOG=st0x_rest_api=info,rocket=warn,warn ``` -The secret is hashed with Argon2 before storage. There is no way to recover it. - -#### List keys +### 6. Start the server ```sh -nix develop -c cargo run keys list +COMMIT_SHA=$(git rev-parse HEAD) nix develop -c cargo run -- serve --config config/dev.toml ``` -Shows all keys with their ID, label, owner, active status, and timestamps. +Server runs on `http://127.0.0.1:8000`. Swagger UI is available at `/swagger`, and the mdBook docs are served at `/docs`. -#### Revoke a key +The database (`data/st0x.db`) is created and migrated automatically on first run — no manual DB setup needed. + +### 7. Test it ```sh -nix develop -c cargo run keys revoke +curl -u "KEY_ID:SECRET" http://localhost:8000/v1/tokens ``` -Sets the key to inactive. Revoked keys are rejected at authentication. +API routes require HTTP Basic Auth with the key ID and secret. `/health`, `/docs`, `/swagger`, and `/api-doc/openapi.json` are public. + +## API Key Management -#### Delete a key +Use the `keys` subcommand to manage credentials. All commands require `--config` to point at the config file. ```sh -nix develop -c cargo run keys delete -``` +# List keys +nix develop -c cargo run -- keys --config config/dev.toml list + +# Revoke a key (sets it to inactive, rejected at auth) +nix develop -c cargo run -- keys --config config/dev.toml revoke -Permanently removes the key from the database. +# Delete a key (permanent removal) +nix develop -c cargo run -- keys --config config/dev.toml delete +``` -### Authenticating API requests +## Authenticating API Requests Use HTTP Basic auth with the key ID as the username and the secret as the password: ```sh -curl -u ":" http://localhost:8000/v1/tokens +curl -u "KEY_ID:SECRET" http://localhost:8000/v1/tokens ``` Or with an explicit header: ```sh -curl -H "Authorization: Basic $(echo -n ':' | base64)" http://localhost:8000/v1/tokens +curl -H "Authorization: Basic $(echo -n 'KEY_ID:SECRET' | base64)" http://localhost:8000/v1/tokens ``` ## Development ```sh -nix develop -c cargo fmt -nix develop -c rainix-rs-static -nix develop -c cargo test +nix develop -c cargo fmt # format +nix develop -c rainix-rs-static # lint +nix develop -c cargo test # test ``` + +Always run the formatter and linter before committing.