API

Keys for programs. Issuers for people.

The API the portal calls, and a program calls with a key. The installer itself does not use a key. It fetches the label URL. The install routes are live. The service and platform routes are designed, not built.

API host: https://api.blunix.io/v1

Three names

The label host, the API host, and the update host are different names so a build URL cannot be mistaken for the API. The portal is https://build.blunix.io/.

API keys

A key is for a program, such as the build-proxy. A person keeps using the issuer. A person mints and revokes keys in the portal.

Sign-in

People sign in with After Dark Systems SSO, our OpenID Connect issuer at adsas.id. It is the only issuer today. Google and GitHub are designed for later, under the same checks.

Routes

JSON in and out, except the ciphertext upload. A JSON body must be application/json, at most 8KiB. Errors are fixed strings, {"error":"..."}. The server does not echo a body back, and it does not decrypt.

The install routes in /v1. Live.
Call Who Result
GET /v1/health anyone GET or HEAD. {"ok":true}. No cookie, no auth. CORS allows https://blunix.io to read it, without credentials. This site uses it to say whether the portal is open.
GET /v1/auth/login anyone 302 to the issuer. The PKCE state is kept on the server for 10 minutes. 429 past the rate limit. 503 {"error":"sign-in unavailable"} when the issuer cannot be reached.
GET /v1/auth/callback the issuer Verify, create or find the account, set the session cookie, and 302 back to the portal. Any failure is 400 {"error":"sign-in failed"}.
POST /v1/auth/logout session Delete the session and clear the cookie. 204. A key is 403.
GET /v1/me session or key {"account":{"name","id","created"},"labels":[...]}. name is there only when sign-in gave one. No secrets.
POST /v1/hosts session, or hosts:write Reserve with {"label":"ada"}. 201 with label, created, and url. 409 if taken or held. 400 if invalid or reserved. 403 {"error":"label limit"} past 20 labels per account.
GET /v1/hosts session, hosts:read, or hosts:write {"hosts":[...]}: each label with created, url, and latest, which is null before the first upload.
GET /v1/hosts/{label} owner: session, hosts:read, or hosts:write The label, when it was made, and its versions: version, sha256, size, created, pinnedUrl. Another account's label is 404, the same as one that does not exist.
DELETE /v1/hosts/{label} owner: session, or hosts:write 204. The label and every version stop serving. The name is held 30 days. Version numbers are never reused, even if the name is reserved again.
POST /v1/hosts/{label}/builds owner: session, or hosts:write The body is raw age ciphertext, application/octet-stream, or 415. 201 with version, sha256, size, url, and pinnedUrl, which is null for now.
DELETE /v1/hosts/{label}/builds/{n} owner: session, or hosts:write 204. That version stops serving. The latest moves back to the highest remaining version.
POST /v1/keys session only Mint a key with {"name":"...","scopes":[...]}. The name is 1 to 64 printable ASCII characters. Scopes are optional. 201 with key, fingerprint, name, scopes, and created. The response is the only time the key appears.
GET /v1/keys session only {"keys":[...]}: fingerprint, name, scopes, and created, for keys not revoked. Never the key.
DELETE /v1/keys/{fingerprint} session only Revoke that key. Immediate. 204, or 404.
Designed for later passes, not built.
Call Who Result
GET /v1/log public Rows for a channel. Same record as the log page.
POST /v1/log publisher Append one version, or one withdrawal. An existing version id is refused. Ordinary keys are refused.
PUT /v1/hosts/{label} account Set visibility to public or enrolled.
POST /v1/hosts/{label}/join-tokens account Mint a blx_join_ token. Shown once. Expires. Use cap.
DELETE /v1/join-tokens/{fingerprint} account Revoke that token. Immediate.
POST /v1/machines join token, once Bind an Ed25519 public key to a hostname and a profile.
POST /v1/machines/{hostname}/checkin machine signature Report the document sha256 and the image digest. Return the desired generation.
GET /v1/machines/{hostname}/desired machine signature The same generation, for a machine that woke up.
GET /v1/machines account Inventory. No secrets.
POST /v1/machines/{hostname}/troubleshoots troubleshoot:request Open a collect. A shell scope and a command field are refused.
GET /v1/machines/{hostname}/troubleshoots/{id} troubleshoot:read, or the machine Status. No report body.
DELETE /v1/machines/{hostname}/troubleshoots/{id} troubleshoot:request Cancel. A later result is refused.
POST /v1/machines/{hostname}/troubleshoots/{id}/result machine signature One report, cap 64KiB. Closes the job.
GET /v1/troubleshoots troubleshoot:read Open jobs for the account. No report bodies.

A stolen API key can replace your ciphertext. It cannot produce a document that decrypts under a build key it does not hold. Revoke the API key. The build key is never a field on any of these calls. Machine routes and join tokens are the service contract. Build, deploy, and the collect are the platform contract.