The Host API¶
The portable surface. Everything else in this documentation — the CLI, the
SDK, /v1, MCP, ACP — is Barista Cloud's own. The Host API is not: it is an
open contract that Barista Cloud implements, alongside a local single-user
provider and anyone else who wants to.
An app written against it runs unchanged on a laptop and here, without depending on either implementation's private API. That is the point, and it is why this surface is worth knowing about even if you only ever use Cloud.
- Contract:
barista-apps—contracts/host-api/v1alpha1/openapi.yaml - Base path here:
/v1alpha1(versus/v1, which is Cloud's own REST API)
When to use it instead of /v1¶
| Use | If you want |
|---|---|
/v1 (and the CLI/SDK on top of it) |
the shortest path to working sessions on Cloud, plus Cloud-only features: templates, published sessions, billing, GitHub missions |
/v1alpha1 (Host API) |
an app that must also run against a local host or another provider, or that needs app manifests and delegated grants |
They are not layered over each other in the way you might expect: both call the
same domain functions internally, so their lifecycle and error semantics cannot
drift, but the shapes differ and /v1alpha1 is the one with a published
specification you can hold us to.
Apps and manifests¶
The Host API's unit of deployment is an app: a manifest naming an OCI image by digest, an entrypoint, the capabilities it requires or would like, and the permissions it wants — including what authority its child sessions receive.
Sessions are then instances of an installed app:
Install is where a manifest is refused. A manifest that requires a capability this provider does not advertise, or that grants its children authority it does not itself hold, is rejected before the app record exists — never accepted and then quietly under-delivered.
Capability discovery¶
GET /v1alpha1/discovery reports what this provider will actually honour for
your account:
{
"contract_versions": ["v1alpha1"],
"provider": {"name": "barista-cloud", "version": "v1alpha1"},
"core_profile": true,
"capabilities": ["grants.delegated", "session.pause_resume"],
"limits": {"max_concurrent_sessions": 1000, "max_session_mem_gib": 2048},
"extensions": {}
}
An app should check this and adapt, rather than assume. A capability appears only where five things agree: the upstream contract defines it, this deployment enables it, your plan includes it, the measured fleet supports it, and — the one that does the most work — an upstream conformance run has proven it here.
That last gate is why the list is shorter than the contract's. Barista Cloud
does not advertise a profile it has not demonstrated under the open suite, even
where everything else would allow it. session.snapshot.exact and
story.publish are withheld today not because they do not work, but because
the pinned suite has no cases that would prove they do — and by the suite's own
rule, an absent case cannot certify an advertised profile.
Currently certified here (barista-conformance 0.1.0a1, 2026-08-27,
passed=23 failed=0 skipped=0): core, session.pause_resume,
grants.delegated.
extensions is empty, and its emptiness is deliberate: an operation an app
needs portably belongs in the contract, not behind a vendor key only one
provider answers to.
Delegated grants¶
The capability worth understanding, because it changes what an app can be.
A grant (bg_…) is scoped authority: one session, a closed set of actions,
an expiry. It carries no tenant membership, so it can do exactly what it names
and nothing else. That is the opposite of an API key, which is ambient authority
over everything you own.
When an app's manifest declares a grant:// secret reference, the provider
mints a grant for each session and resolves it into that session's environment.
The app never handles a long-lived credential; it reads the one it was given:
An app can also declare what its children receive — a coordinator that creates workers can hand each worker a strictly narrower grant than it holds itself. Authority cannot be laundered downward: a manifest granting its children something it does not declare for itself is refused at install.
Refresh¶
Delegated grants are short-lived here (15 minutes; a tenant-issued grant may ask for up to 30 days). A long mission outlives that, so it refreshes:
The replacement carries the same authority — every field of its scope is read off the stored row, so there is nothing to pass and nothing that could widen the result. The presented secret stops working immediately.
Refresh is not retried and takes no idempotency key. A replayable rotation would mean keeping a second live copy of a credential, so a blind retry after a lost response would rotate again from a secret that no longer works. Refresh with enough margin to report the failure and be re-provisioned.
Advertising grants.delegated is a promise that refresh works, not that the
row type exists — upstream conformance fails a provider that advertises the
profile and answers 501.
Errors¶
Every refusal is the same envelope, and nothing else:
class is the contract and the thing to branch on — one of authentication,
authorization, capability, compatibility, conflict, quota,
unavailable, terminal, invalid_request. code narrows it, message is
prose for a human and may change.
The distinction that matters most in practice: unavailable is retryable and
terminal is not. A session that is still starting answers unavailable /
session.not_ready — come back — while a session that does not exist answers
terminal / not_found, and no amount of retrying will help.
Related¶
- API keys and surfaces — how this sits beside Cloud's own surfaces
- Sessions — the lifecycle underneath both APIs
barista-apps— the contract, the conformance suite, the local provider, the SDK, and reference apps