Docs

channel

Source: crates/lvcs-doc/src/kinds/channel/mod.rs.

/// A channel's name (decisions 119, 245, and 297): one or more
/// `/`-separated segments of `[a-z0-9._-]`, none of them empty or `.`,
/// holding no `..` anywhere, and at most `CHANNEL_NAME_MAX_BYTES` bytes in
/// all. Every action moves the channel named for it (a workflow change
/// job's is `<file stem>/<job id>` lowercased), and no name is reserved; a
/// `[channels]` combination's name is one too, with no document. Its
/// document is at
/// `CHANNEL_PREFIX` + the name, written literally, and stores no name: the
/// verifier reads it from the address. Every API parameter and `[git]
/// branches` key parses with [`ChannelName::parse`]. A JSON or CBOR text
/// string; one that doesn't parse is refused.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub struct ChannelName(String);

/// Why [`ChannelName::parse`] refused a name.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChannelNameError {
    /// A segment is empty or `.`, the name holds `..`, or a segment holds
    /// a byte outside `[a-z0-9._-]`.
    Segment,
    /// It is longer than `CHANNEL_NAME_MAX_BYTES` bytes.
    TooLong,
}

/// An action's id (decision 245): `HASH([ACTION_DOMAIN, action])` over
/// the canonical encoding of the action less its name, source, and quorum,
/// computed by lvcs-action's `Action::id`. Change actions, gated actions,
/// and the generator are all keyed by it. Written in `ID_ENCODING`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct ActionId(#[serde(with = "crate::cbor::fixed")] pub [u8; 32]);

/// The `HASH` of everything one action read at a frontier (decisions 121
/// and 245).
///
/// It covers the action id, the heads of the documents its `paths` and
/// `uses` match (decision 161), the dependency frontier, and the toolchain
/// hash. Two runners that ran an action on equal inputs name equal hashes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct InputsHash(#[serde(with = "crate::cbor::fixed")] pub [u8; 32]);

/// A runner's attestation (decisions 119, 245, and 297), in a change
/// action's channel: every change action of that channel, in `actions`
/// with its `InputsHash`, passed or was reused on `frontier` under the
/// toolchain
/// `toolchain_hash`. `sig` (the `runner` key's) and `device_sig` (the
/// `device` key's, the runner's daemon) both cover
/// [`Attestation::signing_input`]. Keyed [`Attestation::key`],
/// `HASH([runner, frontier id])`, so a runner attests a frontier once.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Attestation {
    pub frontier: Frontier,
    pub actions: BTreeMap<ActionId, InputsHash>,
    #[serde(with = "crate::cbor::fixed")]
    pub toolchain_hash: [u8; 32],
    pub runner: ParticipantKey,
    pub device: DeviceKey,
    #[serde(with = "crate::cbor::fixed")]
    pub sig: [u8; 64],
    #[serde(with = "crate::cbor::fixed")]
    pub device_sig: [u8; 64],
}

/// An approval (decisions 119 and 297), only in the approvals document:
/// `approver`
/// approves the changes `frontier` carries to each of `paths`, which are
/// sorted, unique, non-empty, at most `APPROVAL_MAX_PATHS`, and each a file
/// address `paths::protected()` matches. It carries no grant and no inner
/// signature: the `ChangeSig` of the change that puts it names the grant
/// and signs it. Keyed [`Approval::key`], `HASH([approver, frontier id,
/// paths])`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Approval {
    pub frontier: FrontierId,
    pub approver: ParticipantKey,
    pub paths: Vec<String>,
}

/// A gated action's run key (decisions 141 and 245).
///
/// It is `HASH([ACTION_DOMAIN, action, frontier id, run request])` for the
/// run request it serves, so each run request runs once. A run's commands
/// receive it as `LVCS_RUN`.
/// Written in `ID_ENCODING`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct RunKey(#[serde(with = "crate::cbor::fixed")] pub [u8; 32]);

/// A gated action's run outcome (decisions 141 and 245), in its
/// channel.
///
/// `runner` ran the gated action `action` on `frontier` as the run
/// `run`, and it `passed` or failed, at `time_ms` on the runner's clock:
/// the change that puts it is dated `time_ms / 1000` seconds, floored, or
/// every verifier refuses it, and a passed trigger run ranks by the least
/// `time_ms` of its passed outcomes. `sig` (the `runner` key's) and
/// `device_sig` (the `device` key's, the runner's daemon) both cover
/// [`Outcome::signing_input`]. Keyed [`Outcome::key`],
/// `HASH([runner, run])`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Outcome {
    /// The frontier the run ran on.
    pub frontier: Frontier,
    /// The gated action that ran.
    pub action: ActionId,
    /// The run's key.
    pub run: RunKey,
    /// Whether the run's commands exited zero.
    pub passed: bool,
    /// The participant whose runner ran it, and the change's signer.
    pub runner: ParticipantKey,
    /// The runner's daemon.
    pub device: DeviceKey,
    /// When the run ended, in milliseconds since the Unix epoch.
    pub time_ms: i64,
    /// The runner key's signature over the signing input.
    #[serde(with = "crate::cbor::fixed")]
    pub sig: [u8; 64],
    /// The device key's signature over the signing input.
    #[serde(with = "crate::cbor::fixed")]
    pub device_sig: [u8; 64],
}

/// A run request (decision 141), in a channel; the channels actor counts
/// only a gated action's.
///
/// `requester` asks that the channel's gated action run on `frontier`, at
/// `time_ms`.
/// Run requests are ordered by `(time_ms, entry id)`. It carries no inner
/// signature: the `ChangeSig` of the change that puts it names the grant
/// and signs it. Keyed [`RunRequest::key`], `HASH([requester, frontier id,
/// time_ms])`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RunRequest {
    /// The frontier to run on.
    pub frontier: FrontierId,
    /// The participant that asked for the run, and the change's signer.
    pub requester: ParticipantKey,
    /// When it was asked for, in milliseconds since the Unix epoch: the
    /// change that puts it is dated `time_ms / 1000` seconds, floored, or
    /// every verifier refuses it.
    pub time_ms: i64,
}

/// One output of a generation (row 180): the SHA-256 of the output's
/// bytes, and the heads holding those bytes in its document.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GeneratedOutput {
    /// The SHA-256 of the output's bytes.
    #[serde(with = "crate::cbor::fixed")]
    pub digest: [u8; 32],
    /// The heads holding those bytes in the output's document.
    #[serde(with = "crate::cbor::heads")]
    pub heads: Vec<ChangeHash>,
}

/// Why a derivation failed (row 180).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeriveFailure {
    /// The generator exited non-zero, was killed, or timed out.
    Exit,
    /// The run created, changed, or removed a file the classifier doesn't
    /// call derived.
    NonDerivedWrite,
}

/// A runner's generation record (row 180, decisions 245 and 297), in the
/// generator's channel.
///
/// `runner` ran the generator `action` (the one action with `writes`) on
/// the inputs `inputs` at `frontier` under the toolchain `toolchain_hash`,
/// and it wrote or found `outputs` (each output address's digest and
/// heads), or it failed with `failure`. `outputs` is empty exactly when
/// `failure` is set, and it holds at most `GENERATION_OUTPUTS_MAX`
/// addresses (a map, so sorted and unique). `sig` (the `runner` key's) and
/// `device_sig` (the `device` key's, the runner's daemon) both cover
/// [`Generation::signing_input`]. Keyed [`Generation::key`],
/// `HASH([runner, inputs])`, so a runner records one outcome per inputs.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Generation {
    /// The generator that ran.
    pub action: ActionId,
    /// The generator's `InputsHash`.
    pub inputs: InputsHash,
    /// The frontier the derivation ran on.
    pub frontier: Frontier,
    /// Each output address's digest and heads.
    pub outputs: BTreeMap<String, GeneratedOutput>,
    /// Why the derivation failed, when it did.
    pub failure: Option<DeriveFailure>,
    /// The toolchain the derivation ran under.
    #[serde(with = "crate::cbor::fixed")]
    pub toolchain_hash: [u8; 32],
    /// The runner that ran it, and the change's signer.
    pub runner: ParticipantKey,
    /// The runner's daemon.
    pub device: DeviceKey,
    /// The runner key's signature over the signing input.
    #[serde(with = "crate::cbor::fixed")]
    pub sig: [u8; 64],
    /// The device key's signature over the signing input.
    #[serde(with = "crate::cbor::fixed")]
    pub device_sig: [u8; 64],
}

/// One entry of a channel document.
///
/// It is a value of one of its genesis maps (`/attestations`,
/// `/generations`, `/outcomes`, or `/requests` in a channel; `/approvals`
/// in the approvals document), at the key [`ChannelEntry::key`] gives.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChannelEntry {
    Attestation(Attestation),
    Approval(Approval),
    /// A gated action's run outcome, in `/outcomes`.
    Outcome(Outcome),
    /// A run request, in `/requests`.
    Request(RunRequest),
    /// A generation record, in `/generations`.
    Generation(Generation),
}

/// A channel entry's key in its map, written in `ID_ENCODING`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct EntryId(#[serde(with = "crate::cbor::fixed")] pub [u8; 32]);

/// Put `entry` into a channel document or the approvals document
/// (decisions 119, 141, and 297).
///
/// It is committed by `origin` like an edit and answered like one:
/// `DocOutput::Changed` with `req`, or `DocOutput::Rejected` `Failed` when
/// the document has no map for the entry (an approval in a channel, or
/// any other entry in the approvals document), the entry's key
/// doesn't encode, its key holds another entry (an attestation of the same
/// frontier by the same runner with other bytes, such as another
/// device's), or the document is of another kind or closed. An entry is
/// its canonical CBOR as a byte string at its key ([`ChannelEntry::key`]
/// in `ID_ENCODING`) in its map, put only at a key
/// that holds nothing, so no put overwrites an entry; one the document
/// already holds at its key commits nothing and answers at the current
/// heads, so a re-put is a no-op. The caller checks it first (the shell
/// signs attestations and outcomes and checks approvals and run requests):
/// every peer's verifier refuses
/// a change whose entry would fail there.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ChannelPut {
    pub req: RequestId,
    pub origin: Origin,
    pub entry: ChannelEntry,
}

/// A channel document's or the approvals document's render (decisions
/// 119 and 297).
///
/// It comes after every change that adds entries and on
/// `DocEvent::Render`: the incarnation's first render
/// is `full`, listing every entry the document holds, and every later one
/// lists only the entries the changes since the previous render added, so
/// journals grow linearly. `added` lists every value at each key (Automerge
/// `get_all`, or each put a change made), so concurrent puts at one key
/// both appear, and a re-put appears again; `policy::counted` dedupes. A
/// full render lists the maps in genesis order (attestations, then
/// generations, then outcomes, then run requests; or approvals), each map
/// by key; a later one
/// lists entries in the order the changes put them.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ChannelRendered {
    #[serde(with = "crate::cbor::heads")]
    pub heads: Vec<ChangeHash>,
    pub added: Vec<ChannelEntry>,
    pub full: bool,
}