14 — Forgeyard Packaging System Architecture
Document type: Core Distribution & Packaging System Architecture
Project: Forgeyard CI/CD
Subsystem: Package planning, package manifests, platform/ecosystem adapters, deterministic packaging, installer/archive generation, container image packaging, repository metadata, signing handoff, artifact lineage, package validation, reproducibility, publishing preparation, and package-oriented evidence
Architecture style: Build-output-to-package transformation with immutable inputs, deterministic manifests, adapter-isolated format logic, strict artifact lineage, and signing/release separation
Status: Target production architecture for the new Forgeyard repository
Relationship to prior work: Builds on 03 CAS/Data Plane, 04 Pipeline IR, hermetic/reproducible build architecture, 12 Secrets & Trust, 13 Supply Chain/SBOM/Provenance/Signing, and all language/platform ecosystem architecture documents. It produces package artifacts that later flow into 15 — Release and 16 — Deployment.
1. Purpose
Forgeyard needs a packaging subsystem that converts already-built outputs into artifacts users and deployment systems can consume.
Examples:
.tar.zst
.zip
.deb
.rpm
.pkg
.dmg
.msi
.msix
.exe installer
.AppImage
.Flatpak bundle/repo artifact
Snap package
APK
AAB
IPA
XCArchive-derived release object
OCI image
Helm/chart-like bundle
generic server bundle
static-site bundle
Packaging is not “run whatever release script happens to exist.”
It must be modeled explicitly.
The central rule is:
Packaging consumes immutable build outputs and produces new immutable package artifacts. It must not silently rebuild application code.
A second rule is:
Every package format is an adapter behind one normalized package model.
A third rule is:
If packaging or signing changes bytes, Forgeyard creates a new artifact identity and preserves lineage from the original build output.
2. Architectural Position
Verified Build Outputs
│
▼
Package Plan
│
▼
Package Manifest
│
┌────────────┼────────────┐
▼ ▼ ▼
Linux Windows Apple
Adapter Adapter Adapter
│ │ │
└────────────┼────────────┘
▼
Package Artifact
│
┌──────────┼──────────┐
▼ ▼ ▼
Validate SBOM Provenance
│ │ │
└──────────┼──────────┘
▼
Signing Handoff
│
▼
Signed Package Artifact
│
▼
Release
3. Goals
The packaging subsystem MUST:
- consume immutable build outputs;
- define normalized package metadata;
- define normalized package contents;
- define deterministic package manifests;
- support multiple target formats;
- preserve file metadata explicitly;
- normalize timestamps where format permits;
- normalize ordering;
- support package-specific reproducibility;
- support Linux packaging;
- support Windows packaging;
- support macOS packaging;
- support Android packaging;
- support iOS packaging handoff;
- support OCI/container images;
- support generic archives;
- support installer generation;
- support package validation;
- support package SBOM/provenance;
- support signing handoff;
- keep signing keys out of packaging workers;
- support artifact lineage;
- support package repository preparation;
- support publishing metadata generation;
- support dry-run package planning;
- support package verification;
- support cross-platform runner requirements;
- support package adapter capability discovery;
- remain independent from release promotion;
- remain independent from deployment orchestration.
4. Non-Goals
Packaging does not:
compile source
resolve arbitrary mutable dependencies
approve releases
deploy packages
hold production signing keys
decide release channels
5. Workspace Structure
crates/package/
├── forgeyard-package/
├── forgeyard-package-model/
├── forgeyard-package-plan/
├── forgeyard-package-manifest/
├── forgeyard-package-layout/
├── forgeyard-package-metadata/
├── forgeyard-package-validate/
├── forgeyard-package-repro/
├── forgeyard-package-signing/
├── forgeyard-package-publish-model/
├── forgeyard-package-testkit/
│
├── forgeyard-package-archive/
├── forgeyard-package-deb/
├── forgeyard-package-rpm/
├── forgeyard-package-appimage/
├── forgeyard-package-flatpak/
├── forgeyard-package-snap/
├── forgeyard-package-msi/
├── forgeyard-package-msix/
├── forgeyard-package-windows-exe/
├── forgeyard-package-pkg/
├── forgeyard-package-dmg/
├── forgeyard-package-android/
├── forgeyard-package-apple-mobile/
├── forgeyard-package-oci/
└── forgeyard-package-server-bundle/
Potential ecosystem helpers remain in ecosystem crates, not package core.
6. PackageId
#![allow(unused)]
fn main() {
pub struct PackageId(Ulid);
}
Semantic package entity.
7. PackageSpecId
Canonical content/semantic plan digest:
#![allow(unused)]
fn main() {
pub struct PackageSpecId(Digest);
}
8. Package Artifact
Package bytes are CAS objects.
#![allow(unused)]
fn main() {
pub struct PackageArtifact {
pub package: PackageId,
pub spec: PackageSpecId,
pub object: CasObjectRef,
pub format: PackageFormat,
}
}
9. Package Format
#![allow(unused)]
fn main() {
pub enum PackageFormat {
Tar,
TarZstd,
Zip,
Deb,
Rpm,
AppImage,
Flatpak,
Snap,
Msi,
Msix,
WindowsExeInstaller,
Pkg,
Dmg,
Apk,
Aab,
Ipa,
OciImage,
ServerBundle,
Custom(PackageFormatId),
}
}
10. Package Plan
#![allow(unused)]
fn main() {
pub struct PackagePlan {
pub id: PackageSpecId,
pub subject: PackageSubject,
pub metadata: PackageMetadata,
pub files: PackageLayout,
pub format: PackageFormat,
pub reproducibility: PackageReproducibilityPolicy,
pub signing: PackageSigningRequirement,
}
}
11. Package Subject
#![allow(unused)]
fn main() {
pub struct PackageSubject {
pub build_artifacts: Vec<ArtifactId>,
pub build_objects: Vec<CasObjectRef>,
pub source: SourceSnapshotId,
pub plan: PipelinePlanId,
}
}
12. Package Metadata
#![allow(unused)]
fn main() {
pub struct PackageMetadata {
pub name: PackageName,
pub version: PackageVersion,
pub architecture: PackageArchitecture,
pub platform: PackagePlatform,
pub description: Option<String>,
pub license: Option<String>,
pub homepage: Option<String>,
pub maintainer: Option<String>,
}
}
13. Version
Package version is explicit input.
Do not derive from local Git implicitly.
14. Version Source
May come from:
project manifest
release candidate
pipeline parameter
version file
but must be resolved before packaging.
15. Package Layout
#![allow(unused)]
fn main() {
pub struct PackageLayout {
pub entries: Vec<PackageEntry>,
}
}
16. PackageEntry
#![allow(unused)]
fn main() {
pub struct PackageEntry {
pub source: PackageSource,
pub destination: PackagePath,
pub mode: FileMode,
pub ownership: PackageOwnership,
pub kind: PackageEntryKind,
}
}
17. Package Source
#![allow(unused)]
fn main() {
pub enum PackageSource {
Artifact(ArtifactId),
Cas(CasObjectRef),
Generated(GeneratedPackageFile),
}
}
18. Generated Package File
Examples:
desktop file
systemd unit
manifest
plist
metadata/control file
license notice
Generated deterministically from package plan.
19. No Arbitrary Host File
Strict packaging cannot include:
/home/user/random-file
unless materialized as explicit immutable input.
20. Package Path
Validated target-relative path.
21. Path Safety
Reject:
..
absolute paths
drive escape
invalid target path
22. File Ownership
Package ownership is semantic:
root:root
app user
current user
not inherited blindly from build host.
23. File Mode
Explicit.
24. Timestamp Normalization
Use controlled timestamp where format permits.
25. Ordering
Canonical file order.
26. Compression Parameters
Pinned/explicit for reproducibility.
27. Archive Packaging
Generic archive adapter supports:
tar
tar.zst
zip
28. Archive Manifest
Can include:
manifest.ron
checksums
LICENSE
README
SBOM
provenance
depending release policy.
29. Server Bundle
For daemon/server deployments:
binary
config templates
migration binaries
systemd/service files
licenses
SBOM/provenance refs
30. Linux Package Architecture
Linux package formats should consume one normalized Linux layout.
31. Linux Layout
Example:
/usr/bin/forgeyard
/usr/lib/forgeyard/...
/usr/share/doc/forgeyard/...
/usr/lib/systemd/system/forgeyard.service
format adapter maps appropriately.
32. DEB Adapter
Responsibilities:
control metadata
dependencies
filesystem layout
maintainer scripts only when necessary
deterministic archive construction
33. Maintainer Scripts
Avoid where declarative packaging suffices.
Scripts are high risk and harder to make portable/reproducible.
34. RPM Adapter
Same normalized package intent.
35. Dependency Metadata
System package dependencies should be explicit.
36. Auto Dependency Detection
May assist but must produce inspectable package plan.
37. AppImage
Build from exact application layout.
38. Flatpak
Flatpak build semantics may involve external runtime/app manifests.
Treat manifest/runtime as explicit inputs.
39. Snap
Same principle.
40. Windows Packaging
Native Windows packages should be built on Windows where platform tooling/validation/signing requires it.
41. MSI
Adapter handles:
ProductCode/UpgradeCode semantics
components/features
install paths
uninstall behavior
service registration
42. Stable Upgrade Identity
MSI identity fields must follow version/update strategy.
43. MSIX
Adapter handles:
manifest
capabilities
identity
package layout
44. Windows EXE Installer
Could use a selected installer backend.
Forgeyard core models installer semantics, adapter executes backend.
45. No Installer Tool Lock-In
Do not bake one vendor/tool into core.
46. Windows Signing
Unsigned package first.
Restricted signing phase produces signed package.
47. Apple Packaging
macOS packages require real Apple tooling where necessary.
48. .pkg
Adapter constructs installer package.
49. .dmg
Distribution image around signed/notarized app/package.
50. macOS App Bundle
Packaging may normalize:
.app structure
Info.plist
resources
embedded frameworks
51. Apple Signing Sequence
Typical:
build components
↓
assemble app bundle
↓
codesign nested components/app
↓
package/dmg
↓
sign package where required
↓
notarize/staple
Each byte-changing stage gets new artifact identity.
52. Apple Packaging vs Signing Boundary
Package adapter prepares signing operations.
Restricted signing worker/provider performs key operations.
53. Android Packaging
Android application build may already produce APK/AAB through Android toolchain.
Forgeyard packaging system normalizes:
artifact metadata
validation
alignment/signing stages
distribution metadata
54. Android APK
Unsigned APK -> signing/alignment sequence according to Android toolchain semantics.
55. Android AAB
Bundle artifact prepared for signing/publishing.
56. Android Release Key
Never general runner secret.
57. iOS Packaging
Requires Apple runner/toolchain.
58. IPA
Derived from exact signed archive/app bundle.
59. Provisioning Profiles
Sensitive release configuration handled through secret/trust subsystem.
60. OCI Image Packaging
OCI image is package artifact.
61. OCI Model
#![allow(unused)]
fn main() {
pub struct OciImagePlan {
pub config: OciConfig,
pub layers: Vec<OciLayerPlan>,
pub annotations: BTreeMap<String, String>,
}
}
62. OCI Layers
Construct deterministically from immutable inputs.
63. OCI Base Image
Resolved by digest.
Never mutable tag only in strict/release mode.
64. OCI Metadata
Avoid unnecessary timestamps/random data.
65. Container Entrypoint
Explicit.
66. OCI SBOM
Can attach as evidence/attestation rather than bake into filesystem unless desired.
67. OCI Signing
Separate signing/attestation stage.
68. Multi-Architecture OCI
Manifest/index references exact platform image digests.
69. Package Manifest
Every generated package has Forgeyard-native manifest.
#![allow(unused)]
fn main() {
pub struct ForgeyardPackageManifest {
pub spec: PackageSpecId,
pub format: PackageFormat,
pub subject: PackageSubject,
pub entries: Vec<ManifestEntry>,
pub generated_files: Vec<GeneratedFileRecord>,
pub toolchain: PackageToolchainIdentity,
}
}
70. Package Manifest Storage
Manifest bytes in CAS.
Metadata references it.
71. Manifest Is Not Necessarily Embedded
Could be embedded if format/release policy desires.
72. Package Toolchain Identity
Include:
dpkg tooling
rpm tooling
WiX/MSIX backend
Apple packaging tools
Android build tools
OCI builder
compression library
73. Toolchain Pinning
Release package toolchain must be immutable/pinned.
74. Packaging Derivation
Packaging itself can be represented as hermetic derivation:
package output =
f(
build artifacts,
package plan,
packaging toolchain,
controlled environment
)
75. PackageDerivationId
#![allow(unused)]
fn main() {
pub struct PackageDerivationId(Digest);
}
76. Package Reproducibility
#![allow(unused)]
fn main() {
pub struct PackageReproducibilityPolicy {
pub expected: ReproducibilityExpectation,
pub normalized_fields: Vec<PackageNormalizedField>,
}
}
77. Reproducible Package
Same inputs/toolchain -> same unsigned package bytes where format permits.
78. Format Constraints
Some formats/signing stages introduce unavoidable/non-deterministic fields.
Mark explicitly.
79. Unsigned Reproducibility
Prefer validating unsigned package reproducibility independently from signing.
80. Package Reproduction
Can run package derivation on independent runner and compare.
81. Package Normalized Comparison
If format inherently contains non-semantic variation, normalized-tree/semantic verification may apply.
82. No False Bit-for-Bit Claim
Use existing reproducibility levels.
83. Package Builder
Packaging should execute via normal Runner/Sandbox/Executor.
No special privileged daemon build path.
84. Package Worker
General runner is allowed if package step does not require sensitive signing.
85. Signing Worker
Only for signing/key operations.
86. Package Step JobSpec
Contains:
immutable input refs
package plan
toolchain
output declaration
network policy
87. Network
Packaging should generally be network-denied once dependencies/tooling are resolved.
88. Package Validation
After package generation, adapter validates package.
89. Validation Categories
format integrity
metadata
file layout
dependency metadata
signature state
installability static checks
policy
90. Install Test
Strong validation can install package in clean disposable environment.
91. Linux Install Test
Fresh container/VM matching distro.
92. Windows Install Test
Windows VM.
93. macOS Install Test
macOS VM/runner where licensing/platform permits.
94. Android Install Test
Emulator/physical device through device lab.
95. iOS Install Test
Simulator/device as supported.
96. Package Smoke Test
Install -> launch/version -> uninstall.
97. Installer Side Effects
Test in isolated disposable environment.
98. Package Validation Evidence
Stored as evidence.
99. Package SBOM
Final package artifact gets exact subject-bound SBOM.
100. Package Provenance
Provenance includes packaging transformation.
101. Build vs Package Provenance
Possible chain:
BuildArtifact provenance
↓
Packaging provenance
↓
PackageArtifact
102. Transformation Provenance
Records:
input artifact digests
PackageSpecId
PackageDerivationId
packaging toolchain
output digest
103. Signing Provenance
Separate stage from package derivation.
104. Package Lineage
#![allow(unused)]
fn main() {
pub struct PackageLineage {
pub inputs: Vec<CasObjectRef>,
pub unsigned: CasObjectRef,
pub signed: Option<CasObjectRef>,
pub notarized: Option<CasObjectRef>,
}
}
105. No In-Place Mutation
Every byte-changing stage creates new CAS object.
106. Metadata-Only State
Verification/promotion can change metadata state without changing object.
107. Package State
#![allow(unused)]
fn main() {
pub enum PackageState {
Planned,
Building,
Built,
Validating,
Verified,
AwaitingSignature,
Signed,
Notarized,
Failed,
}
}
108. Package State Is Not Release State
Release subsystem later decides candidate/promotion/channel.
109. Package Attempt
Packaging is executed as normal JobAttempt.
Package entity links resulting job/run.
110. Package Service
#![allow(unused)]
fn main() {
#[async_trait]
pub trait PackageService {
async fn plan_package(...);
async fn register_result(...);
async fn verify_package(...);
}
}
111. Package Adapter Trait
#![allow(unused)]
fn main() {
#[async_trait]
pub trait PackageAdapter: Send + Sync {
fn format(&self) -> PackageFormat;
fn requirements(
&self,
plan: &PackagePlan,
) -> PackageCapabilityRequirements;
async fn build(
&self,
context: PackageBuildContext,
) -> Result<PackageBuildResult, PackageError>;
async fn validate(
&self,
artifact: &PackageArtifact,
) -> Result<PackageValidationResult, PackageError>;
}
}
112. Adapter Requirements
Returns:
OS
architecture
packaging toolchain
SDK
sandbox
113. Capability Flow
Package plan -> adapter requirements -> scheduler.
114. Cross-Build Packaging
Generic archive can package cross-built outputs.
Native installers may require target OS.
115. Do Not Fake Native Validation
A Windows MSI created elsewhere without Windows validation cannot automatically be claimed production-ready.
116. Build/Host/Target Types
Reuse existing strong Build/Host/Target architecture.
117. Package Target
#![allow(unused)]
fn main() {
pub struct PackageTarget {
pub os: TargetOs,
pub arch: TargetArch,
pub format: PackageFormat,
}
}
118. Distribution Target
Linux distro package may include:
debian-family/version
rpm-family/version
119. ABI Compatibility
Package metadata can state minimum OS/runtime requirements.
120. glibc/musl
Linux artifact/package must preserve actual libc requirement.
121. Universal Binaries
Apple multi-arch package can contain universal app.
122. Windows Architecture
x64/arm64/etc explicit.
123. Android ABI
APK/AAB semantics explicit.
124. Package Metadata Sources
Project-level package config.
Example:
.forgeyard/package.ron
125. Human Package Config
Illustrative:
(
packages: [
(
name: "forgeyard-cli-linux",
format: TarZstd,
target: (
os: Linux,
arch: X86_64,
),
files: [
(
artifact: "forgeyard-cli",
to: "bin/forgeyard",
mode: "0755",
),
],
),
],
)
126. Package Config Parser
Could live in package model/parser or reuse general config infrastructure.
127. Package Config Validation
Check:
referenced output exists
destination collision
format metadata complete
target compatible
signing requirement compatible
128. Destination Collision
Two files to same path:
error
unless explicit override semantics.
129. Duplicate Metadata
Reject conflicting values.
130. Package Description
Non-semantic text can be versioned but still affects package bytes if embedded.
Therefore part of PackageSpecId.
131. Maintainer Metadata
Same.
132. License Files
Explicit package entries.
133. Generated Notices
Generated from dependency/license evidence.
134. Third-Party Notices
Could generate deterministic NOTICE artifact.
135. Desktop Integration
Linux:
.desktop
icons
MIME associations
Windows:
shortcuts
file associations
service registration
macOS:
Info.plist
bundle identifiers
icons
136. Bundle Identifier
Stable semantic ID.
137. Application Identifier
Use typed:
#![allow(unused)]
fn main() {
pub struct ApplicationId(BoundedString);
}
138. Package Identifier
Format-specific mapping from stable Forgeyard application/package IDs.
139. Upgrade Semantics
Packaging architecture must model:
install
upgrade
downgrade
uninstall
140. Upgrade Compatibility
Package adapter validates version identity rules.
141. Config Files
System package can mark config files appropriately.
142. User Data
Installer/package must not treat user data as package-owned artifact.
143. Uninstall
Should not delete user data by default unless product semantics require.
144. Service Installation
Server package can install:
systemd unit
Windows service
launchd plist
145. Service Credentials
Never baked into package.
146. Configuration
Package includes templates/defaults, not production secrets.
147. Post-Install Configuration
Handled by user/operator/deployment subsystem.
148. Migrations
Package may include migration binary.
Deployment/release decides when to execute migration.
149. No Auto DB Migration in Installer by Default
Dangerous for server packages.
150. Package Repository
Packaging prepares repository-ready objects/metadata.
Publishing is later action.
151. Linux Repository Metadata
Potential:
APT metadata
RPM repository metadata
152. Repository Metadata Signing
Restricted signing operation.
153. Repository Index Identity
Index bytes are separate artifact.
154. Repository Update
Produces new immutable index snapshot.
155. Package Registry Metadata
Container/package registry publish descriptors prepared.
156. Publishing Model
#![allow(unused)]
fn main() {
pub struct PackagePublishDescriptor {
pub package: PackageId,
pub object: CasObjectRef,
pub destination: PublishDestination,
pub coordinates: PackageCoordinates,
}
}
157. Publish Destination
#![allow(unused)]
fn main() {
pub enum PublishDestination {
AptRepository,
RpmRepository,
OciRegistry,
GenericHttp,
PackageRegistry,
AppStore,
PlayStore,
Custom(PublishDestinationId),
}
}
158. Release Subsystem Owns Publish Decision
Packaging only prepares descriptor/artifact.
159. App Stores
Android/iOS store upload is release/publish workflow.
160. Package Validation Before Publish
Required according to policy.
161. Package Scan
Security scan final package.
162. Malware Scan
Optional enterprise evidence.
163. Binary Signature Check
After signing.
164. Package Format Validation
Use native tooling where available.
165. Installer Simulation
Optional.
166. Package Metadata DB
Store:
PackageId
PackageSpecId
format
target
state
artifact refs
lineage
validation refs
167. Package Bytes
CAS only.
168. Large Package
Stream.
169. Package Cache
Packaging derivation can be action-cached.
170. Cache Key
Includes:
input artifact digests
package spec
toolchain
format adapter semantic version
171. Package Cache Trust
Release may require trusted/reproduced package even if cache hit.
172. Cache Hit
Still validate evidence/signature policy.
173. Package Spec Canonicalization
Deterministic RON/model normalization.
174. PackageSpecId
Digest of canonical semantic package plan.
175. Adapter Version
Part of derivation identity if behavior affects bytes.
176. Compression Library Version
Same if output bytes depend on it.
177. Packaging Environment
Controlled:
TZ
locale
umask
hostname
user
timestamps
178. SOURCE_DATE_EPOCH
Use where compatible.
179. Package Timestamp Source
Source snapshot timestamp or explicit release epoch according to reproducibility architecture.
180. Random IDs
Avoid if format permits.
If format requires GUID-like IDs:
derive deterministically from semantic package identity where safe.
181. MSI GUIDs
Must respect Windows Installer upgrade/component semantics.
Can derive stable UUIDv5-like identifiers from package/component identity where appropriate.
182. Randomness
If required for security/signing, separate from deterministic unsigned packaging.
183. Temporary Files
Per attempt.
184. Host Tool Leakage
Strict package jobs only use managed toolchain.
185. Package Adapter Error
#![allow(unused)]
fn main() {
pub enum PackageError {
InvalidPlan,
UnsupportedTarget,
MissingInput,
ToolchainUnavailable,
BuildFailed,
ValidationFailed,
ReproMismatch,
SigningRequired,
Internal,
}
}
186. Failure Class Mapping
Packaging build failure is workload/package failure unless infrastructure-specific.
187. Adapter Diagnostic
Provide format-specific actionable diagnostics.
188. Package Explain
forgeyard package explain
Shows:
inputs
layout
target
toolchain
signing requirement
repro policy
189. CLI
forgeyard package plan
forgeyard package build
forgeyard package validate
forgeyard package inspect
forgeyard package reproduce
forgeyard package lineage
forgeyard package list
190. package plan
No execution.
191. package build
Creates Job/Run or local execution according to mode.
192. package validate
Validates package object.
193. package inspect
Shows normalized package metadata/file list.
194. package lineage
Shows:
build outputs
unsigned package
signed package
notarized package
195. Dioxus UI
Package page:
Overview
Inputs
Layout
Metadata
Validation
SBOM
Provenance
Signatures
Lineage
Publish Targets
196. Package Matrix
One product release may include:
Linux x86_64 tar
Linux arm64 tar
Windows x64 MSI
Windows arm64 MSIX
macOS universal DMG
Android APK/AAB
197. Package Group
#![allow(unused)]
fn main() {
pub struct PackageSetId(Ulid);
}
Groups related target packages.
198. Package Set
#![allow(unused)]
fn main() {
pub struct PackageSet {
pub id: PackageSetId,
pub version: PackageVersion,
pub packages: Vec<PackageId>,
}
}
199. Release Candidate Uses Package Set
Release subsystem can promote a package set.
200. Partial Package Failure
One target package can fail while others succeed.
Release policy decides whether full set required.
201. Cross-Target Version Consistency
Package set enforces same logical version where desired.
202. Cross-Target Source Consistency
All package artifacts in one release should bind same SourceSnapshotId unless explicitly multi-source.
203. Cross-Target Plan Consistency
Release records exact plan/spec for each target.
204. Package Naming
Human names not identities.
205. Output Filename
Generated deterministic from:
name
version
target
format
206. Filename Collision
Reject.
207. Sanitization
Platform-safe.
208. Content Type
Package metadata records MIME/media type.
209. Checksum File
Can generate:
SHA256SUMS
BLAKE3SUMS
as release/package artifacts.
210. Checksum Signing
Can be signed separately.
211. Package Index Manifest
For download site, generate machine-readable manifest.
212. Public Manifest
JSON may be appropriate for interoperability/site.
Internal canonical manifest can be RON/Postcard/CAS.
213. Download Metadata
Includes:
platform
arch
format
digest
size
signature
214. Package Size
Recorded from CAS.
215. Delta Updates
Future optional.
216. Delta Package
Derived from:
old exact package
new exact package
217. Delta Is New Artifact
With provenance linking both.
218. Delta Validation
Reconstruct new package and verify exact digest.
219. Auto-Update Feeds
Release subsystem later publishes update feed.
Packaging can create feed entries/manifests.
220. Feed Signing
Restricted signing.
221. Supply Chain Integration
Each package should have:
SBOM
provenance
validation evidence
signature evidence
as policy requires.
222. Evidence Bundle
Package-level evidence bundle becomes release input.
223. Package Provenance Chain
SourceSnapshot
↓
BuildArtifact
↓
PackageArtifact
↓
SignedPackage
↓
Release
224. VEX
VEX can target final package digest.
225. License Notice
Package can embed notices generated from evidence.
226. Package Policy Facts
#![allow(unused)]
fn main() {
pub struct PackagePolicyFacts {
pub format: PackageFormat,
pub target: PackageTarget,
pub validated: bool,
pub reproducibility: ReproducibilityLevel,
pub signed: bool,
pub sbom: bool,
}
}
227. Policy Examples
Windows production package must be signed
macOS release must be notarized
Android release must use production signing key
Linux tar must have SBOM
all release packages must be reproduced
228. Policy Engine Reuse
No separate package policy language.
229. Signing Requirement
#![allow(unused)]
fn main() {
pub enum PackageSigningRequirement {
None,
Optional,
Required(SigningProfileId),
}
}
230. Signing Profile
Defines:
key class
format signer
timestamping
notarization
231. Package Adapter Does Not Resolve Key Value
Only SigningKeyRef/profile.
232. Restricted Signing Request
Generated after package validation/evidence.
233. Notarization Request
Separate external side effect.
234. Notarization State
Pending
Submitted
Accepted
Rejected
Unknown
235. Ambiguous Notarization
Reconcile provider state before retry.
236. Package Publish Credentials
Resolved only by release/publish worker.
237. No Publish Credential in Packaging Job
Unless packaging itself absolutely requires provider operation, which should be separated.
238. Package Reconciler
Checks:
Built but no artifact ref
Verified state without validation evidence
Signed state missing signed object
notarization unknown
239. Event Model
Examples:
PackagePlanned
PackageBuilt
PackageValidationSucceeded
PackageValidationFailed
PackageSigningRequested
PackageSigned
PackageNotarized
240. Event Payload
Digest/IDs only, no keys/secrets.
241. Observability
Metrics:
package_build_duration
package_validation_duration
package_size
package_repro_match
package_repro_mismatch
package_signing_wait
package_failures
242. Format Metrics
Low-cardinality by format/platform.
243. Tracing
package.plan
package.build
package.validate
package.reproduce
package.sign.request
package.notarize
244. Doctor
forgeyard package doctor
Checks:
packaging toolchains
platform capability
signing integration availability
validation tooling
245. Platform Doctor
Examples:
dpkg/rpm tooling
Windows installer backend
Xcode packaging tools
Android SDK/build-tools
OCI builder
246. Testkit
forgeyard-package-testkit/src/
├── lib.rs
├── plan.rs
├── layout.rs
├── artifact.rs
├── validator.rs
├── reproducibility.rs
├── lineage.rs
└── assertions.rs
247. Unit Tests
Test:
PackageSpecId
layout collisions
path safety
metadata normalization
248. Adapter Conformance
Every adapter should support standardized tests:
requirements
build
inspect
validate
lineage
249. Archive Tests
Bit-for-bit reproducible archive from same inputs.
250. DEB Tests
Validate package metadata/files.
251. RPM Tests
Same.
252. Windows Tests
Install/uninstall in clean VM.
253. macOS Tests
Bundle/package validation on real macOS.
254. Android Tests
APK/AAB validation and install test where applicable.
255. OCI Tests
Manifest/config/layer digest validation.
256. Signing Isolation Tests
Packaging runner cannot access private production key.
257. Package Rebuild Guard Test
Packaging stage cannot invoke source build action not declared as packaging tool transformation.
258. Input Identity Test
Changing input artifact digest changes PackageDerivationId.
259. Metadata Identity Test
Changing embedded package description/version changes PackageSpecId.
260. Toolchain Identity Test
Changing packaging toolchain changes derivation identity.
261. Timestamp Test
Controlled timestamps produce deterministic unsigned package where supported.
262. File Order Test
Host directory traversal order does not change package digest.
263. Ownership Test
Host UID/GID does not leak into package unexpectedly.
264. Permission Test
Package modes follow manifest.
265. Symlink Test
Safe and deterministic according to format.
266. Path Traversal Test
Malicious destination rejected.
267. Huge Package Test
Stream packaging without loading all files into RAM.
268. Repro Test
Independent runner produces identical/accepted normalized package.
269. Signed Lineage Test
Signing modifies bytes -> new object.
270. Promotion Guard Test
Release later uses signed object directly.
271. Fuzzing
Fuzz:
package manifest parser
layout
archive path handling
package metadata decoder
adapter inspection parsers
272. Failure Injection
disk full
packaging tool crash
CAS read fail
validation tool fail
signer unavailable
notarization timeout
273. Scale Tests
Many package targets.
274. Large File Count
Hundreds of thousands of files.
275. Memory Bound
Streaming manifest/package builder.
276. Build Artifact Closure
Package inputs can reference directory tree CAS closure.
277. Sparse Files
Handle according to format capability.
278. Extended Attributes
Explicit policy.
279. macOS xattrs
Important for bundle/signing semantics.
280. Linux capabilities/xattrs
Package adapter can represent when needed.
281. ACLs
Only where target format supports and explicitly declared.
282. Device Nodes
Forbidden for normal application packages unless special privileged package policy.
283. Setuid Bits
Denied by default.
284. Privileged Package Files
Require explicit security policy.
285. Installer Scripts
Treated as executable code.
Need explicit source/digest/review.
286. Script Environment
Package managers control runtime environment; document carefully.
287. Safe Default
Prefer declarative package metadata over scripts.
288. Package Template
Reusable package definitions can use typed templates from pipeline/config model.
289. Template Inputs
Typed.
290. Template Resolution
Before PackageSpecId.
291. Remote Package Template
Immutable source/digest only.
292. Package Config Version
#![allow(unused)]
fn main() {
pub struct PackageSchemaVersion(u16);
}
293. Package IR Version
Could be independent:
#![allow(unused)]
fn main() {
pub struct PackageIrVersion(u16);
}
294. Raw Config vs Canonical Plan
Same compiler-like approach as Pipeline.
295. Package Parse Flow
RON
↓
parse
↓
validate
↓
normalize
↓
PackagePlan
↓
PackageSpecId
296. Dry Run
Plan outputs:
target
runner requirements
input artifacts
expected generated files
signing requirements
297. Package DAG
One release can have dependency graph:
build
↓
package unsigned
↓
validate
↓
sign
↓
notarize
298. Use Normal Pipeline/Run System
Do not implement separate package scheduler.
299. Package Nodes
Pipeline package steps lower into normal jobs.
300. Package State Metadata
Convenience/read model around normal Run/Job results.
301. Release Candidate Inputs
Future release subsystem consumes only package artifacts meeting policy.
302. Package Readiness
#![allow(unused)]
fn main() {
pub enum PackageReadiness {
Incomplete,
Built,
Verified,
ReleaseReady,
}
}
303. ReleaseReady
Means required package-level evidence/signing complete.
It does not mean promoted.
304. Production Readiness Gates
Do not call packaging production-ready until:
normalized package model stable
PackageSpecId deterministic
path/layout safety tested
archive adapter reproducible
Linux package adapters validated
Windows packaging validated on Windows
Apple packaging validated on macOS
Android package flow validated
OCI packaging validated
package provenance integrated
signing handoff separated
install/smoke test path exists
lineage immutable
305. Implementation Phase 1 — Core Package Model
Implement:
PackageId
PackageFormat
PackagePlan
PackageLayout
PackageSpecId
306. Phase 2 — Generic Archives
tar
tar.zst
zip
307. Phase 3 — Package Derivation/Reproducibility
Hermetic packaging and comparison.
308. Phase 4 — Linux
DEB/RPM first.
AppImage/Flatpak/Snap later.
309. Phase 5 — Windows
MSI/MSIX/installer adapters.
310. Phase 6 — Apple
macOS app/pkg/dmg + signing handoff.
311. Phase 7 — Android/iOS
Mobile package metadata and signing handoff.
312. Phase 8 — OCI
Image/index packaging.
313. Phase 9 — Validation / Install Tests
Disposable environments/device lab.
314. Phase 10 — Evidence
Final package SBOM/provenance/validation.
315. Phase 11 — Publishing Descriptors
Prepare release destinations.
316. Phase 12 — Hardening
Large packages, fuzzing, cross-version adapters, failure injection.
317. Acceptance Tests
- Packaging accepts immutable build artifact refs only.
- Package plan cannot silently compile source.
- PackageSpecId is deterministic.
- Destination path traversal is rejected.
- Host UID/GID do not leak unintentionally.
- Host file ordering does not change deterministic archive.
- Controlled timestamps are applied.
- Packaging toolchain identity is recorded.
- Changing package metadata changes PackageSpecId.
- Changing input artifact changes PackageDerivationId.
- Archive packaging is bit-for-bit reproducible where expected.
- DEB validates on supported distro tooling.
- RPM validates on supported distro tooling.
- MSI/MSIX production validation uses Windows.
- Apple production packaging uses macOS tooling where required.
- Android release packaging keeps signing key outside general runner.
- OCI base image is digest-pinned in strict mode.
- Package output bytes are stored in CAS.
- Package metadata remains in SQL.
- Package provenance binds input/output digests.
- Final package SBOM binds exact package digest.
- Validation evidence binds exact package digest.
- Signing never mutates an existing CAS object.
- Signed package becomes new artifact if bytes change.
- Notarization/stapling byte changes create new object.
- General packaging worker cannot access production signing key.
- Package install test runs in disposable environment.
- Service package does not embed runtime secrets.
- Installer scripts require explicit declaration.
- Release publish descriptor has no authority to publish by itself.
- Same package model works standalone/distributed.
- Package adapters declare platform capabilities to scheduler.
- Package cache key includes adapter/toolchain semantics.
- Evidence bundle can prove complete package lineage.
- Forgeyard’s own Linux/Windows/macOS/mobile/container packages use this subsystem.
318. Architectural Invariants
- packaging is a transformation, not an uncontrolled rebuild;
- inputs are immutable CAS/artifact refs;
- package plan is canonical and deterministic;
- PackageSpecId identifies semantic package intent;
- PackageDerivationId identifies actual packaging derivation;
- package bytes live in CAS;
- metadata lives in store;
- every byte-changing stage creates new object;
- signing is separate from general packaging;
- production signing keys never reach general runners;
- package adapters are format-specific and isolated;
- common package model remains provider-neutral;
- native packages are validated on appropriate real platform;
- packaging toolchain is part of provenance/derivation;
- timestamps/order/ownership normalized where possible;
- path traversal is impossible by construction/validation;
- package scripts are explicit high-risk inputs;
- package configuration never contains production secrets;
- package verification does not mutate bytes;
- release promotion does not rebuild package;
- publishing authority belongs to release subsystem;
- final package has digest-bound evidence;
- package lineage remains immutable;
- OCI mutable tags never replace digest identity;
- package sets bind consistent version/source policy;
- cross-platform capability requirements are explicit;
- cache hits never bypass verification policy;
- standalone/distributed share package semantics;
- package adapters do not leak platform dependencies into core;
- Forgeyard dogfoods its packaging system.
319. Final Target Architecture
Build Artifact(s)
│
▼
PackagePlan
│
▼
PackageSpecId
│
▼
Packaging Derivation
│
▼
Unsigned Package
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Validate SBOM Provenance
│ │ │
└─────────────┼─────────────┘
▼
Policy Check
│
▼
Signing Handoff
│
▼
Signed Package
│
optional notarize
│
▼
Release-Ready Artifact
│
▼
Release
320. Final Architectural Position
Packaging identity:
immutable input artifact digests
+
canonical package plan
+
packaging toolchain identity
+
controlled environment
↓
PackageDerivationId
↓
package bytes
↓
CAS digest
Signing lineage:
Unsigned Package A
↓
restricted signing
↓
Signed Package B
↓
optional notarization/stapling
↓
Package C
Release handoff:
exact release-ready package digest
+
SBOM
+
provenance
+
validation
+
signature evidence
↓
Release subsystem
The key guarantee is:
Forgeyard packaging never obscures where distribution bytes came from. Every installer, archive, mobile bundle, system package, or container image is a deterministic, inspectable transformation of exact immutable build outputs, with explicit toolchains, validation, evidence, signing lineage, and no hidden rebuild step.
321. New-Repository Sequence
01 Core Domain & Foundation
02 Storage & Metadata
03 CAS & Artifact Data Plane
04 Pipeline IR / Parsing / Planning
05 Run / Job State Machine
06 Scheduler
07 Runner / Agent
08 Sandbox & Executor
09 Transport / QUIC / Protocol
10 Events / Reconciliation
11 Policy / Authorization / Identity
12 Secrets & Trust
13 Supply Chain / SBOM / Provenance / Signing
14 Packaging
15 Release
16 Deployment
17 Observability / Health / Doctor
18 API / Axum
19 Dioxus UI
20 Device Lab
21 SCM Provider Integrations
22 HA / Coordination / Raft
23 RBE Interop
24 Plugin / Extension Architecture
25 Operations / Backup / Upgrade / DR
26 Self-Hosting / Bootstrap / Release of Forgeyard