greentic_types/
supply_chain.rs

1//! Supply-chain oriented shared types (builds, scans, signing, metadata).
2
3use alloc::{string::String, vec::Vec};
4use core::hash::BuildHasherDefault;
5use fnv::FnvHasher;
6use indexmap::IndexMap;
7
8#[cfg(feature = "schemars")]
9use schemars::JsonSchema;
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13
14#[cfg(feature = "time")]
15use time::OffsetDateTime;
16
17use crate::{
18    ArtifactRef, AttestationId, AttestationRef, BranchRef, BuildLogRef, BuildRef, CommitRef,
19    ComponentRef, RegistryRef, RepoRef, SbomRef, ScanRef, SignatureRef, SigningKeyRef,
20    StatementRef, StoreRef, TenantCtx, VersionRef,
21};
22
23/// Hasher used for IndexMap fields to stay `no_std` friendly.
24pub type SupplyHasher = BuildHasherDefault<FnvHasher>;
25
26/// Plan describing how to execute a build.
27#[derive(Clone, Debug, PartialEq)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29#[cfg_attr(feature = "schemars", derive(JsonSchema))]
30pub struct BuildPlan {
31    /// Identifier for the build.
32    pub build_id: BuildRef,
33    /// Component being built.
34    pub component: ComponentRef,
35    /// Optional source branch reference.
36    #[cfg_attr(
37        feature = "serde",
38        serde(default, skip_serializing_if = "Option::is_none")
39    )]
40    pub branch: Option<BranchRef>,
41    /// Source repository reference.
42    pub source_repo: RepoRef,
43    /// Commit identifier from the source repository.
44    pub commit: String,
45    /// Optional structured commit reference.
46    #[cfg_attr(
47        feature = "serde",
48        serde(default, skip_serializing_if = "Option::is_none")
49    )]
50    pub commit_ref: Option<CommitRef>,
51    /// Language or ecosystem descriptor (for example `rust`, `nodejs`).
52    pub language: String,
53    /// Entrypoint or build target.
54    pub entrypoint: String,
55    /// Environment variables passed to the build.
56    #[cfg_attr(
57        feature = "serde",
58        serde(default, skip_serializing_if = "IndexMap::is_empty")
59    )]
60    #[cfg_attr(
61        feature = "schemars",
62        schemars(
63            with = "alloc::collections::BTreeMap<String, String>",
64            description = "Environment variables"
65        )
66    )]
67    pub env: IndexMap<String, String, SupplyHasher>,
68    /// Expected outputs (artifact references).
69    #[cfg_attr(
70        feature = "serde",
71        serde(default, skip_serializing_if = "Vec::is_empty")
72    )]
73    pub outputs: Vec<ArtifactRef>,
74    /// Provider-specific metadata.
75    #[cfg_attr(feature = "serde", serde(default))]
76    pub metadata: Value,
77}
78
79/// Lifecycle status for a build.
80#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
81#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
82#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
83#[cfg_attr(feature = "schemars", derive(JsonSchema))]
84pub enum BuildStatusKind {
85    /// Build has been accepted but not started.
86    Pending,
87    /// Build is currently running.
88    Running,
89    /// Build finished successfully.
90    Succeeded,
91    /// Build failed.
92    Failed,
93    /// Build was cancelled before completion.
94    Cancelled,
95}
96
97/// Summary status for a build execution.
98#[derive(Clone, Debug, PartialEq)]
99#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100#[cfg_attr(feature = "schemars", derive(JsonSchema))]
101pub struct BuildStatus {
102    /// Identifier for the build.
103    pub build_id: BuildRef,
104    /// Current status.
105    pub status: BuildStatusKind,
106    /// Build start time (UTC).
107    #[cfg_attr(
108        all(feature = "schemars", feature = "time"),
109        schemars(with = "Option<String>", description = "RFC3339 timestamp in UTC")
110    )]
111    #[cfg_attr(
112        feature = "serde",
113        serde(default, skip_serializing_if = "Option::is_none")
114    )]
115    #[cfg(feature = "time")]
116    pub started_at_utc: Option<OffsetDateTime>,
117    /// Build finish time (UTC).
118    #[cfg_attr(
119        all(feature = "schemars", feature = "time"),
120        schemars(with = "Option<String>", description = "RFC3339 timestamp in UTC")
121    )]
122    #[cfg_attr(
123        feature = "serde",
124        serde(default, skip_serializing_if = "Option::is_none")
125    )]
126    #[cfg(feature = "time")]
127    pub finished_at_utc: Option<OffsetDateTime>,
128    /// Produced artifacts.
129    #[cfg_attr(
130        feature = "serde",
131        serde(default, skip_serializing_if = "Vec::is_empty")
132    )]
133    pub artifacts: Vec<ArtifactRef>,
134    /// Optional build logs reference.
135    #[cfg_attr(
136        feature = "serde",
137        serde(default, skip_serializing_if = "Option::is_none")
138    )]
139    pub logs_ref: Option<String>,
140    /// Optional structured build log references.
141    #[cfg_attr(
142        feature = "serde",
143        serde(default, skip_serializing_if = "Vec::is_empty")
144    )]
145    pub log_refs: Vec<BuildLogRef>,
146    /// Provider-specific metadata.
147    #[cfg_attr(feature = "serde", serde(default))]
148    pub metadata: Value,
149}
150
151/// Supported scan kinds.
152#[derive(Clone, Debug, PartialEq, Eq, Hash)]
153#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
154#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
155#[cfg_attr(feature = "schemars", derive(JsonSchema))]
156pub enum ScanKind {
157    /// Source code analysis (SAST).
158    Source,
159    /// Dependency or composition analysis.
160    Dependencies,
161    /// Binary or container image analysis.
162    Artifact,
163    /// Custom or provider-specific scan.
164    Custom(String),
165}
166
167/// Request to execute a scan.
168#[derive(Clone, Debug, PartialEq)]
169#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
170#[cfg_attr(feature = "schemars", derive(JsonSchema))]
171pub struct ScanRequest {
172    /// Identifier for the scan.
173    pub scan_id: ScanRef,
174    /// Component being scanned.
175    pub component: ComponentRef,
176    /// Scan kind.
177    pub kind: ScanKind,
178    /// Optional commit associated with the scan.
179    #[cfg_attr(
180        feature = "serde",
181        serde(default, skip_serializing_if = "Option::is_none")
182    )]
183    pub commit_ref: Option<CommitRef>,
184    /// Target artifact (when applicable).
185    #[cfg_attr(
186        feature = "serde",
187        serde(default, skip_serializing_if = "Option::is_none")
188    )]
189    pub artifact: Option<ArtifactRef>,
190    /// Provider-specific inputs.
191    #[cfg_attr(feature = "serde", serde(default))]
192    pub metadata: Value,
193}
194
195/// Lifecycle status for a scan.
196#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
197#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
198#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
199#[cfg_attr(feature = "schemars", derive(JsonSchema))]
200pub enum ScanStatusKind {
201    /// Scan has been accepted but not started.
202    Pending,
203    /// Scan is currently running.
204    Running,
205    /// Scan finished successfully.
206    Succeeded,
207    /// Scan failed.
208    Failed,
209}
210
211/// Result summary for a scan.
212#[derive(Clone, Debug, PartialEq)]
213#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
214#[cfg_attr(feature = "schemars", derive(JsonSchema))]
215pub struct ScanResult {
216    /// Identifier for the scan.
217    pub scan_id: ScanRef,
218    /// Component scanned.
219    pub component: ComponentRef,
220    /// Scan kind.
221    pub kind: ScanKind,
222    /// Final scan status.
223    pub status: ScanStatusKind,
224    /// Optional SBOM reference emitted by the scan.
225    #[cfg_attr(
226        feature = "serde",
227        serde(default, skip_serializing_if = "Option::is_none")
228    )]
229    pub sbom: Option<SbomRef>,
230    /// Scanner-specific findings.
231    #[cfg_attr(feature = "serde", serde(default))]
232    pub findings: Value,
233    /// Scan start time (UTC).
234    #[cfg_attr(
235        all(feature = "schemars", feature = "time"),
236        schemars(with = "Option<String>", description = "RFC3339 timestamp in UTC")
237    )]
238    #[cfg_attr(
239        feature = "serde",
240        serde(default, skip_serializing_if = "Option::is_none")
241    )]
242    #[cfg(feature = "time")]
243    pub started_at_utc: Option<OffsetDateTime>,
244    /// Scan finish time (UTC).
245    #[cfg_attr(
246        all(feature = "schemars", feature = "time"),
247        schemars(with = "Option<String>", description = "RFC3339 timestamp in UTC")
248    )]
249    #[cfg_attr(
250        feature = "serde",
251        serde(default, skip_serializing_if = "Option::is_none")
252    )]
253    #[cfg(feature = "time")]
254    pub finished_at_utc: Option<OffsetDateTime>,
255}
256
257/// Signing request for an artifact.
258#[derive(Clone, Debug, PartialEq)]
259#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
260#[cfg_attr(feature = "schemars", derive(JsonSchema))]
261pub struct SignRequest {
262    /// Signing key reference.
263    pub signing_key: SigningKeyRef,
264    /// Artifact to sign.
265    pub artifact: ArtifactRef,
266    /// Payload provided to the signer (hashes, claims, etc.).
267    #[cfg_attr(feature = "serde", serde(default))]
268    pub payload: Value,
269    /// Provider-specific metadata.
270    #[cfg_attr(feature = "serde", serde(default))]
271    pub metadata: Value,
272}
273
274/// Verification request for a signature.
275#[derive(Clone, Debug, PartialEq)]
276#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
277#[cfg_attr(feature = "schemars", derive(JsonSchema))]
278pub struct VerifyRequest {
279    /// Signature reference to verify.
280    pub signature: SignatureRef,
281    /// Subject artifact associated with the signature.
282    pub artifact: ArtifactRef,
283    /// Provider-specific metadata.
284    #[cfg_attr(feature = "serde", serde(default))]
285    pub metadata: Value,
286}
287
288/// Verification result.
289#[derive(Clone, Debug, PartialEq)]
290#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
291#[cfg_attr(feature = "schemars", derive(JsonSchema))]
292pub struct VerifyResult {
293    /// Signature reference.
294    pub signature: SignatureRef,
295    /// Whether the signature is valid.
296    pub valid: bool,
297    /// Optional diagnostic message.
298    #[cfg_attr(
299        feature = "serde",
300        serde(default, skip_serializing_if = "Option::is_none")
301    )]
302    pub message: Option<String>,
303    /// Provider-specific metadata.
304    #[cfg_attr(feature = "serde", serde(default))]
305    pub metadata: Value,
306}
307
308/// Predicate type for attestations.
309#[derive(Clone, Debug, PartialEq, Eq, Hash)]
310#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
311#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
312#[cfg_attr(feature = "schemars", derive(JsonSchema))]
313pub enum PredicateType {
314    /// SLSA provenance predicate.
315    Slsa,
316    /// Vulnerability assessment predicate.
317    Vulnerability,
318    /// Custom predicate identified by name.
319    Custom(String),
320}
321
322/// Attestation statement descriptor.
323#[derive(Clone, Debug, PartialEq)]
324#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
325#[cfg_attr(feature = "schemars", derive(JsonSchema))]
326pub struct AttestationStatement {
327    /// Optional generated attestation identifier.
328    #[cfg_attr(
329        feature = "serde",
330        serde(default, skip_serializing_if = "Option::is_none")
331    )]
332    pub attestation_id: Option<AttestationId>,
333    /// Attestation identifier.
334    pub attestation: AttestationRef,
335    /// Predicate type describing the attestation.
336    pub predicate_type: PredicateType,
337    /// Statement reference (for example DSSE envelope).
338    pub statement: StatementRef,
339    /// Optional registry where the attestation is stored.
340    #[cfg_attr(
341        feature = "serde",
342        serde(default, skip_serializing_if = "Option::is_none")
343    )]
344    pub registry: Option<RegistryRef>,
345    /// Optional content store reference.
346    #[cfg_attr(
347        feature = "serde",
348        serde(default, skip_serializing_if = "Option::is_none")
349    )]
350    pub store: Option<StoreRef>,
351    /// Provider-specific metadata.
352    #[cfg_attr(feature = "serde", serde(default))]
353    pub metadata: Value,
354}
355
356/// Generic metadata record attached to supply-chain entities.
357#[derive(Clone, Debug, PartialEq)]
358#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
359#[cfg_attr(feature = "schemars", derive(JsonSchema))]
360pub struct MetadataRecord {
361    /// Optional version reference associated with the record.
362    #[cfg_attr(
363        feature = "serde",
364        serde(default, skip_serializing_if = "Option::is_none")
365    )]
366    pub version: Option<VersionRef>,
367    /// Optional namespace grouping related keys.
368    #[cfg_attr(
369        feature = "serde",
370        serde(default, skip_serializing_if = "Option::is_none")
371    )]
372    pub namespace: Option<String>,
373    /// Metadata key (lower_snake_case or dotted).
374    pub key: String,
375    /// Metadata value as arbitrary JSON.
376    pub value: Value,
377}
378
379/// Repository-scoped context for convenience.
380#[derive(Clone, Debug, PartialEq)]
381#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
382#[cfg_attr(feature = "schemars", derive(JsonSchema))]
383pub struct RepoContext {
384    /// Tenant context.
385    pub tenant: TenantCtx,
386    /// Repository reference.
387    pub repo: RepoRef,
388}
389
390/// Store-scoped context for convenience.
391#[derive(Clone, Debug, PartialEq)]
392#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
393#[cfg_attr(feature = "schemars", derive(JsonSchema))]
394pub struct StoreContext {
395    /// Tenant context.
396    pub tenant: TenantCtx,
397    /// Store reference.
398    pub store: StoreRef,
399}