/v1/teams are declared, addressable and refused. Which
ones depends on the tenant’s runtime, so this page describes the contract and
sends you to Durable Runtime API for the
current membership and counts.
The lists live on one page, and it is not this one. This page used to carry
its own tables of “the refused reads (5)” and “the refused writes (12)”. Those
numbers were the measurement of a build in which every tenant was a hermes tenant,
and they went stale the moment a durable tenant could be served operations a
hermes tenant is refused — including six that this page still listed as refused.They are not restated here.
naive-docs/api-reference/runtime/overview.mdx carries
the split, and ci/runtime-surface-counts.test.ts derives its numbers from the
router and fails the build when the prose disagrees. A count that no gate reads is
a count that drifts, which is exactly what happened here.The contract
Every refusal on this surface has the same shape:501
missingis a list, not a string. Most of these have more than one prerequisite. A caller that fixes one and retries is told about the second one now rather than after another round trip.- The request is authorised before it is refused. A bad credential still gets
401; a credential for the wrong tenant still gets the tenant error. The501is not an oracle for whether a resource exists. details.runtimenames which runtime the refusal is about. The same operation refuses for different reasons on the two runtimes, and a refusal that did not say which one was wrong in both directions: it told a durable tenant its dispatcher did not exist, and told a hermes tenant about a manifest store hermes has never had.
The three refusals, and they are different facts
The second row is the one worth reading carefully.
error.details.missing names
which verb the runtime already implements, so “this runtime cannot do that” and
“this address cannot reach it” are distinguishable. Where the refusal came from the
runtime itself, its own sentence is carried through verbatim in
error.details.runtime_said rather than being translated.
🔴 The exception: the reads that are empty without refusing
Why writes are not quietly forwarded
The obvious shortcut for a write a tenant’s runtime cannot serve is to forward it to the other one. It is not taken, for two reasons:- The legacy runtime is frozen. A second write path into a frozen runtime is how a frozen runtime stops being frozen.
- The response would lie.
POST …/submitreturnsmanifest_digestandrequired_attestations. The legacy runtime has neither. A200carrying a fabricated digest is worse than a501, because the digest travels.
Checking programmatically
error.details.missing and error.details.runtime are machine-readable and stable
enough to log. Do not parse error.message.
A
501 is a statement about one tenant, not about the API. The same request
against a tenant on the other runtime may be served. That was not true when this
page was first written, which is why the check above no longer reads a 501 as
“not wired in this build”.