A declarative reconciling REST client in Rust. Define REST resources in YAML, and Restium converges your API state to match — creating, updating, or cleaning up resources as needed.
Replace fragile curl scripts with readable, reviewable YAML.
Restium is built for platform engineers who need to provision resources against REST APIs (Netbird, Keycloak, API gateways) as part of automated deployment pipelines. It ships as a 3.5MB distroless container — no shell, no package manager, zero CVEs.
# Install (after crate is published)
cargo install restium
# Or build from source
cargo build --release
# Or pull the container
docker pull ghcr.io/kitstream/restium:latest
# Validate your spec
restium validate --spec resources.yaml
# Reconcile — converge API state to match your spec
restium reconcile --spec resources.yamlglobal:
base_url: https://api.netbird.io/api
auth:
type: bearer
token_env: NETBIRD_TOKEN
resources:
- name: internal_network
endpoint: /networks
read_endpoint: /networks/internal
payload:
name: internal
description: Internal service network
outputs:
id: id
- name: monitoring_route
endpoint: /routes
payload:
network_id: "${internal_network.output.id}"
peer: monitoring-peer
network: 10.100.0.0/24
description: Route to monitoring subnet
depends_on:
- internal_network
- name: monitoring_access_policy
endpoint: /policies
payload:
name: monitoring-access
enabled: true
rules:
- sources: ["monitoring-group"]
destinations: ["${internal_network.output.id}"]
action: accept
depends_on:
- internal_networkexport NETBIRD_TOKEN="your-api-token"
restium reconcile --spec netbird.yamlRestium resolves dependencies automatically: internal_network is created first, its id is extracted, then monitoring_route and monitoring_access_policy use that ID in their payloads. On subsequent runs, unchanged resources are skipped.
| Field | Type | Default | Example | Description |
|---|---|---|---|---|
base_url |
string | — | https://api.example.com |
Base URL prepended to all resource endpoints |
default_headers |
map | — | Content-Type: application/json |
Headers applied to all requests (overridable per resource) |
auth |
object | — | See Authentication | Global authentication config |
ca_bundle |
string | — | /etc/ssl/custom-ca.pem |
Path to PEM CA bundle for internal/self-signed certs |
| Field | Type | Default | Example | Description |
|---|---|---|---|---|
name |
string | required | my_resource |
Unique resource identifier |
endpoint |
string | required | /api/v1/resources |
API endpoint path (appended to base_url) |
method |
string | POST |
PUT |
HTTP method for create operations |
payload |
object | — | name: foo |
Request body (YAML, sent as JSON) |
headers |
map | — | X-Custom: value |
Per-resource headers (merged with global, overrides on conflict) |
base_url |
string | global | https://other-api.com |
Override global base URL for this resource |
depends_on |
list | — | [network, policy] |
Explicit dependency on other resource names |
read_endpoint |
string | — | /api/v1/resources/mine |
GET endpoint for state discovery (enables idempotent updates) |
outputs |
map | — | id: id |
Extract fields from API response (output_key: json_field) |
action |
string | — | delete |
Set to delete for explicit resource deletion |
auth |
object | global | See Authentication | Per-resource auth override |
Use ${resource_name.output.field} to reference outputs from other resources. Dependencies are resolved automatically.
payload:
network_id: "${my_network.output.id}"If a reference cannot be resolved, Restium reports which resource and field are missing.
| Condition | Exit Code | Error |
|---|---|---|
| Spec file not found | 2 | Failed to read spec file '<path>': No such file |
| Invalid YAML | 2 | Failed to parse spec file '<path>': <details> |
| Unknown field in spec | 2 | unknown field '<name>' |
| Broken reference | 2 | Resource '<name>' references unknown resource '<ref>' |
| Circular dependency | 2 | Circular dependency detected: a -> b -> a |
| Missing env var for auth | 2 | Environment variable '<VAR>' is not set |
| API error during reconcile | 1 | Failed to create resource '<name>': 403 Forbidden on POST /api/... — check authentication token permissions |
All credentials come from environment variables — never from spec files.
auth:
type: bearer
token_env: MY_API_TOKENSets Authorization: Bearer <value> header.
auth:
type: basic
username_env: API_USER
password_env: API_PASSSets Authorization: Basic <base64> header.
# As a header
auth:
type: api_key
key_env: MY_API_KEY
header_name: X-API-Key
# As a query parameter
auth:
type: api_key
key_env: MY_API_KEY
query_param: api_keyauth:
type: oidc
token_url: https://auth.example.com/oauth/token
client_id_env: OIDC_CLIENT_ID
client_secret_env: OIDC_CLIENT_SECRET
scope: api:read api:write # optionalFetches an access token via OAuth2 client_credentials grant and sets Authorization: Bearer <token>.
auth:
type: mtls
client_cert_path: /certs/client.pem
client_key_path: /certs/client.keyPresents client certificate during TLS handshake. Combine with ca_bundle for internal CAs.
restium [OPTIONS] <COMMAND>
Commands:
reconcile Converge API state to match the spec
validate Validate spec file without making API calls
Options:
--json Structured JSON log output [env: RESTIUM_JSON]
--insecure-tls Skip TLS certificate verification [env: RESTIUM_INSECURE_TLS]
--sidecar Keep process alive after completion [env: RESTIUM_SIDECAR]
Subcommand options:
--spec <PATH> Path to YAML spec file [env: RESTIUM_SPEC]
| Code | Meaning |
|---|---|
| 0 | All resources reconciled successfully |
| 1 | One or more resources failed during reconciliation |
| 2 | Spec validation error (bad YAML, broken refs, cycles, missing env vars) |
All flags can be set via RESTIUM_* environment variables:
| Variable | Equivalent Flag |
|---|---|
RESTIUM_SPEC |
--spec |
RESTIUM_JSON |
--json |
RESTIUM_INSECURE_TLS |
--insecure-tls |
RESTIUM_SIDECAR |
--sidecar |
- Distroless container:
FROM scratch— no shell, no package manager, no OS packages, zero CVEs - No secrets in logs: Credentials are automatically redacted in all log output
- TLS by default: Certificate verification enabled;
--insecure-tlsrequires explicit opt-in - Credentials via env vars: Auth tokens never appear in spec files
- Non-root: Container runs as user 65534 (nobody)
- 3.5MB image: Minimal attack surface
docker run --rm \
-v $(pwd)/spec.yaml:/config/spec.yaml \
-e NETBIRD_TOKEN="$NETBIRD_TOKEN" \
ghcr.io/kitstream/restium:latest \
reconcile --spec /config/spec.yamlapiVersion: batch/v1
kind: Job
metadata:
name: restium-bootstrap
spec:
template:
spec:
containers:
- name: restium
image: ghcr.io/kitstream/restium:latest
args: ["reconcile", "--spec", "/config/spec.yaml"]
volumeMounts:
- name: spec
mountPath: /config
envFrom:
- secretRef:
name: restium-credentials
volumes:
- name: spec
configMap:
name: restium-spec
restartPolicy: Neverhelm install restium-bootstrap charts/restium \
--set secretName=restium-credentialsUse --sidecar to keep the process alive after reconciliation completes — useful for sidecar containers that must not exit:
restium --sidecar reconcile --spec /config/spec.yamlmake fmt # cargo fmt
make lint # cargo clippy --all-targets -- -D warnings
make test # cargo test
make build # cargo build --release
make cross # cargo zigbuild for musl targets
make docker-build # docker buildApache-2.0