ORCHD

Keys & access

Every /v1/* endpoint is behind an API key. /healthz and /internal/* are the exceptions — see the TLS gate.

Presenting a key

Either header works:

curl https://api.example.com/v1/projects -H "Authorization: Bearer $ORCHD_KEY"
curl https://api.example.com/v1/projects -H "X-API-Key: $ORCHD_KEY"

The bootstrap key

The root key is read from a file on the box (ORCHD_API_KEY_FILE). It is never in the repository, never in the state store, never logged, and it is always admin. That is deliberate: a leaked state file must not be a leaked fleet, and the key that can mint other keys should be recoverable by whoever has shell access and nobody else.

With no key file configured the API is open. That is right for a laptop and never right for a box — the deploy scripts generate one during bootstrap.

Minted keys and roles

Beyond the bootstrap key you can mint named keys:

curl -X POST https://api.example.com/v1/keys \
  -H "Authorization: Bearer $ORCHD_KEY" \
  -d '{"name":"ci-bot","role":"readonly"}'
# -> { "key": "…", "meta": { "id": "…", "name": "ci-bot", "role": "readonly" } }

The plaintext is returned exactly once; only a SHA-256 hash is stored. There is no recovery — revoke and mint a new one.

RoleCan do
readonlyevery GET / HEAD
admineverything, including POST / PUT / DELETE

A readonly key on a mutating call gets 403. This is intentionally a two-role model: ORCHD is not an identity provider, and per-project RBAC is a non-goal — see Goals & non-goals.

GET /v1/keys lists metadata (never secrets); DELETE /v1/keys/{id} revokes.

Rate limiting

ORCHD_RATE_LIMIT sets a per-key token bucket in requests per minute (0 = off). Buckets are per key, so a runaway client cannot starve another one. Over the limit returns 429 with Retry-After.

Error codes

Errors are JSON — { "error": "…" } — with these statuses:

CodeMeans
200 / 201 / 204ok / created / no content
400bad request (malformed body, e.g. an image ref without @version)
401missing or invalid key
403role forbids it (readonly key on a mutating call)
404no such project, workload, route, image or backup
409conflict
429over the per-key rate limit
501capability not available on this driver (e.g. Docker image management on the process driver)

Workload credentials are a separate thing

Do not confuse control-plane keys with the credentials a workload has. A database workload gets its own JWT secret and minted anon / service-role keys at provision time, returned on the project view. Those are the tenant's keys for talking to their own backend; they grant nothing on the control plane.

Audit trail

Control-plane actions emit events — project.created, workload.keepwarm, image.pushed and so on — to a pluggable sink: an in-memory feed the panel shows under Activity, and optionally a webhook that receives them as JSON. GET /v1/events?limit=n reads the recent feed. See Adaptors.

CORS

The API sends permissive CORS headers so the admin panel can call it from a browser with a key. Treat the key as a secret accordingly: it belongs in the operator's browser session or a server-side caller, never in a shipped client bundle.

from the same team

Related projects

ORCHD is built by the makers of these open-source tools and products.