Docs

authorize

Source: crates/lvcs-doc/src/kinds/caps/authorize.rs.

/// One use of a capability to check: `signer`, holding the grant `grant`
/// (`GrantId::OWNER` for the owner, who needs none), does `need` on the
/// document at `address` at `time_s`, a change's Automerge time in Unix
/// seconds. For a change, `signer` and `grant` are its `ChangeSig`'s and
/// `address` its `doc`. Owner: `M3.grants`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Attempt<'a> {
    pub signer: &'a ParticipantKey,
    pub grant: &'a GrantId,
    pub address: &'a str,
    pub time_s: i64,
    pub need: &'a Capability,
}

/// What [`authorize`] found (decision 146): the signer's trust tier
/// (design §15; `owner` for the owner, `participant` under any grant), the
/// grant's change quota (`None` for the owner and for a grant whose terms
/// name none), which the document actor counts by `seq` among the actor's
/// changes under the grant (decision 70) and which makes the grant's own
/// actor id the change's (`AuthorId::granted`, decision 125), and the
/// grant's rate terms. Owner: `M3.grants`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Authorized {
    pub tier: TrustTier,
    pub quota: Option<u64>,
    pub rates: GrantRates,
}

/// A grant's rate terms (`GrantTerms`, decision 146): each one present is
/// the grant's value for the grantee's signing or gossip bucket in place
/// of its `[limits]` default, and the daemon meters the grantee at the
/// largest over its live grants (decision 178); all absent for the owner,
/// who holds no grant, and for a grant whose terms name none. Owner:
/// `M3.grants`.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct GrantRates {
    pub sign_per_sec: Option<u32>,
    pub sign_burst: Option<u32>,
    pub gossip_per_sec: Option<u32>,
}

/// Whether the grants in `known` authorize `attempt`: the owner (the
/// signer is `known.root`'s owner and names `GrantId::OWNER`) may do
/// anything; any other signer needs `attempt.grant` in `known.grants`,
/// granted to it, with a window containing `time_s`, naming `need`, with a
/// path pattern matching `address`, and issued by the owner or by the
/// grantee of a chain link: a grant that names [`Capability::Delegate`]
/// and itself holds at `time_s` for `need` and `address`, issued by the
/// owner or another link. Every link must cover the attempt, so a
/// delegated grant is never wider than its issuer's (decision 109). No
/// rule reads a grant's terms (decisions 146 and 147): every window is
/// `not_before_s..not_after_s` as written, any grant may name any
/// capability on any paths, and any grant naming `Delegate` is a link. A
/// grant `known.revoked` holds at `t` (decision 148) holds only before
/// `t`: its window ends at `min(not_after_s, t)`, for the attempt and for
/// every chain link, so a revoked `Delegate` link counts only for an
/// attempt before `t`.
/// Path patterns are gitignore syntax, matched against `address` and the
/// directories above it; a `!` pattern excludes what it matches wherever it sorts; an
/// `address` with a segment that is empty, `.`, or `..`, and a list with a
/// pattern that doesn't parse or a `!` line whose rest starts with `#` or
/// `!` (stripped of its `!`, it would be a comment or another `!` line,
/// and exclude nothing), match nothing. For a control document,
/// `address` is what the write concerns (decisions 108, 109, 111, 112,
/// and 147; the document verifier maps each change to its attempts): for an override
/// change, the file path its claim resolves to in the workspace
/// (`OverrideClaim::path`, decision 111), never its base, which for a
/// stacked claim is the lower override's address; for an index entry, the
/// file path its key names (a whiteout's key, or the `path` of the claim an
/// override or workspace-local entry's key, its override id, commits to;
/// decision 112), never what its value claims; and each path a merge
/// record lists, one attempt each;
/// a change to a thread document (`THREAD_ADDRESS`), with
/// `need` `Write` or `Message`, needs any valid grant: its chain and
/// windows are checked, its capabilities and paths are not. So does
/// `Write` on an issue document (`ISSUE_ADDRESS`, decision 304), whose
/// assign grant is `Write` on `<issue address>/assign`, a path check. So does
/// `Write` on the workspace registry (`REGISTRY_ADDRESS`) or on a
/// workspace index (`WORKSPACE_INDEX_ADDRESS`, one name in its slot;
/// decisions 111 and 112), which the document verifier names only for a
/// registry workspace entry and an index init (`/name`, `/base`,
/// `/subtree`, `/created_ms`, `/base_frontier`), each written alone and
/// accepted only where `WorkspaceId::of` its own fields and its signer
/// names it, so a grant lets its holder create workspaces of its own and
/// never write another participant's; an index entry's attempt is `Write`
/// on the file path its key names. So does `Write` on `ALLOWLIST_ADDRESS`
/// (decision 147), which no document has and the document verifier names
/// only for a caps change adding `/allowlist` entries, so any valid grant
/// may add one. A change to the caps document
/// (`CAPS_ADDRESS`, `need` `Write`) needs a grant that names `Delegate`
/// (decision 109): its chain and windows are checked, its paths
/// are not; any other `need` on that address is checked like a path, as
/// is any other `need` on the registry's, an index's, or
/// `ALLOWLIST_ADDRESS`'s own address. The
/// thread, registry, index, allowlist, and caps rules read `address` alone, so the
/// caller names one of those addresses only for a change to that
/// document (for `ALLOWLIST_ADDRESS`, a caps change's allowlist adds),
/// never as the path an entry, claim, or merge record of a
/// change holds. On a channel document's address (`CHANNEL_PREFIX` and a
/// name `ChannelName::parse` takes), `Attest` naming that channel and
/// `Approve` are decided by the address alone, like the thread and caps
/// rules (decision 119): the chain, windows, and capability are checked,
/// its paths are not; any other
/// `need` there is checked like a path. `Approve` on a file address stays a
/// path check. That a caps change only adds entries (a `/grants/<id>`
/// entry whose issuer is the signer, an `/allowlist/<hash>` entry), never
/// deletes or overwrites one, and that only the
/// project's first change writes `/root`, is the caller's merge-time check
/// (`M3.doc`), not this one's.
/// Refusals, in
/// this order: `NoCapability` (`known.root` is unknown, a signer other
/// than the owner names `GrantId::OWNER`, the grant is unknown or another
/// signer's), `GrantExpired` (its window doesn't contain `time_s`, or it
/// was revoked at or before `time_s`),
/// `OutOfScope` (it doesn't name `need` or match `address`), and
/// `NoCapability` (no chain of links reaches the owner, as when the
/// issuer's own grant is narrower than the attempt). A chain link's path patterns are compiled
/// only once its issuer is reached, so grants that don't reach the owner
/// cost no matching.
pub fn authorize(known: &GrantsKnown, attempt: &Attempt<'_>) -> Result<Authorized, SyncRejection> {