The control plane API for Fabrium. See the system design for the full picture.
-
Copy the example environment file.
cp .env.example .env
-
Generate a key and keep it safe.
KEY=$(openssl rand -hex 32) -
Hash the key. Use
shasum -a 256instead ofsha256sumon macOS.printf %s "$KEY" | sha256sum
-
Put
zooid:<hash>inAPI_KEYSin.env. Use only the 64-character hash, not the key. -
Start the service.
docker compose up --build
-
Call the API from another terminal.
curl -i localhost:8080/health # 200 {"status":"ok"} curl -i -H "Authorization: Bearer $KEY" localhost:8080/v1/ping # 200 {"pong":true,"client":"zooid"}
The database lives in the data Docker volume. It survives restarts.
| Variable | Default | Docker value | Notes |
|---|---|---|---|
API_KEYS |
none (required) | from .env |
Comma-separated <name>:<sha256-hex> pairs. The service refuses to start if it is missing, empty, or malformed, or if two entries share a name or a hash. |
BIND_ADDR |
127.0.0.1:8080 |
0.0.0.0:8080 |
Set by the image. |
DATABASE_URL |
sqlite://data/control-plane.db |
sqlite:///data/control-plane.db |
Set by the image. The service creates the file and its directory. |
RUST_LOG |
info |
from .env |
Log filter. |
A .env file in the working directory is loaded automatically for local runs.
You need Rust 1.99 or newer.
cargo run
cargo test
cargo fmt --check
cargo clippy --all-targets -- -D warningscargo run needs API_KEYS in your environment or in .env.
The service runs pending migrations at startup. Use the CLI to run them by hand.
cargo run -p migration -- up
cargo run -p migration -- down
cargo run -p migration -- statusThe CLI reads DATABASE_URL. There are no migrations yet. Generate entities later with sea-orm-cli.
| Method | Path | Auth | Response |
|---|---|---|---|
| GET | /health |
none | 200 {"status":"ok"}, or 503 {"status":"unavailable"} if the database does not answer |
| GET | /v1/ping |
Bearer | 200 {"pong":true,"client":"<name>"} |
Send the key in the Authorization: Bearer <key> header. The server hashes the key with SHA-256 and compares it with the stored hashes.
The scheme name is case-insensitive. Extra spaces before the key are ignored.
A missing header, a wrong scheme, or an unknown key returns 401 with WWW-Authenticate: Bearer and a JSON body such as {"error":"missing or invalid API key"}.
Every request has a 10 second limit. A request that runs longer returns 503 Service Unavailable. /health gives the database 2 seconds to answer, so a stalled database returns the JSON 503 above.
The server logs every response at info level with the method, path, status, latency, and x-request-id.