# Service endpoint callers

## Caller policy

`caller-grant` takes an owner granter with an interactive row who holds the approved instance, and a
grantee is a current exact owner, actor and lifecycle UID. A managed grantee also needs its actual
same-owner parent chain, with each current ancestor holding active covering grants for the same
service approval. The operation records those immutable grant ids and lifecycle UIDs in `parentRefs`
and rechecks them before append. An admin scope never substitutes for endpoint caller rights. Every
capability must name that instance with `instanceOnly: true`, carry no journal access and no `any` or
`ledger` target, and sit within the approval's caller ceiling. The answer is `granted` with the grant
as stored. Ordinary user-auth agent connects now derive their endpoint rows from this policy through
core's native grant builder, including reserved Describe for each authorized exact instance. An unpinned
`invokeService` or `invokeCommand` whose class publish the broker refuses is re-sent once on the rail of
the instance that answered its describe, so a granted caller reaches the service without naming the instance. The
callout rechecks grantee lifecycle, named ancestor grants, current name, assignment, root, activation,
approval and ceiling, then checks the policy evidence again after signing and native-key recording.
The original connection expiry is at most 60 seconds from issuance, and capped by root and approval
expiry. These are fresh leader reads,
not a linearized revoke fence. Existing issued connections keep their original expiry, and caller
revocation remains deny-new, not terminal absence proof.
One instance indexes at most 256 grants over its life, including attempts refused after indexing, and
a full index answers `resource-exhausted`; a new instance is the remedy. `caller-revoke` admits the
grant's source owner, its grantee owner and an operator. It answers `pending` with
`denyNew: "landed"` and `notBefore`, in epoch seconds, 71 s after the broker time of the grant's
cutoff marker. It never answers `revoked`, and the result parser refuses that arm.
`caller-policy-read` gives an owner its own grantees, and another owner's grantee only filtered to a
service it holds; `caller-policy-list` needs a service selector.

## Broker payload limits

Caller grants need `max_payload >= 2098176` (`SERVICE_CALLER_PAYLOAD_FLOOR`: 2 MiB plus 1 KiB),
`max_control_line >= 4096`, and an auth stream with `max_msg_size: -1`. Broker generation accepts an
explicit `serverConfig(broker, accounts, { ...options, maxPayload: SERVICE_CALLER_PAYLOAD_FLOOR })`;
omission leaves the native 1 MiB default unchanged. No startup path raises limits automatically. The
stock root sets it with [`cotal up --max-payload <bytes>`](/api-reference/cli/#up), which records the value in
`.cotal/broker-policy.json`; a renderer outside `up` reads it back with
`readBrokerPolicy(root)?.maxPayload` so every render of that broker carries the same limit.

## Payload observation

For a **standalone broker only**, pass `serviceEndpoints.callerPayload.observerCreds` to
`startAuthService` (or the corresponding authority-plane option). The callback returns fresh
host-private credentials from the broker owner's
`mintServiceCallerPayloadObserverCreds({ pub: broker.sys.pub, signingSeed: broker.sys.signingSeed })`.
Keep that registered SYS signer with the broker owner, never in the data account, a sandbox or a
participant client. The helper returns an in-memory, at-most-60-second VARZ-only credential.
The stock root does this itself: `cotal up` retains the system-account signing seed in the root's
secret store at space creation and at `--rotate-sys`, and the auth service started with
`--service-endpoints` composes `callerPayload` from it when the record names the trust record's
system account and the seed derives it; a root with no retained seed opens the engine and door
without caller issuance and logs why.
The callback only supplies credentials, owns no authority resource, and may finish after its
10-second observation deadline has refused the operation. It is not a cancellation callback.

Startup authenticates native VARZ, matches the observer and data connection's broker incarnation,
checks config/system-account evidence and live stream limits, then writes an exact-key assertion
bound to that broker, configuration and genuine plane claim. Configured clusters, gateways and
leaf links refuse, regardless of connected peer count. This is not multi-broker proof. A default
1 MiB broker fails startup with the required byte limit and creates no assertion.

## Process restart

Positive caller issuance repeats the observation and checks the exact assertion revision, complete
plane hold and effective authority again before native JWT release. Withdrawal or claim loss refuse
new issuance. A broker RELOAD, which keeps the same incarnation serving and moves its
`config_digest`, does not: when the fresh observation proves the same incarnation, the same auth
store, and every limit above again, the record is re-asserted under the held claim by CAS on the
exact prior row and issuance continues. A reload that breaks any of those, a `max_payload` below the
floor included, still refuses, and a withdrawn record is never overwritten. A RESTART is a new
incarnation and refuses. Reopening needs an explicit bootstrap to reassert for the new claim;
a prior-plane broker-bound assertion cannot authorize another append. Legacy assertion syntax still
parses for existing trusted storage fixtures but alone never enables production caller issuance.
Shutdown drains in-flight observations and closes their owned connections before releasing the claim.
Reads and revokes do not need a positive assertion. A revoke whose policy row would not fit holds
`payload-precondition` without writing it, and a failed live-limit check withdraws the assertion.

## Unknown outcomes

Give each caller operation a fresh random requestId, record it with the exact body before you send
it, and replay that body after a lost reply, a 503 or a `held` answer. Never switch to a new requestId
while an earlier answer is unknown. A settled `refused` or `expired` ends the attempt, and a grant
replayed after its 10-minute window adopts a grant that landed and otherwise answers `expired`.

Caller issuance is generic, not tied to Linear: the same provider-independent contract discovery and
invoke path works for ordinary and managed user-auth callers. Controlled policy append currently
requires the application's approved service-host instance. Native control endpoints retain their
existing ledger/scope and typed authority contracts, not fabricated application approvals. Scoped
Describe is not available, and complete terminal retirement and restore remain unsupported. A host revoke refuses new activation, renewal, readiness
and connections, but a connection already open keeps its original expiry of at most 60 s. Approval
runs service bootstrap and revoke runs cancellation, while complete observer evidence and writer
exclusion remain unavailable. Do not seed service reservations in an existing space, adopt old
service-shaped rows, or use ordinary retirement to make them terminal. A signing-key or raw-store
holder can bypass helper guards, so schema rejection alone does not make mixed-version writers safe.
Restore must quarantine service work until a separately supported reconciliation protocol is
available.
