api-grants
Source: crates/lvcs-api/src/grants.rs.
/// A grant's terms (design §15, decision 146), as the `caps` region's
/// `GrantTerms`, each optional and recorded as given: `harness` and
/// `model`, what the grantee runs, for attribution only, each at most
/// `GRANT_TERM_MAX_BYTES` (decision 178); `quota`, its
/// change quota per document, enforced only when set; and `sign_per_sec`,
/// `sign_burst`, and `gossip_per_sec`, this grant's values for the
/// grantee's buckets, the `[limits]` default when unset, the grantee being
/// metered at the largest over its live grants (decision 178). An issuer
/// may set any value. `alias` names the grantee (decision 243), for
/// display and addressing only: at most `ALIAS_MAX_BYTES` of lowercase
/// ASCII letters, digits, and `-`, starting with a letter; only the project
/// owner gives one, so any other issuer's is refused `REJECTED`
/// `out_of_scope` with an ask to the owner in `next` (decision 247); and
/// it is refused `INVALID_PARAMS`, with a free alias in `next`, when
/// another participant was ever granted it; it stays the grantee's for the
/// life of the project, revoked or not. `device` marks a device
/// certificate (decision 300).
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct GrantTermsParam {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub harness: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub quota: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sign_per_sec: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sign_burst: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gossip_per_sec: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub alias: Option<String>,
/// A device certificate (decision 300), which `lvcs auth login` issues.
#[serde(default, skip_serializing_if = "core::ops::Not::not")]
pub device: bool,
}
/// One capability (design §11), as the `caps` region's `Capability`: a
/// JSON string `write`, `message`, `approve`, `delegate`, or `read`
/// (decision 300), or `{ "attest": <channel> }`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum CapabilityParam {
Write,
Message,
Attest(String),
Approve,
Delegate,
Read,
}
/// The grantee a `grant` names to grant anyone (decision 300): only a
/// `read` grant to it means anything, letting an unauthenticated client
/// of the host peer read the paths it covers (a public project).
pub const ANYONE: &str = "anyone";
/// `grant` (design §11, §15): the calling participant signs a grant to
/// `grantee` (`ID_ENCODING`) for `capabilities` on `paths` (gitignore
/// syntax), on `terms` (none when omitted), starting now (the request's
/// time, in seconds). The grant's window ends `ttl_secs` after it starts,
/// and it has no expiry when `ttl_secs` is omitted; a `ttl_secs` below
/// `GRANT_TTL_MIN_SECS` is refused `INVALID_PARAMS`. A grant with a window
/// is renewed by its issuer's daemon, unattended, once half its window has
/// passed: a new grant with the same terms and a window of the same length
/// starting then, until it is revoked (`revoke`, decision 148). Refused `REJECTED` only when
/// the caller is neither the owner nor a holder of a live `delegate` grant
/// covering each capability the request names on each of its paths
/// (decisions 109 and 146): no rule reads the terms, and any grant may
/// name any capability on any paths. Refused `INVALID_PARAMS` when
/// `harness` or `model` is longer than `GRANT_TERM_MAX_BYTES`, which every
/// peer refuses in a grant (decision 178), and `REJECTED` when the caps
/// change would pass the change quota of the grant the caller issues it
/// under.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct GrantParams {
pub grantee: String,
pub paths: Vec<String>,
pub capabilities: Vec<CapabilityParam>,
#[serde(default)]
pub terms: GrantTermsParam,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ttl_secs: Option<u64>,
}
/// `grant` result: the grant's id (`ID_ENCODING`) and its window in Unix
/// seconds (`not_after_s` omitted when it has no expiry).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GrantResult {
pub grant: String,
pub not_before_s: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub not_after_s: Option<i64>,
}
/// `revoke` (decision 148): revoke the grant `grant` (its id, in
/// `ID_ENCODING`, the start of one, or a participant's id, alias, or id
/// prefix naming its one live grant, resolved in the daemon, decision
/// 325; several are refused with the candidates in `next`) and every unrevoked grant of its lineage, the grants
/// with the same issuer, grantee, paths, capabilities, terms, and window
/// length (its renewals, since a renewal leaves its predecessor live for
/// half a window), in one signed caps change: each revocation is
/// `/revoked/<grant id>` holding the change's own time `t`, the start of
/// the next whole second, after which no change at or past `t` is within
/// that grant; a daemon signs nothing more under a grant once its
/// revocation renders, and a revoked grant is never renewed. Refused
/// `INVALID_PARAMS` when `grant` names no grant the
/// caps document holds, and `REJECTED`, with nothing committed, unless
/// the caller is the owner, or holds a live `delegate` grant (the one a
/// caps change is signed under) and is the grant's issuer or that grant
/// also covers `delegate` on each of the grant's paths, as every peer's
/// verifier requires, or when the change would pass that grant's change
/// quota (decision 178).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct RevokeParams {
pub grant: String,
}
/// `revoke` result: the grants it revoked, in `ID_ENCODING`, sorted as
/// strings; empty when the grant and every grant of its lineage were
/// revoked already. `seen_by` and `not_seen_by` split the project's known
/// peers (decision 326) by whether they hold every one of those
/// revocations, sorted by peer id. A peer keeps honouring a grant until it
/// holds its revocation, so `not_seen_by` names who still does. Just
/// after the change commits no peer can hold it yet, so every known peer
/// is in `not_seen_by`; `status` follows the revocation until each has it.
/// Both are empty when nothing was revoked.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RevokeResult {
pub revoked: Vec<String>,
#[serde(default)]
pub seen_by: Vec<RevocationPeer>,
#[serde(default)]
pub not_seen_by: Vec<RevocationPeer>,
}
/// A peer of the project this daemon knows (decision 326): one whose caps
/// document synced with this daemon's since it started, at most
/// `REACH_PEERS_MAX`. `peer` is its libp2p peer id, `host` the machine
/// name its inventory gave, `online` whether it syncs now, and
/// `last_seen_ms` when it last did (joined, left, or sent a caps message),
/// in Unix milliseconds by this daemon's clock.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RevocationPeer {
pub peer: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
#[serde(default)]
pub online: bool,
pub last_seen_ms: i64,
}
/// `secret.confirm` (design §15, decision 147): commit a held edit.
///
/// Commit the caller's edit the secret scan held (`held`, from the
/// error's `secret`) and record a signed
/// allowlist entry for each flagged string's hash. Refused `REJECTED`
/// unless the caller is the participant whose edit was held and is the
/// owner or holds a live grant, which lets it add allowlist entries
/// (`Write` on `ALLOWLIST_ADDRESS`), whatever its paths and capabilities,
/// and when an allowlist entry, or the edit signed under a grant other
/// than the one it was held under, would pass the change quota of the
/// grant it is signed under (decision 178). A refused confirm leaves the
/// edit held.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SecretConfirmParams {
pub held: String,
}
/// `secret.confirm` result: as for `edit`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SecretConfirmResult {
pub heads: Vec<String>,
}