Skip to content

KitStream/restium

Repository files navigation

Restium

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.

Quick Start

# 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.yaml

Example: Netbird Bootstrapping

global:
  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_network
export NETBIRD_TOKEN="your-api-token"
restium reconcile --spec netbird.yaml

Restium 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.

Spec Reference

Global Settings

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

Resource Fields

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

References

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.

Failure Modes

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

Authentication

All credentials come from environment variables — never from spec files.

Bearer Token

auth:
  type: bearer
  token_env: MY_API_TOKEN

Sets Authorization: Bearer <value> header.

Basic Auth

auth:
  type: basic
  username_env: API_USER
  password_env: API_PASS

Sets Authorization: Basic <base64> header.

API Key

# 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_key

OIDC Client Credentials

auth:
  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  # optional

Fetches an access token via OAuth2 client_credentials grant and sets Authorization: Bearer <token>.

mTLS

auth:
  type: mtls
  client_cert_path: /certs/client.pem
  client_key_path: /certs/client.key

Presents client certificate during TLS handshake. Combine with ca_bundle for internal CAs.

CLI Reference

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]

Exit Codes

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)

Environment Variables

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

Security

  • 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-tls requires 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

Deployment

Docker

docker run --rm \
  -v $(pwd)/spec.yaml:/config/spec.yaml \
  -e NETBIRD_TOKEN="$NETBIRD_TOKEN" \
  ghcr.io/kitstream/restium:latest \
  reconcile --spec /config/spec.yaml

Kubernetes Job

apiVersion: 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: Never

Helm

helm install restium-bootstrap charts/restium \
  --set secretName=restium-credentials

Sidecar Mode

Use --sidecar to keep the process alive after reconciliation completes — useful for sidecar containers that must not exit:

restium --sidecar reconcile --spec /config/spec.yaml

Development

make 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 build

License

Apache-2.0

About

A declarative reconciling REST client in Rust

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages