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/apiauth:
type: bearertoken_env: NETBIRD_TOKENresources:
- name: internal_networkendpoint: /networksread_endpoint: /networks/internalpayload:
name: internaldescription: Internal service networkoutputs:
id: id
- name: monitoring_routeendpoint: /routespayload:
network_id: "${internal_network.output.id}"peer: monitoring-peernetwork: 10.100.0.0/24description: Route to monitoring subnetdepends_on:
- internal_network
- name: monitoring_access_policyendpoint: /policiespayload:
name: monitoring-accessenabled: truerules:
- sources: ["monitoring-group"]destinations: ["${internal_network.output.id}"]action: acceptdepends_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: bearertoken_env: MY_API_TOKENSets Authorization: Bearer <value> header.
auth:
type: basicusername_env: API_USERpassword_env: API_PASSSets Authorization: Basic <base64> header.
# As a headerauth:
type: api_keykey_env: MY_API_KEYheader_name: X-API-Key# As a query parameterauth:
type: api_keykey_env: MY_API_KEYquery_param: api_keyauth:
type: oidctoken_url: https://auth.example.com/oauth/tokenclient_id_env: OIDC_CLIENT_IDclient_secret_env: OIDC_CLIENT_SECRETscope: api:read api:write # optionalFetches an access token via OAuth2 client_credentials grant and sets Authorization: Bearer <token>.
auth:
type: mtlsclient_cert_path: /certs/client.pemclient_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/v1kind: Jobmetadata:
name: restium-bootstrapspec:
template:
spec:
containers:
- name: restiumimage: ghcr.io/kitstream/restium:latestargs: ["reconcile", "--spec", "/config/spec.yaml"]volumeMounts:
- name: specmountPath: /configenvFrom:
- secretRef:
name: restium-credentialsvolumes:
- name: specconfigMap:
name: restium-specrestartPolicy: 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