clawft_kernel/licence/gate.rs
1//! The run gate (ADR-106 section 4, step 7): bytes are installed or run only
2//! with BOTH a valid grant for this mesh and an operator hash approval whose
3//! sha256 set covers the binary. A grant alone never makes bytes runnable,
4//! and the grant key cannot sign an approval. Not wired into placement yet
5//! (phase 3); exported and tested here.
6
7use super::{ApprovalStore, CheckoutGrantStore};
8
9/// What is about to be installed or run.
10#[derive(Debug, Clone, Copy)]
11pub struct RunRequest<'a> {
12 /// Cog id.
13 pub cog_id: &'a str,
14 /// Cog version.
15 pub version: &'a str,
16 /// sha256 of the binary, lower-case hex, computed from the bytes.
17 pub sha256: &'a str,
18 /// BLAKE3 of the binary, lower-case hex, computed from the bytes (never
19 /// copied from a grant: the revocation check is made on this value).
20 pub blake3: &'a str,
21}
22
23/// Why the gate refused.
24#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
25pub enum RunDenied {
26 /// No valid grant for this mesh lists a binary with exactly this
27 /// (sha256, blake3) pair.
28 #[error("no valid checkout grant covers this binary")]
29 NoValidGrant,
30 /// No operator approval for this cog version covers this sha256.
31 #[error("no operator approval covers this binary")]
32 NoApproval,
33 /// The binary's BLAKE3 is revoked (this also withdraws its approval).
34 #[error("the binary's hash is revoked")]
35 HashRevoked,
36}
37
38/// Evidence for the run, for provenance.
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct RunPermit {
41 /// The covering grant's id.
42 pub grant_id: String,
43 /// The covering approval's content key.
44 pub approval_id: String,
45 /// The binary's BLAKE3, lower-case hex.
46 pub blake3: String,
47}
48
49/// May `req` be installed or run? Needs a valid grant and a covering approval,
50/// and the binary's `ArtifactHash` must not be revoked.
51pub fn may_run(
52 grants: &CheckoutGrantStore,
53 approvals: &ApprovalStore,
54 req: &RunRequest<'_>,
55) -> Result<RunPermit, RunDenied> {
56 let (grant, art) = grants
57 .valid_grant_for_artifact(req.cog_id, req.version, req.sha256, req.blake3)
58 .ok_or(RunDenied::NoValidGrant)?;
59 if grants.is_hash_revoked(req.blake3) {
60 return Err(RunDenied::HashRevoked);
61 }
62 let approval_id = approvals
63 .covering(req.cog_id, req.version, req.sha256)
64 .ok_or(RunDenied::NoApproval)?;
65 Ok(RunPermit { grant_id: grant.grant_id, approval_id, blake3: art.blake3 })
66}