9.5 KiB
9.5 KiB
Artifact Generation
Overview
Every deployment generates immutable artifacts that enable reproducibility, audit, and rollback.
Generated Artifacts
1. Compose Lock File
File: compose.stella.lock.yml
A Docker Compose file with all image references pinned to specific digests.
# compose.stella.lock.yml
# Generated by Stella Ops - DO NOT EDIT
# Release: myapp-v2.3.1
# Generated: 2026-01-10T14:30:00Z
# Generator: stella-artifact-generator@1.5.0
version: "3.8"
services:
api:
image: registry.example.com/myapp/api@sha256:abc123...
# Original tag: v2.3.1
deploy:
replicas: 2
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
labels:
stella.component.id: "comp-api-uuid"
stella.release.id: "rel-uuid"
stella.digest: "sha256:abc123..."
worker:
image: registry.example.com/myapp/worker@sha256:def456...
# Original tag: v2.3.1
deploy:
replicas: 1
labels:
stella.component.id: "comp-worker-uuid"
stella.release.id: "rel-uuid"
stella.digest: "sha256:def456..."
# Stella metadata
x-stella:
release:
id: "rel-uuid"
name: "myapp-v2.3.1"
created_at: "2026-01-10T14:00:00Z"
environment:
id: "env-uuid"
name: "production"
deployment:
id: "deploy-uuid"
started_at: "2026-01-10T14:30:00Z"
checksums:
sha256: "checksum-of-this-file"
2. Version Sticker
File: stella.version.json
Metadata file placed on deployment targets indicating current deployment state.
{
"version": "1.0",
"generatedAt": "2026-01-10T14:35:00Z",
"generator": "stella-artifact-generator@1.5.0",
"release": {
"id": "rel-uuid",
"name": "myapp-v2.3.1",
"createdAt": "2026-01-10T14:00:00Z",
"components": [
{
"name": "api",
"digest": "sha256:abc123...",
"semver": "2.3.1",
"tag": "v2.3.1"
},
{
"name": "worker",
"digest": "sha256:def456...",
"semver": "2.3.1",
"tag": "v2.3.1"
}
]
},
"deployment": {
"id": "deploy-uuid",
"promotionId": "promo-uuid",
"environmentId": "env-uuid",
"environmentName": "production",
"targetId": "target-uuid",
"targetName": "prod-web-01",
"strategy": "rolling",
"startedAt": "2026-01-10T14:30:00Z",
"completedAt": "2026-01-10T14:35:00Z"
},
"deployer": {
"userId": "user-uuid",
"userName": "john.doe",
"agentId": "agent-uuid",
"agentName": "prod-agent-01"
},
"previous": {
"releaseId": "prev-rel-uuid",
"releaseName": "myapp-v2.3.0",
"digest": "sha256:789..."
},
"signature": "base64-encoded-signature",
"signatureAlgorithm": "RS256",
"signerKeyRef": "stella/signing/prod-key-2026"
}
3. Evidence Packet
File: Evidence stored in database (exportable as JSON/PDF)
See Evidence Schema for full specification.
4. Deployment Script (Optional)
File: deploy.stella.script.dll or deploy.stella.sh
When deployments use C# or shell scripts with hooks:
// deploy.stella.csx (source, compiled to DLL)
#r "nuget: StellaOps.Sdk, 1.0.0"
using StellaOps.Sdk;
// Pre-deploy hook
await Context.RunPreDeployHook(async (ctx) => {
await ctx.ExecuteCommand("./scripts/backup-database.sh");
await ctx.HealthCheck("/ready", timeout: 30);
});
// Deploy
await Context.Deploy();
// Post-deploy hook
await Context.RunPostDeployHook(async (ctx) => {
await ctx.ExecuteCommand("./scripts/warm-cache.sh");
await ctx.Notify("slack", "Deployment complete");
});
Artifact Storage
Storage Structure
artifacts/
├── {tenant_id}/
│ ├── {deployment_id}/
│ │ ├── compose.stella.lock.yml
│ │ ├── deploy.stella.script.dll (if applicable)
│ │ ├── deploy.stella.script.csx (source)
│ │ ├── manifest.json
│ │ └── checksums.sha256
│ └── ...
└── ...
Manifest File
{
"version": "1.0",
"deploymentId": "deploy-uuid",
"createdAt": "2026-01-10T14:30:00Z",
"artifacts": [
{
"name": "compose.stella.lock.yml",
"type": "compose-lock",
"size": 2048,
"sha256": "abc123..."
},
{
"name": "deploy.stella.script.dll",
"type": "script-compiled",
"size": 8192,
"sha256": "def456..."
}
],
"totalSize": 10240,
"signature": "base64-signature"
}
Artifact Generation Process
┌─────────────────────────────────────────────────────────────────────────────┐
│ ARTIFACT GENERATION FLOW │
│ │
│ ┌─────────────────┐ │
│ │ Promotion │ │
│ │ Approved │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ARTIFACT GENERATOR │ │
│ │ │ │
│ │ 1. Load release bundle (components, digests) │ │
│ │ 2. Load environment configuration (variables, secrets refs) │ │
│ │ 3. Load workflow template (hooks, scripts) │ │
│ │ 4. Generate compose.stella.lock.yml │ │
│ │ 5. Compile scripts (if any) │ │
│ │ 6. Generate version sticker template │ │
│ │ 7. Compute checksums │ │
│ │ 8. Sign artifacts │ │
│ │ 9. Store in artifact storage │ │
│ │ │ │
│ └────────────────────────────┬────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ DEPLOYMENT ORCHESTRATOR │ │
│ │ │ │
│ │ Artifacts distributed to targets via agents │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Artifact Properties
Immutability
Once generated, artifacts are never modified:
- Content-addressed storage (hash in path/metadata)
- No overwrite capability
- Append-only storage pattern
Integrity
All artifacts are:
- Checksummed (SHA-256)
- Signed with deployment key
- Verifiable at deployment time
Retention
| Environment | Retention Period |
|---|---|
| Development | 30 days |
| Staging | 90 days |
| Production | 7 years (compliance) |
API Operations
# List artifacts for deployment
GET /api/v1/deployment-jobs/{id}/artifacts
Response: Artifact[]
# Download specific artifact
GET /api/v1/deployment-jobs/{id}/artifacts/{name}
Response: binary
# Get artifact manifest
GET /api/v1/deployment-jobs/{id}/artifacts/manifest
Response: ArtifactManifest
# Verify artifact integrity
POST /api/v1/deployment-jobs/{id}/artifacts/{name}/verify
Response: { valid: boolean, checksum: string, signature: string }
Drift Detection
Version stickers enable drift detection:
interface DriftCheck {
targetId: UUID;
expectedSticker: VersionSticker;
actualSticker: VersionSticker | null;
driftDetected: boolean;
driftType?: "missing" | "corrupted" | "mismatch";
details?: {
expectedDigest: string;
actualDigest: string;
field: string;
};
}