deputy_core/traits.rs
1use crate::error::Result;
2use crate::ids::{ArtifactRef, ContentHash, EcosystemId, Pin, SourceId};
3use crate::state::ScanVerdict;
4
5/// Which store a content-addressed artifact lives in. The dirty store is staging; the
6/// prod store is the trusted, append-only golden set (`docs/ARCHITECTURE.md` §4).
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum StoreKind {
9 Dirty,
10 Prod,
11}
12
13/// An acquirable dependency ecosystem. Cargo is the first implementor; npm/PyPI/Go follow
14/// without pipeline-core changes (`docs/PIPELINE.md` §0).
15///
16/// Method signatures are synchronous contracts; I/O-bound implementations may bridge to an
17/// async runtime internally. An implementor produces candidates for the **dirty** store
18/// only — it can never write to prod, and verification (hash/signature) is enforced by core
19/// callers, not by the implementor (`docs/THREAT_MODEL.md` ADV-6).
20pub trait DepEcosystem {
21 fn id(&self) -> EcosystemId;
22
23 /// Read the source's resolved, pinned dependency graph as [`Pin`]s — each an exact
24 /// name+version bound to its expected content hash. For Cargo these come straight from
25 /// `Cargo.lock` (`name`, `version`, `checksum`), so the pins are already tamper-evident.
26 /// Dependencies without a fetchable registry checksum (path/git/workspace members) are
27 /// omitted.
28 fn discover(&self, source: &SourceId) -> Result<Vec<Pin>>;
29
30 /// Download the artifact bytes for a pin. The bytes are untrusted until verified.
31 fn fetch(&self, pin: &Pin) -> Result<Vec<u8>>;
32
33 /// Verify downloaded bytes against the pin's expected hash. Errors on mismatch.
34 fn verify_integrity(&self, pin: &Pin, raw: &[u8]) -> Result<()>;
35}
36
37/// Content-addressed artifact storage. The address is the hash of the bytes (ecosystem is a
38/// higher-level annotation, not part of storage). Implementations seal every artifact at rest
39/// with AES-256-GCM under a per-artifact subkey (`docs/STORAGE.md` §2).
40pub trait ArtifactStore {
41 /// Store bytes and return their content address. Idempotent: storing the same bytes
42 /// twice yields the same `ContentHash` and is a no-op the second time.
43 fn put(&self, kind: StoreKind, raw: &[u8]) -> Result<ContentHash>;
44
45 /// Retrieve and decrypt previously stored bytes by content address.
46 fn get(&self, kind: StoreKind, hash: &ContentHash) -> Result<Vec<u8>>;
47
48 /// Whether the given store holds the artifact with this content address.
49 fn contains(&self, kind: StoreKind, hash: &ContentHash) -> Result<bool>;
50}
51
52/// Encrypted metadata: scan verdicts, promotion receipts, and graph annotations
53/// (`docs/STORAGE.md` §4).
54pub trait MetadataStore {
55 fn record_verdict(&self, artifact: &ArtifactRef, verdict: &ScanVerdict) -> Result<()>;
56 fn verdict(&self, artifact: &ArtifactRef) -> Result<Option<ScanVerdict>>;
57}