Docs

api-status

Source: crates/lvcs-api/src/status.rs.

/// `status` (decision 164): where the caller stands. Read-only, and never
/// retried.
///
/// It shows the caller only what is its own (its grants, notifications,
/// mirrors, stale saves, and errors) and a peer count. `workspace` (`main`,
/// or a forked workspace's name or id) limits `mirrors` and `stale_saves`
/// to that workspace's, and is refused as `read` refuses a workspace this
/// daemon doesn't run. `path` asks how this daemon classifies one path
/// now (row 180), answered as `path`; it is refused when it is no valid
/// address.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct StatusParams {
    /// The one workspace whose mirror and stale saves to show (every one
    /// when omitted).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
    /// The one path to classify, as `lvcs explain <path>` asks (row 180).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub path: Option<String>,
}

/// `status` result (decision 164).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusResult {
    /// The caller, in `ID_ENCODING`.
    pub participant: String,
    /// The caller's `trust_tier` value now, as other participants' daemons
    /// compute it from its grants: `owner`, or `participant` (also when it
    /// holds no live grant).
    pub tier: String,
    /// The harness its grant names, if any.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub harness: Option<String>,
    /// The model its grant names, if any.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    /// The mirrored directories this daemon runs that the caller authors
    /// (decision 137 B): `main`'s for the daemon's default participant, a
    /// forked workspace's for the participant that forked it.
    #[serde(default)]
    pub mirrors: Vec<StatusMirror>,
    /// The caller's live grants, newest first: those whose window holds
    /// now and whose revocation the caps document doesn't render, which
    /// the daemon signs under.
    #[serde(default)]
    pub grants: Vec<StatusGrant>,
    /// The caller's grants that ended in the last `RETRY_WINDOW_SECS`,
    /// latest end first: by their window, or once their revocation renders
    /// (decision 148).
    #[serde(default)]
    pub ended: Vec<StatusEnded>,
    /// The caller's open asks and holds, as `notifications` lists them.
    #[serde(default)]
    pub open: Vec<Notification>,
    /// How many of the caller's other notifications wait unread, with no
    /// filter applied.
    #[serde(default)]
    pub pending: u32,
    /// The stale saves of the mirrors the caller authors since each file's
    /// last clean save, oldest first.
    #[serde(default)]
    pub stale_saves: Vec<StatusStale>,
    /// The paths the mirrors the caller authors sync but never write to
    /// disk, since this platform's filesystem can't hold them (Windows,
    /// decision 252), in mirror then address order; empty elsewhere.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub unmaterialized: Vec<StatusUnmaterialized>,
    /// The paths the mirrors the caller authors stopped writing because
    /// another program held them open past `HELD_DEADLINE_MS` (Windows,
    /// the held-rename row refining decision 252), until a later write of
    /// each lands, in mirror then address order; empty elsewhere.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub held: Vec<StatusHeld>,
    /// How many peers this daemon syncs a document with now.
    #[serde(default)]
    pub peers: u32,
    /// The project is hosted with no mDNS (decision 307): one `mdns: off`
    /// line.
    #[serde(default, skip_serializing_if = "core::ops::Not::not")]
    pub no_mdns: bool,
    /// The peers this daemon found running a release it can't sync with
    /// in this run (production-readiness U4), by peer id, until one
    /// identifies as compatible again.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub incompatible: Vec<StatusIncompatible>,
    /// Each revocation some known peer doesn't hold yet (decision 326),
    /// newest first, at most `STATUS_REVOCATIONS_MAX`; a revocation leaves
    /// once every known peer holds it.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub revocations: Vec<StatusRevocation>,
    /// The last error the daemon answered the caller, on any connection,
    /// since the daemon started, until a later request of the same op
    /// succeeds, or, for a refused save (no op), a later save of the
    /// caller's is admitted; a restart forgets it.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub last_error: Option<LastError>,
    /// The caller's open failing streaks (decision 193): the latest
    /// `action_failed` it received for each check no `action_succeeded` has
    /// ended since, newest first; a restart forgets them.
    #[serde(default)]
    pub checks: Vec<Notification>,
    /// On a change runner's daemon, the change actions failing here now
    /// (decision 193), each with the authors its runner told; empty when
    /// this daemon serves no change channel.
    #[serde(default)]
    pub red: Vec<StatusRed>,
    /// The channels this daemon's runner attests, comma-separated, with
    /// why it runs nothing when the sandbox can't run here (decisions 245
    /// and 297); `None` when its participant's grants name no channel it
    /// can run.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub runner: Option<String>,
    /// Every protected path main changed past its approved heads, waiting
    /// for an approval (decision 314): what to run to approve it, or why it
    /// can't be approved yet.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub waiting: Vec<StatusWaiting>,
    /// Every action's channel and every `[channels]` combination at
    /// `lvcs.toml`'s approved heads, by name, with its value (decision
    /// 297): one `channel <name>: <frontier ids or none>` line each.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub channels: Vec<StatusChannel>,
    /// While the runner serves channels, one line when its sandbox's runs
    /// have the rlimits only, with no cgroup memory, pids, or cpu limits,
    /// since the daemon has no writable delegated cgroup v2 subtree (the
    /// sandbox-hardening row), then one when its steps have no `/proc` of
    /// their own, since the kernel refused the sandbox's `proc` mount (row
    /// 307): each why, with how to recover and the next action.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub sandbox: Vec<String>,
    /// One line per GitHub workflow file at main's approved heads
    /// (decision 245, stage C): `actions <file>: <n> mapped, <n> skipped,
    /// <n> ignored`, or `actions <file>: refused <reason> <name>: <next>`
    /// for each job it refused, which holds every change channel.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub actions: Vec<String>,
    /// At most `DERIVED_STATUS_MAX` of the derived files the caller's
    /// mirrors hold that are stale or failed, or that were superseded for
    /// the caller in the last `RETRY_WINDOW_SECS`, stale and failed first,
    /// then by address (row 180).
    pub derived: Vec<DerivedView>,
    /// How many more such files `derived` leaves out.
    #[serde(default)]
    pub derived_omitted: u32,
    /// The path a `status { path }` asked about, as this daemon classifies
    /// it now (row 180); absent when none was asked.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub path: Option<DerivedView>,
    /// How to name each participant the result names, the caller
    /// included (decision 243), by id.
    #[serde(default)]
    pub names: BTreeMap<String, Named>,
    /// The aliases a grant gave the caller that were first granted to
    /// another participant, so they name that one, not the caller
    /// (decision 243).
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub alias_conflicts: Vec<String>,
    /// Whether this daemon applies a signed release itself (decision 293):
    /// its own setting, under its own `LVCS_NO_AUTO_UPDATE`, not the
    /// caller's.
    #[serde(default)]
    pub auto_update: bool,
}

/// One path as row 179's classifier sees it, with where its derived output
/// stands (row 180), as `status` and `lvcs explain <path>` show it.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DerivedView {
    /// The path.
    pub address: String,
    /// Whether it is derived.
    pub derived: bool,
    /// The `derived_rule` value of the rule that decided: `none` when no
    /// rule applies, and for a path that is never derived.
    pub rule: String,
    /// What matched, cut to `RANGE_CONTEXT_MAX_CHARS` code points: the
    /// `.gitattributes` line as `<file>:<line>: <text>`, the `lvcs.toml`
    /// pattern, the vocabulary pattern, or the marker; empty for `none`
    /// with no pattern.
    pub detail: String,
    /// the generator's command at main's approved heads, for a derived
    /// path of a project that has one: its argv when it is one `Exec`
    /// step, else one word describing its steps (decision 248).
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub generator: Option<Vec<String>>,
    /// Where its output stands under the generator; absent without one.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub state: Option<DerivedState>,
    /// For a `stale` path, how far its shown output is behind its inputs.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub behind: Option<Behind>,
    /// For a `failed` path, why its derivation failed and how to recover.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub error: Option<ErrorData>,
}

/// Where a derived path's output stands under the generator (row 180).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DerivedState {
    /// A counted generation covers its inputs' newest change.
    Current,
    /// Its inputs changed after the shown generation, or its new output
    /// waits for approval.
    Stale,
    /// Its derivation for the newest inputs failed or diverged.
    Failed,
}

/// How far a stale derived path's shown output is behind its inputs (row
/// 180).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Behind {
    /// The input documents whose heads moved past the shown generation's
    /// frontier.
    #[serde(default)]
    pub inputs: u32,
    /// When the first of them moved, in Unix milliseconds by the daemon's
    /// clock.
    pub since_ms: i64,
}

/// One channel's value, as `status` lists it (decision 297).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusChannel {
    /// The channel's or combination's name.
    pub name: String,
    /// Its value's frontier ids, in `ID_ENCODING`, sorted; empty for none.
    pub frontiers: Vec<String>,
    /// For an action's channel, why it can't move (decision 321): no
    /// runner can attest it, or none who may is online; `None` when a
    /// runner is online for it, and for a combination.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub blocked: Option<ErrorData>,
}

/// A change action failing on this runner's daemon (decision 193).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusRed {
    /// The check's id, in `ID_ENCODING`.
    pub check: String,
    /// Why it is red: a `red_kind` value (decision 230). `timed_out` with
    /// no one told is a first timeout, which reruns at the next quiet;
    /// with authors told, a second consecutive timeout at the same inputs
    /// opened its streak.
    #[serde(serialize_with = "vocab_to", deserialize_with = "vocab_from")]
    pub kind: RedKind,
    /// The id of the frontier where it last passed on its channel; absent
    /// when it never did.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub passed_at: Option<String>,
    /// The id of the frontier where its failing streak began, or where it
    /// timed out.
    pub failed_at: String,
    /// The participants told with an `action_failed`: the authors of changes
    /// in the window to files the check reads. Empty when no change there
    /// touches one (a toolchain or environment change, or a flaky check):
    /// the check is red with no cause.
    #[serde(default)]
    pub told: Vec<String>,
}

/// One mirrored directory the caller authors (decision 137 B).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusMirror {
    /// `main`, or the forked workspace's name.
    pub workspace: String,
    /// The forked workspace's id, in `ID_ENCODING`; absent for `main`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    /// The directory, absolute.
    pub dir: String,
    /// What the forked workspace was forked from: `main`, or a workspace
    /// id in `ID_ENCODING`; absent for `main`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub base: Option<String>,
}

/// One live grant of the caller's.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusGrant {
    /// The grant's id, in `ID_ENCODING`.
    pub id: String,
    /// Its issuer, in `ID_ENCODING`.
    pub issuer: String,
    /// The paths it covers (gitignore syntax).
    pub paths: Vec<String>,
    /// The capabilities it gives on them.
    pub capabilities: Vec<CapabilityParam>,
    /// The end of its window, in Unix seconds; absent when it has none.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub not_after_s: Option<i64>,
    /// Whether this daemon, holding its issuer's key, renews it before its
    /// window ends (decision 148): never for a grant with no window, nor
    /// for a lineage with a revoked grant or one a revocation this daemon
    /// sent names (decision 185).
    #[serde(default)]
    pub renews: bool,
}

/// One grant of the caller's that ended lately.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusEnded {
    /// The grant's id, in `ID_ENCODING`.
    pub id: String,
    /// When it ended, in Unix seconds: its window's end, or the time its
    /// revocation names when that comes first.
    pub ended_s: i64,
    /// Whether it is revoked.
    pub revoked: bool,
}

/// One stale save of a mirror the caller authors (design §8, decision 66).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusStale {
    /// The file.
    pub address: String,
    /// The forked workspace whose mirror saved it, in `ID_ENCODING`;
    /// absent for `main`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
}

/// A path a mirror the caller authors syncs but never materializes, since
/// this platform's filesystem can't hold it under its own name (decision
/// 252): a reserved device name, a trailing dot or space, a character
/// Windows forbids, or a name equal ignoring case to a sibling's that sorts
/// first. Its document syncs as any other.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusUnmaterialized {
    /// The path.
    pub address: String,
    /// The forked workspace whose mirror leaves it out, in `ID_ENCODING`;
    /// absent for `main`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
    /// Why, and how to recover: `fix_request`, with a `read` of the
    /// document as `next`, to move its content to a path this platform
    /// can hold.
    pub error: ErrorData,
}

/// A path a mirror the caller authors stopped writing (`write_held`):
/// another program held the file open past `HELD_DEADLINE_MS`, so the file
/// on disk is older than its document. Listed until a later write of it
/// lands.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusHeld {
    /// The path.
    pub address: String,
    /// The forked workspace whose mirror holds it, in `ID_ENCODING`;
    /// absent for `main`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
    /// Why, and how to recover: `wait`, with a `read` of the document as
    /// `next`; close the file in the program holding it, and the next
    /// render writes it.
    pub error: ErrorData,
}

/// A revocation not every known peer holds yet (decision 326): the grants
/// one `revoke` ended (their ids in `ID_ENCODING`, sorted), their
/// grantee's name, its time `at_s` (Unix seconds), and the known peers
/// that hold it and those that don't, as `revoke`'s result splits them.
/// A peer in `not_seen_by` keeps honouring the grants until it syncs.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusRevocation {
    pub grants: Vec<String>,
    pub grantee: String,
    pub at_s: i64,
    #[serde(default)]
    pub seen_by: Vec<crate::grants::RevocationPeer>,
    #[serde(default)]
    pub not_seen_by: Vec<crate::grants::RevocationPeer>,
}

/// A peer whose release this daemon can't sync with (production-readiness
/// U4): its identify advertised another wire major, none, or no sync
/// protocol of this daemon's major, so no sync stream opens with it.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusIncompatible {
    /// Its libp2p peer id.
    pub peer: String,
    /// Its identify agent version (`lvcs/<version> wire/<major>.<minor>`
    /// from a release that has one), printable ASCII, at most
    /// `AGENT_MAX_BYTES`.
    pub agent: String,
    /// The wire major it advertises, absent when its agent version names
    /// none.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub wire: Option<u16>,
    /// The wire major this daemon speaks.
    pub here: u16,
    /// Why, and who upgrades: `upgrade` when its wire major is newer than
    /// this daemon's, with `lvcs daemon` to run once a newer `lvcs` is
    /// installed; else `ask`, its owner to install an `lvcs` of this
    /// daemon's major, with `lvcs --version` for the release to name.
    pub error: ErrorData,
    /// This daemon's agent version, present when the peer speaks the same
    /// wire major but this daemon refused its sync (decision 320): their
    /// builds disagree.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub ours: Option<String>,
}

/// The last error the daemon answered a participant (decision 164).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LastError {
    /// The op of the request it answered, when the request named one.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub op: Option<String>,
    /// Its JSON-RPC error code.
    pub code: i64,
    /// Its `data`, as the answer carried it.
    pub data: ErrorData,
    /// When it was answered, in Unix milliseconds.
    pub ts: i64,
}

/// A protected path waiting for approval (decision 314): main holds it at
/// other heads than its approved heads, so mirrors and every frontier's
/// tree keep its approved version until an `approve` names it.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusWaiting {
    pub path: String,
    /// A frontier every change channel attests that holds the path as main
    /// does, which `lvcs approve <frontier> <path>` approves it at; absent
    /// while there is none.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub frontier: Option<String>,
    /// What to do: `fix_request` with the `lvcs approve` line, or why it
    /// can't be approved yet, with how to recover.
    pub error: ErrorData,
}