Crucible / Docs

Use the AOS Hub API

AOS Hub exposes unary, Connect-compatible JSON routes over HTTP. Requests use JSON bodies and responses use JSON; clients do not need a gRPC runtime.

#Endpoint shape

Methods are mounted at:

POST /aos.hub.v1.<Service>/<Method>

For example:

curl -fsS \
  -H 'Content-Type: application/json' \
  -d '{"slug":"acme/cdn"}' \
  https://hub.example.com/aos.hub.v1.RegistryService/GetRegistry

An empty request may be sent as {}. API errors use a JSON envelope with a machine-readable code and a human-readable message.

The service families cover registries, organizations, projects, storage, packages, channels, audits, instance settings, identity and access, webhooks, publishing, Git surfaces, and binary caches. The complete request and response schema is in hub.proto.

#Authentication

Public registry reads do not require authentication. Private reads and changes require a bearer access token with the necessary permission:

Authorization: Bearer <access-token>

The simple /<flat-slug>/-/api/... routes documented in the web guide are always public-only: they do not use a browser session or bearer token, and the current router accepts only single-segment slugs. Use the unary service routes for canonical organization/registry paths and authenticated visibility.

Interactive clients start an RFC 8628 device grant with POST /oauth2/device_authorization, show the returned verification URL and user code, and poll POST /oauth2/token at the advertised interval. A successful poll returns a one-hour access token and a rotating refresh credential. aos hub login implements this flow:

aos hub login --hub https://hub.example.com

The device request uses client_id=aos-cli, an optional canonical stable resource scope, and an optional space-separated permission value. Polling uses grant type urn:ietf:params:oauth:grant-type:device_code. Refresh uses grant type refresh_token; every successful refresh returns a replacement refresh credential. Reusing a consumed credential revokes the complete family. POST /oauth2/revoke accepts the refresh credential, client_id=aos-cli, and token_type_hint=refresh_token.

Publishing automation may instead start with a provisioning token whose secret begins with aos_. The Hub stores only its hash. Exchange it with the explicit grant type urn:aos:params:oauth:grant-type:provisioning-token and the secret as an Authorization: Bearer credential. A native operator can mint scoped provisioning tokens with aos-hub token mint.

All OAuth credential responses carry Cache-Control: no-store. Native and Cloudflare Worker deployments mount the same handlers and return the same structured pending, slow-down, denial, expiry, and invalid-grant errors.

The CLI obtains user credentials through aos hub login; the browser console obtains a short-lived bearer from its signed-in session without exposing it to the user. Bootstrap administration through the local aos-hub command on native deployments or the web console on either runtime. Non-browser API clients still need a suitably scoped device-flow or provisioning credential.

Browser authentication uses an opaque session cookie and is intentionally separate from API bearer tokens.

Inspect a bearer without exposing its secret through IdentityService/WhoAmI:

curl -fsS \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <access-token>' \
  -d '{}' \
  https://hub.example.com/aos.hub.v1.IdentityService/WhoAmI

The response identifies the live user or service account, lists current role grants, and separately reports this token's scope, permissions, and expiry.

IdentityService manages generic access tokens with ListAccessTokens, PlanIssueAccessToken/IssueAccessToken, and PlanRetireAccessToken/RetireAccessToken. Requests use canonical stable authorization scopes and native permission verbs such as read, publish, binding.manage, or cache.gc.plan. There are no registry-token RPC aliases. Token metadata includes its non-secret comment, creation, expiry, last-use, rotation, retirement, and lifecycle state; the plaintext secret is returned once by the issuance apply response.

Service accounts use ListServiceAccounts, GetServiceAccount, and reviewed create, update, and delete pairs. A rename changes only the human-facing <org>/<name> reference; the numeric principal identity remains stable. Delete removes direct memberships atomically, while retained token metadata becomes unusable immediately because its owner is no longer live. The former AutomationPrincipal API names are not served.

Organization invitations use ListInvitations, GetInvitation, reviewed PlanCreateInvitation/CreateInvitation and PlanCancelInvitation/CancelInvitation pairs, plus the authenticated AcceptInvitation identity ceremony. Creation returns a 256-bit aosi_ acceptance secret; only its SHA-256 verifier and AES-GCM-sealed recovery copy are stored. Retrying the exact same apply idempotency key returns the same unsealed secret, while a different apply is rejected. Acceptance or cancellation erases the recovery copy immediately; bounded maintenance erases expired copies. A pending invitation is not a user or membership. Acceptance succeeds only for a live user whose canonical email and organization match the invitation, and atomically consumes the secret while creating the exact direct membership. History remains visible as pending, accepted, cancelled, or time-derived expired metadata. Connect responses carry Cache-Control: no-store, Pragma: no-cache, and Referrer-Policy: no-referrer, so secret-bearing mutation results are not retained by shared caches or leaked as referrers.

Organization SSO uses two explicit IdentityService resources. The identity-provider surface consists of GetIdentityProvider, reviewed PlanSetIdentityProvider/SetIdentityProvider, and reviewed PlanRemoveIdentityProvider/RemoveIdentityProvider. Reads report only whether a client secret is configured. A plaintext replacement is accepted at the request edge, sealed before plan persistence, and never returned.

Email-domain ownership uses ListOrganizationDomains, GetOrganizationDomain, and reviewed claim, verify, and release pairs. A new claim requires expectedResourceVersion: "absent"; subsequent operations use the exact returned resource version. Verification performs DNS resolution in both native and Worker deployments and commits only when the exact reviewed TXT challenge is present. Claim, audit, and plan completion are one atomic database transaction, including on MySQL.

#Topology and cache bytes

TopologyService/ExplainSurfaceRequest explains how one absolute HTTP request selects a live simultaneous route. TopologyService/ListObjectPresence returns physical evidence for one logical object across all placements. Placements are addressed by their stable surface-local names. Cache placement eviction uses the reviewed BinaryCacheService/PlanRunPlacementEviction/RunPlacementEviction pair and is separate from logical GC.

Cache producers call BinaryCacheService/CreateCacheObjectUploads with one canonical machine path and declared byte size, or parallel paths/sizes arrays containing at most 256 unique objects. The batch response preserves input order and exact retries return the same live tickets. A non-empty uploadUrl accepts the exact bytes with PUT; direct-origin URLs are capabilities and must not receive the Hub bearer, while /BinaryCacheService/UploadObject/... is a typed authenticated Hub proxy. An empty URL requires BeginCacheMultipartUpload, numbered PUT requests to the returned partUploadUrl, and CompleteCacheMultipartUpload; clients abort failed uploads with AbortCacheMultipartUpload.

#Scripting

The remote client covers common calls and provides stable JSON output:

aos --json hub registry list --hub https://hub.example.com
aos --json hub registry get acme/cdn --hub https://hub.example.com

Pass --token '<access-token>' to commands that require authentication. Use the schema when building a client or integration.