Skip to main content

Module decision

Module decision 

Source
Expand description

Decision records: what a pushed authorization request becomes, and what an agent answers.

plan/oauth.md’s hard part is who consents. The answer this module carries is that the pushed request itself is the question: at PAR, once the client is known and login_hint has been resolved to an account, this server works out what it would grant and writes that down as a DecisionRecord the named account can read, approve or decline over bot.did.*. Nothing about it needs a browser, and nothing about it forbids one — GET /oauth/authorize renders the same record for a person.

§What the record says, and what it keeps to itself

DecisionRecord::to_json is the whole wire shape. The client’s own copy of its name, its home page and its logo is kept (ClientSelf) and never serialized: plan/app-allowlist.md’s “record the client’s own copy; do not show it”, because showing an untrusted client’s self-description is how impersonation works. What an agent sees about the client is the origin it served its metadata from, the content key of that metadata, and whether this account has approved that key before.

§Narrowing is never silent

A Verdict is one of three answers, and two of them say something was taken away. Verdict::Narrow names the grant and the atoms the ceiling did not give in full. See crate::oauth::scope::ScopeSet::narrow.

An approval covers what the verdict granted, and that is what the login is issued at. A token may do that set as the ceiling in force grants it (crate::oauth::token::in_force), so a ceiling tightened after the approval takes a granted atom back. A cut atom stays cut: widening the ceiling cannot add one to a login the agent approved without it.

§Nothing here is durable

A record lives as long as the pushed request it was minted for (crate::oauth::par::REQUEST_URI_TTL), answered or not, and is swept when it expires: GET /oauth/authorize answers from it until then. An approval whose code a browser carries holds its redirect here too, until the page hands it to a browser signed in as the account (DecisionStore::collect). MemoryDecisionStore is bounded twice over — per account and in total — because PAR is unauthenticated, so an unbounded queue of records would be a way to spend this server’s memory without holding any credential.

§A remembered approval

The account’s approval of a client is remembered, per account and ClientKey, at the grant it approved, for REMEMBERED_TTL (DecisionStore::remember). GET /oauth/authorize reads it to confirm a later sign-in without asking; see crate::oauth::authorize::confirms_unasked. A confirmation does not write one, so it never renews itself. It lives in memory, so a restart forgets every one. See plan/remembered-consent.md.

Structs§

ClientSelf
The client’s own account of itself, kept for the audit trail and never serialized to any caller.
Cursor
A place in one store’s arrival order, and which store’s order it is.
DecisionRecord
One authorization request, as this server decided about it.
Displaced
The record one push displaced to make room for its own, and what must be undone for it.
Inner 🔒
MemoryDecisionStore
The in-process decision store.
Remembered
The account’s latest approval of one client, remembered.
Stored
What DecisionStore::put stored, and what it displaced to store it.

Enums§

DecisionRefused
Why a record could not be stored.
DecisionState
Where one decision record is in its life.
Verdict
What this server decided about one authorization request.

Constants§

CEILING_RULE
The rule a ceiling narrowing or refusal is attributed to.
DEFAULT_PENDING_PER_ACCOUNT
How many decisions one account may have awaiting an answer.
DEFAULT_PENDING_TOTAL
How many decisions this server may hold in total. An answered one counts until its pushed request expires.
EPOCH_BYTES 🔒
How many random bytes name one store’s epoch; see Cursor.
REMEMBERED_TTL
How long the account’s approval of a client is remembered: as long as the login it approved can last.
SEEN_CLIENTS_MAX 🔒
How many (account, client key) approvals MemoryDecisionStore remembers for DecisionRecord::first_time.

Traits§

DecisionStore
Where decision records live between PAR and an answer.

Functions§

atoms 🔒
A scope set as the array of atoms the wire carries.
hard_blocked 🔒
Whether this atom is one this server never grants, whatever a ceiling says: plan/scope-policy.md’s hard-blocked capabilities, and any atom the grammar cannot read. verdict_for drops each from the request whole, so the rest can be granted and the agent sees it in cut.
verdict_for
The verdict a ceiling alone gives for one request.