Skip to content

Pre-release. v0.1 is not out yet, so there is nothing to install and no public source to clone — the quickstart builds from a checkout.

Tasks and task grants

A task is one piece of work an agent is doing for one person, against one audience. It is created by the first exchange and it has an expiry of its own, separate from any token.

A task grant is what the agent gets back to keep that task alive. It arrives in the refresh_token field, because that is the field OAuth clients already know how to use, but it behaves differently in every way that matters.

An agent registration may carry two limits:

{
"max_task_ttl": "PT30M",
"max_token_ttl": "PT5M"
}

max_task_ttl bounds the whole piece of work. max_token_ttl bounds each individual credential handed out along the way. Both are optional: a registration that names neither is given the server’s Onbe:Tokens:DefaultTaskTtl (thirty minutes) and DefaultTokenTtl (five minutes), and every registration is held inside Onbe:Agents:MinTaskTtlMaxTokenTtl.

This split is the reason long-running work is safe. A job that takes half an hour does not hold a half-hour credential; it holds a five-minute one, renewed as it goes. If that credential leaks — copied out of a log, scraped from a crash dump, read from a process list — the window in which it is worth anything is five minutes, not thirty.

The cap is the task’s remaining life, in both directions. A token is issued for the shorter of max_token_ttl and whatever is left of the task, so the last token of a 22-minute task is issued for two minutes, not five. A token never outlives its task; the conformance suite checks this at issue and at refresh. A task with less than five seconds left is refused a token rather than handed a dead one.

The grant is not a general-purpose credential. It is bound to the task, and the server checks, in order, on every refresh:

  1. The grant exists.
  2. The task has not been revoked.
  3. The task has not expired.
  4. The task has more than five seconds left — less is access_denied with reason task_ending, and a client that sees it should stop, not retry.
  5. The sponsor is still active at your identity provider.
  6. The requested scope is a subset of what the task was already granted.
  7. The agent’s current registration still allows all of it.

Then it issues a fresh token with the same task_id and a new jti.

So a grant cannot widen scope — a refresh asking for something the task was not granted is invalid_scope, even when the user and the agent would both allow it. It cannot change audience: another resource is invalid_target. And it belongs to one agent — the refresh authenticates the agent exactly as the exchange did, so a grant presented by anybody else does not exist.

Step 5 is the one that surprises people. On every refresh the control plane asks your identity provider whether the human is still active, and caches the answer for no longer than the lifetime of the token it is about to issue.

That means a user disabled or deleted at the identity provider has every task fail its next refresh within one expires_in. Off-boarding somebody stops the agents working on their behalf, without anybody having to remember that agents existed.

A disabled or deleted user is access_denied. An identity provider that cannot answer is temporarily_unavailable — never treated as active, because “we could not check” is not the same as “yes”.

Renew at about sixty per cent of expires_in, not when a call comes back 401. By the time you see a 401 you have already failed a request that mattered.

@onbe/client does this for you and never hands out a token that is about to expire. Doing it by hand is the single most common source of mid-task failures.

Three ways: it expires, it is revoked, or the agent finishes and stops asking. Expiry is handled by a sweeper that marks the task terminal and revokes its grants, writing one task.expired record. Expiry is not a revocation, and is never written as one — the distinction matters when you are reading the ledger back and trying to work out whether something was stopped or simply finished.

OnbePre-release. v0.1 is not out yet.

© 2026 Onbe