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,
}