better/compute
Clients

Clients

Every way to call Better Compute, from raw HTTP to the typed client, the CLI, MCP tools and the console, and the credentials each one carries.

@better-compute/server@better-compute/client@better-compute/cli@better-compute/mcp@better-compute/console-kitindex.mdxhttp.tsindex.tsREADME.mdframework.tsserver.tsREADME.mdREADME.mdREADME.mdconsole.config.mjswatch.tsPLAN.md

Better Compute has one API. Every client, whether it is a shell script, the typed TypeScript client, the better-compute CLI, an AI agent's MCP tool or the web console, ends up sending the same request to the same route, and every call is authorized the same way: Better IAM decides one action on one resource, as the caller. No client has a side door, and no client can do more than the credential it carries.

The HTTP API

Every method is POST {basePath}/{group}/{method} with a JSON object body. The default base path is /api/compute, so reservations.create is POST /api/compute/reservations/create. The generated API reference lists all groups and methods; plugin methods live under /api/compute/plugins/{id}/{group}/{method}.

curl -X POST https://compute.example.com/api/compute/pools/list \
  -H "Authorization: Bearer $BETTER_COMPUTE_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-Compute: 1" \
  -d '{"tenantId":"acme"}'
# → { "data": { "items": [ … ] } }

The rules every request follows:

RuleDetail
MethodPOST for API calls. GET answers only {basePath}/health and {basePath}/.well-known/better-compute; anything else is METHOD_NOT_ALLOWED.
HeadersContent-Type: application/json and X-Better-Compute: 1, or the call fails with CSRF_REJECTED before anything is read.
BodyA JSON object, at most 64 KiB, checked against the method's input schema (INVALID_INPUT otherwise).
Answer{ "data": … } on success, { "error": { "code", "message", "details"? } } on failure. See errors.
Request idSend X-Request-Id (up to 128 letters, digits, ., _, : or -) and the server echoes it and passes it to IAM; otherwise it makes one up and returns it.
Rate limitsA RATE_LIMITED answer carries retryAfterMs in the error body and a Retry-After header in seconds.

X-Better-Compute: 1 is the CSRF guard. A browser cannot send a custom header cross-origin without a CORS preflight, and nothing in Better Compute approves one, so a forged form post from another site never reaches a handler.

Before any database access, the server authenticates the caller and refuses a caller whose tenant is neither the input's tenantId nor the root tenant, with the same ACCESS_DENIED whether or not the thing exists. Then it decides once: see how a call is decided.

Credentials

Better Compute authenticates Better IAM credentials only; it has no users, passwords or API keys of its own.

CredentialHow it is sentWho uses it
IAM bearer token (biam_…): a user session, an API key, a role session or a delegated agent sessionAuthorization: Bearer …scripts, services, the CLI, agents
Session JWT from sts.getSessionToken({ format: 'jwt', audience: [...] }), with compute's audienceAuthorization: Bearer …services that verify tokens offline, browser apps on another origin
The IAM session cookiethe browser's cookieonly a console on the same origin, with betterCompute({ acceptIamCookie: true })

The cookie counts only when acceptIamCookie is on and the request's Origin is the deployment's own origin: a cookie request without Origin is CSRF_REJECTED, and one from another origin is UNTRUSTED_ORIGIN. Without the option, compute drops the cookie before it calls IAM. Credentials compute accepts covers what each credential resolves to.

system.version and system.whoami are public: they answer without a credential, which makes them a quick check that a URL points at a compute server.

Ways to call

ClientPackageRunsCredentialBest for
HTTPnoneanywherebearer tokenother languages, quick checks
In-process API@better-compute/serverinside your server{ headers } or { token } per callyour own backend that embeds compute
TypeScript client@better-compute/clientbrowsers, Node.js 22+, edge runtimesbearer token, or the cookie on the same originapps and services in TypeScript
CLI@better-compute/clia terminal or CIBETTER_COMPUTE_TOKENoperators, tenants, CI drift checks
MCP@better-compute/mcpan MCP host you runthe agent's bearer token, forwardedAI agents
Consoleapps/console, @better-compute/console-kita Next.js appthe IAM session cookiepeople in a browser

In process

A backend that builds the control plane with betterCompute() can skip HTTP. compute.api.<group>.<method> takes the caller's credential first and the input second, and runs exactly the same envelope: the tenant pre-check, the IAM decision, the transaction and the audit row.

server.ts
import { compute } from './compute';

const pools = await compute.api.pools.list({ headers: request.headers }, { tenantId: 'acme' });

Pass the incoming request's headers so that IAM sees the real caller. There is no "system" credential that skips the decision.

The typed client

createComputeClient<typeof compute>() infers every group and method from your server's type, with a type-only import, so there is no code generation. It is a small fetch wrapper that adds the headers above and turns error envelopes into ComputeClientError. See TypeScript client.

import { createComputeClient } from 'better-compute/client';
import type { compute } from './compute.server';

const client = createComputeClient<typeof compute>({
  baseURL: 'https://compute.example.com',
  token: () => session.getToken(),
});
const { items } = await client.pools.list({ tenantId: 'acme' });

The CLI

better-compute calls the API as BETTER_COMPUTE_TOKEN, either against a running server with --url or in process through better-compute.config.ts. better-compute api GROUP.METHOD key=value reaches any method; reserve, capacity, fleet plan and friends wrap the common ones. See CLI.

export BETTER_COMPUTE_URL=https://compute.example.com BETTER_COMPUTE_TOKEN=biam_…
better-compute api pools.list tenantId=acme --format table

MCP

createComputeMcp() serves a set of tools (list_pools, reserve_gpus, node_status and more) as a Streamable HTTP MCP endpoint behind Better IAM's MCP gate. Each tool calls the API with the agent's own token, and a reservation that a person must approve comes back as confirmation-required with the idempotency key to retry with. See MCP.

The console

apps/console runs Better IAM and Better Compute in one Next.js process on one origin, so the IAM session cookie signs people in to both. Its pages are built on @better-compute/console-kit, framework-free stores with live updates and permission-aware buttons that you can reuse in your own UI. See console.

Events

Clients that need to know when something changes do not poll list methods. The events section covers the three ways out of the :

  • events.list pages a tenant's history from a cursor, over the same API.
  • GET /watch streams live events as Server-Sent Events. The host mounts it (the console serves it at /api/compute/watch), and console-kit's watchEvents reads it.
  • Webhooks POST signed events to a tenant's HTTPS endpoints.

Idempotency and retries

Methods that create things take an idempotencyKey. Keep the key for the life of one attempt and reuse it on every retry, whether the retry follows a timeout, a RATE_LIMITED answer or a person's approval of a CONFIRMATION_REQUIRED call. The same key with the same input returns the stored result; the same key with a different input is CONFLICT. Reservations shows the rules in full, and delegated confirmation shows the approve-and-retry loop.

Was this page helpful?

Better Compute is created by Sean Filimon

Last updated

On this page