inventory-api.md 7.0 KB

Inventory API

RackPeek exposes a small HTTP API for upserting inventory from scripts and external systems — useful for automatic ingestion from Ansible, Terraform, discovery scanners, or any other tool that can produce a YAML or JSON description of your infrastructure.

The API is served by the same process as the Web UI, on the same port.

What you need

  1. The server URL — wherever RackPeek is running, e.g. http://localhost:8080.
  2. An API key — a shared secret you choose, set via the RPK_API_KEY environment variable on the server. Requests without a matching key are rejected.

Configuring the API key

Set RPK_API_KEY on the RackPeek container or process. Until you set it, the inventory endpoint returns 503 Service Unavailable for every request.

Variable Required Description
RPK_API_KEY Yes (to use the API) Shared secret. Clients must send this in the X-Api-Key header.

Example with Docker Compose:

version: "3.9"

services:
  rackpeek:
    image: aptacode/rackpeek:latest
    container_name: rackpeek
    ports:
      - "8080:8080"
    volumes:
      - rackpeek-config:/app/config
    environment:
      - RPK_API_KEY=change-me-to-a-long-random-string
    restart: unless-stopped

volumes:
  rackpeek-config:

Pick a long random value (e.g. openssl rand -hex 32). The server compares keys in constant time, so timing-based guessing is not viable, but a weak key still loses to brute force — treat it like a password.

Endpoints

GET /health

Liveness check. No authentication. Returns 200 OK with body rackpeek as text/plain. Used by the Docker healthcheck.

curl http://localhost:8080/health
# rackpeek

POST /api/inventory

Upsert resources and connections. Requires the X-Api-Key header.

Request body

Field Type Required Description
yaml string one of yaml or json Raw YAML payload, same schema as config.yaml.
json object one of yaml or json Structured JSON payload (see shape below).
mode "Merge" | "Replace" No (default Merge) How incoming resources combine with existing ones.
dryRun bool No (default false) Compute the diff and return it without writing to disk.

Exactly one of yaml or json must be supplied — sending both is rejected.

Merge semantics

For resources whose name doesn't already exist, the resource is added. For resources whose name already exists:

  • Merge — fields present in the incoming payload overwrite the existing values; fields that are omitted, null, empty lists, or empty maps in the incoming payload are left untouched. Use this for partial updates.
  • Replace — the incoming resource fully replaces the existing one, even for omitted fields. Use this when the incoming payload is the authoritative full picture.

If an incoming resource has a different kind than the existing one with the same name (e.g. server → switch), the existing resource is fully replaced regardless of mode.

JSON payload shape

The json field accepts the same shape as config.yaml — a root object with version, resources, and optional connections. The canonical schemas live in schemas/ in the repository.

{
  "version": 3,
  "resources": [
    {
      "kind": "Server",
      "name": "web-01",
      "tags": ["homelab", "prod"],
      "labels": { "env": "production" },
      "ipmi": true,
      "ram": { "size": 64 }
    }
  ],
  "connections": []
}

The kind discriminator is case-sensitive. Use the exact value for each resource type: Server, Switch, Router, Firewall, AccessPoint, Ups, Desktop, Laptop, Service, System.

Response

200 OK with a diff summary:

Field Description
added Names of resources that were newly created.
updated Names of existing resources whose fields changed under Merge mode.
replaced Names of resources fully overwritten (either Replace mode, or a kind mismatch).
oldYaml YAML snapshot of each updated/replaced resource before the change, keyed by name.
newYaml YAML snapshot of each incoming resource after the change, keyed by name.

oldYaml is omitted for newly-added resources (nothing existed to snapshot).

Error responses

Status When
400 Bad Request Validation failure — neither yaml nor json supplied, both supplied, malformed payload, duplicate resource names, etc. Body: { "error": "..." }.
401 Unauthorized X-Api-Key header missing or doesn't match RPK_API_KEY.
503 Service Unavailable RPK_API_KEY is not configured on the server.

Examples

Upsert from a YAML file with curl

curl -X POST http://localhost:8080/api/inventory \
  -H "X-Api-Key: $RPK_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -Rs --arg mode Merge '{yaml: ., mode: $mode}' < inventory.yaml)"

Upsert a single resource as JSON

curl -X POST http://localhost:8080/api/inventory \
  -H "X-Api-Key: $RPK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "Merge",
    "json": {
      "version": 3,
      "resources": [
        {
          "kind": "Server",
          "name": "web-01",
          "tags": ["homelab"],
          "ram": { "size": 64 }
        }
      ]
    }
  }'

Preview a change with dryRun

curl -X POST http://localhost:8080/api/inventory \
  -H "X-Api-Key: $RPK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dryRun": true,
    "mode": "Replace",
    "json": {
      "version": 3,
      "resources": [
        { "kind": "Server", "name": "web-01", "ram": { "size": 128 } }
      ]
    }
  }'

The response shows what would change without modifying config.yaml.

Bash helper

rpk_ingest() {
  local file="$1"
  curl -fsS -X POST "${RPK_URL:-http://localhost:8080}/api/inventory" \
    -H "X-Api-Key: $RPK_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$(jq -Rs '{yaml: ., mode: "Merge"}' < "$file")"
}

rpk_ingest inventory.yaml

Tips

  • Run with dryRun: true first when wiring up a new pipeline — the response tells you exactly what would change.
  • Prefer Merge when your script only knows about a subset of fields (e.g. a discovery scanner that only sees CPU/RAM but doesn't know about labels/tags). Use Replace when the script owns the full resource.
  • The tags array (a list) is replaced wholesale by any non-empty incoming value, even in Merge mode — if you want to add a single tag without overwriting the others, fetch the resource first or use the CLI (rpk <kind> tag add ...).
  • The labels map (a dictionary) is merged key-wise in Merge mode — existing keys absent from the incoming payload are preserved; keys present in the incoming payload overwrite the existing value.
  • The same YAML you commit to Git via the Git integration is what the API reads and writes — there is no separate API store.