orcinus api serves a REST API over the same engine the CLI uses, so anything
you can orcinus deploy you can also POST /api/v1/deploy. It ships an OpenAPI
spec and an interactive Swagger UI.
- Interactive docs:
GET /docs(Swagger UI) - Spec:
GET /openapi.json(or/openapi.yaml)
- 1. Starting the server
- 2. Authentication
- 3. Endpoints
- 4. Deploy & convert
- 5. Projects, scale, rollback
- 6. Secrets
- 7. Plugins
- 8. Cluster & system
- 9. Notes
orcinus api --addr :8080 --token "$(openssl rand -hex 16)"
# → docs: http://:8080/docs
# openapi: http://:8080/openapi.json| Flag | Default | Description |
|---|---|---|
--addr |
:8080 |
Listen address |
--token |
— | Bearer token for /api/v1/* (or $ORCINUS_API_TOKEN) |
--kubeconfig |
auto | Cluster to target (default: ~/.orcinus/kubeconfig, $KUBECONFIG, ~/.kube/config) |
The server resolves the cluster the same way the CLI does; run it wherever a kubeconfig for your orcinus cluster is available. It shuts down gracefully on SIGINT/SIGTERM.
If --token (or $ORCINUS_API_TOKEN) is set, every /api/v1/* request must send
Authorization: Bearer <token>. The open routes — /healthz, /version,
/openapi.json, /openapi.yaml, /docs — never require it. If no token is set
the API is unauthenticated (the server prints a warning) — only do that on a
trusted network.
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/projects| Method & path | Description |
|---|---|
GET /healthz |
Liveness |
GET /version |
Build & component versions |
POST /api/v1/convert |
Render input to manifests (no apply) |
POST /api/v1/deploy |
Convert + apply (auto-installs required plugins) |
GET /api/v1/projects |
List deployed projects |
GET /api/v1/projects/{project}/pods |
List a project's pods |
DELETE /api/v1/projects/{project} |
Remove a project's resources |
POST /api/v1/projects/{project}/services/{service}/scale |
Scale a service |
POST /api/v1/projects/{project}/services/{service}/rollback |
Roll back a service |
GET /api/v1/secrets |
List secrets (name, type, key names) |
POST /api/v1/secrets |
Create an opaque secret — replaces an existing one |
GET /api/v1/secrets/{name} |
Show a secret's keys (?showValues=true for values) |
PATCH /api/v1/secrets/{name} |
Set keys, keeping the ones not named |
POST /api/v1/secrets/tls |
Create a TLS secret from an inline PEM cert + key |
DELETE /api/v1/secrets/{name} |
Delete a secret |
GET /api/v1/plugins |
List plugins + install state |
POST /api/v1/plugins/{name} |
Install a plugin |
DELETE /api/v1/plugins/{name} |
Remove a plugin |
GET /api/v1/cluster |
Cluster status |
All examples below assume TOKEN and a server at localhost:8080.
deploy and convert accept either a JSON body or a raw compose/manifest
body (with options as query params).
JSON body:
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"source": "services:\n web:\n image: nginx:1.27\n ports: [\"80\"]\n",
"project": "shop",
"wait": true
}' \
http://localhost:8080/api/v1/deploy
# → {"applied":3,"project":"shop","installed":[]}Raw YAML body (options as query params) — convenient with a file:
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/yaml" \
--data-binary @orcinus.yml \
"http://localhost:8080/api/v1/deploy?project=shop&wait=true&prune=true"convert returns the rendered manifests without touching the cluster:
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/yaml" \
--data-binary @orcinus.yml \
"http://localhost:8080/api/v1/convert?project=shop"
# → {"objects":3,"manifests":"apiVersion: apps/v1\nkind: Deployment\n..."}DeployRequest fields: source (required), project (default default),
namespace, mode (""|compose|manifest), replicas, pvcSize, prune
(default true), wait, acmeEmail. Like the CLI, deploy auto-installs
cert-manager (for x-orcinus-tls, needs acmeEmail) and argo-rollouts (for
x-orcinus-rollout).
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/projects
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/projects/shop/pods
# Scale service "web" of project "shop" to 3
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"replicas":3}' \
http://localhost:8080/api/v1/projects/shop/services/web/scale
# Roll back "web" to its previous revision
curl -H "Authorization: Bearer $TOKEN" -X POST \
http://localhost:8080/api/v1/projects/shop/services/web/rollback
# Remove the whole project
curl -H "Authorization: Bearer $TOKEN" -X DELETE \
http://localhost:8080/api/v1/projects/shopAdd ?namespace=<ns> where relevant (defaults to default).
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"app-config","data":{"FOO":"bar","BAZ":"qux"}}' \
http://localhost:8080/api/v1/secrets
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/secrets
curl -H "Authorization: Bearer $TOKEN" -X DELETE http://localhost:8080/api/v1/secrets/app-configRead one secret. Keys are listed and values withheld unless asked for, so a listing is safe to fetch:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/secrets/app-config
# {"keys":["BAZ","FOO"],"managedBy":true,"name":"app-config",
# "namespace":"default","redacted":true,"type":"Opaque"}
curl -H "Authorization: Bearer $TOKEN" \
'http://localhost:8080/api/v1/secrets/app-config?showValues=true'Change one key. POST writes the whole secret and drops keys it was not
given; PATCH merges, and creates the secret if it is absent:
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-X PATCH -d '{"data":{"FOO":"updated"}}' \
http://localhost:8080/api/v1/secrets/app-config
# {"keys":2,"name":"app-config","namespace":"default","set":1,
# "note":"running pods keep the old values until restarted"}Env vars are injected when a container starts, so a changed secret does not
reach a running pod — restart the service afterwards
(POST /api/v1/projects/{project}/services/{service}/restart).
BYO TLS cert — the CLI reads PEM files, over HTTP the contents go inline:
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "{\"name\":\"mysite-cert\",\"cert\":$(jq -Rs . < fullchain.pem),\"key\":$(jq -Rs . < privkey.pem)}" \
http://localhost:8080/api/v1/secrets/tlsEvery secret route takes ?namespace= (default default). Secrets created here
are labelled managed-by=orcinus, so they show up in
GET /api/v1/secrets with ManagedBy: true.
Compose keys that reference local files do not work over HTTP. Only the compose text is uploaded, so
env_file:,configs:/secrets:withfile:, and bind mounts have nothing to resolve against and the request fails naming the missing path. Inline the values (environment:), or reference a Secret that already exists in the cluster withx-orcinus-env-from-secret— that one works over HTTP, since it resolves in the cluster rather than on disk.
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/plugins
# Install cert-manager (options mirror the CLI plugin flags)
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"email":"you@example.com"}' \
http://localhost:8080/api/v1/plugins/cert-manager
# Install object storage (MinIO, distributed)
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"provider":"minio","replicas":4,"size":"10Gi"}' \
http://localhost:8080/api/v1/plugins/storage
curl -H "Authorization: Bearer $TOKEN" -X DELETE http://localhost:8080/api/v1/plugins/cert-managercurl http://localhost:8080/healthz # {"status":"ok"}
curl http://localhost:8080/version
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/cluster- Errors are JSON:
{"error":"..."}with a matching HTTP status (400 bad input, 401 unauthorized, 404 unknown plugin, 503 no cluster reachable). - Parity with the CLI. Both front-ends call
pkg/engine, so conversion, auto-install, prune and ownership labels behave identically. - TLS. Terminate TLS in front of the API (a reverse proxy / ingress) for production; the server speaks plain HTTP.
- The OpenAPI spec is the source of truth — browse it at
/docs.