Skip to content

API and MCP ​

API keys ​

Long-lived tokens for scripts and AI agents, separate from user accounts — a machine cannot do one-time passwords, and it should not borrow someone's password.

Format is walis_<prefix>_<124-bit secret>; the database keeps only a domain-separated SHA-256 lookup hash and the plaintext is returned once. A fixed-cost hash is the right choice here because the secret is machine-generated and high-entropy, unlike a password.

Keys carry a role (admin, operator, viewer) and a scope:

ScopeReaches
mcp/mcp only. The safe default for a new key.
apiREST and MCP, subject to the key's role.
runnerRegister, heartbeat, claim and report — for one runner name only. Set automatically when a key names a runner.

No key of any scope can touch sign-in, accounts, or key lifecycle, so a leaked key cannot breed more.

bash
curl -H "Authorization: Bearer walis_..." http://127.0.0.1:11120/api/jobs

MCP ​

The API speaks MCP directly at /mcp, so an agent needs a URL and a key — nothing to install:

bash
# Claude Code
claude mcp add --transport http walis http://127.0.0.1:11120/mcp \
  --header "Authorization: Bearer walis_..."

# Codex CLI — reads the token from an environment variable
codex mcp add walis --url http://127.0.0.1:11120/mcp --bearer-token-env-var WALIS_API_KEY

Tools: list_jobs, get_job, trigger_build, wait_for_build, get_build, get_build_log, list_builds. Jobs can be named rather than referenced by id.

Settings, accounts and key management are deliberately not exposed. Those are irreversible or credential-bearing, and belong to a human in the admin UI; a leaked key stays limited to triggering builds and reading logs.

/mcp goes through the same authentication as /api — it can trigger builds, so it is not left open just because it sits outside the /api prefix.

Health ​

GET /health needs no key and reports which database the node is using, what it calls itself, and whether the deployment is clustered:

json
{ "status": "ok", "database": "PostgreSQL", "node": "walis-a", "clustered": true }

clustered is false on SQLite, which is single-node by definition. Behind a load balancer this is the quickest way to confirm which node answered. See High availability.

Metrics ​

GET /metrics returns Prometheus text: job counts, queue depth, builds by status, average duration and queue time, runners by state.

It sits outside /api so a local scraper has a stable path that does not move with the API surface, but it is not unauthenticated: the authentication middleware lists /metrics alongside the administrator-only paths. Point your scraper at it with an admin-scoped key.

Portability ​

GET /api/portability/export exports every job and custom action without ids, statistics or timestamps — those describe this machine, not the definition. Import matches by name: existing jobs are skipped unless you ask to overwrite, and overwriting updates them in place so build history survives.

Released under the MIT License.