CAS (Content Addressable Storage) Infrastructure Contract
Status: APPROVED (infrastructure compose) — see “Implementation status” below for which parts are wired in
src/Version: 1.0.0 Last Updated: 2025-12-06 Owner: Platform Storage Guild
Overview
This contract defines the Content-Addressable Storage (CAS) infrastructure for StellaOps, using RustFS as the S3-compatible storage backend. The design provides:
- Content-addressed storage — objects addressed by SHA-256 hash
- Immutable evidence storage — write-once, never-delete for audit trails
- Lifecycle management — automated retention-policy enforcement
- Service-account isolation — fine-grained access control per service
Audience: Platform/storage operators deploying CAS via Docker Compose, and module engineers (Scanner, Signals, Evidence Locker, Attestor) wiring object storage into their services. The “Implementation status” section maps each claim to its compose file or src/ source so you can verify it.
Implementation status
The deployment topology in this document is realized by the devops/compose/docker-compose.cas.yaml compose file and the devops/compose/env/cas.env.example environment template (both present in the repo). The three RustFS instances, the cas-lifecycle manager, the per-service account/bucket bindings, and the retention values below are all verifiable against that compose file.
On the application side, services reach CAS through two distinct access models, depending on the consuming module:
S3-compatible (Evidence Locker). The Evidence Locker object-store abstraction (
StellaOps.EvidenceLocker.Core.Configuration.ObjectStoreKind) exposes two concrete backends —FileSystemandAmazonS3(seeEvidenceLockerOptions.cs). RustFS is consumed through theAmazonS3(S3-compatible) driver:S3EvidenceObjectStoreconstructs anIAmazonS3client (Amazon.S3). WORM/Object-Lock behaviour is controlled byObjectStoreOptions.EnforceWriteOnceandAmazonS3StoreOptions.ObjectLock(ObjectLockMode.Governance/ObjectLockMode.Compliance).Native RustFS HTTP API (Scanner, Signals). Other consuming services talk to RustFS directly over its HTTP object API rather than the S3 API. Scanner’s
RustFsArtifactObjectStore(StellaOps.Scanner.Storage.ObjectStore) and Signals’RustFsRuntimeFactsArtifactStore/RustFsCallgraphArtifactStoreissuePUT/GET/DELETErequests againstbuckets/{bucket}/objects/{key}and set RustFS-specific headers (X-RustFS-Immutable,X-RustFS-Retain-Seconds). Scanner storage selects the backend via aDriversetting (ScannerStorageDefaults.ObjectStoreProviders:s3,minio, orrustfs) plus aRustFsOptionsblock (BaseUrl,ApiKey,ApiKeyHeader).
In short, RustFS is both an S3-compatible target (for Evidence Locker) and a first-class, RustFS-named client target (for Scanner/Signals) within src/. The access model in this document describes the storage-tier configuration; application services reach the buckets via the per-service account keys defined here, supplied either as S3 credentials or as the RustFS API-key header.
Architecture
CAS Infrastructure
┌───────────────────┐ ┌───────────────────┐ ┌─────────────────────┐
│ rustfs-cas │ │ rustfs-evidence │ │ rustfs-attestation │
│ (mutable) │ │ (immutable) │ │ (immutable) │
├───────────────────┤ ├───────────────────┤ ├─────────────────────┤
│ • scanner- │ │ • evidence- │ │ • attestations │
│ artifacts │ │ bundles │ │ • dsse-envelopes │
│ • surface-cache │ │ • merkle-roots │ │ • rekor-receipts │
│ • runtime-facts │ │ • hash-chains │ │ │
│ • signals-data │ │ │ │ │
│ • provenance-feed │ │ │ │ │
│ • replay-bundles │ │ │ │ │
└─────────┬─────────┘ └─────────┬─────────┘ └──────────┬──────────┘
│ │ │
└──────────────────────┼───────────────────────┘
│
┌───────────┴───────────┐
│ cas-lifecycle │
│ (retention manager) │
└───────────────────────┘
Retention Policies
Aligned with best-in-class on-premise vulnerability scanners:
| Data Type | Retention | Rationale | Scanner Comparison |
|---|---|---|---|
| Vulnerability DB | 7 days | Fresh advisories required | Trivy: 7d, Grype: 5d |
| SBOM artifacts | 365 days | Audit compliance (SOC2, ISO27001) | Anchore: 365d |
| Scan results | 90 days | Common compliance window | Snyk: 90d enterprise |
| Evidence bundles | Indefinite | Immutable audit trail | N/A (StellaOps unique) |
| Attestations | Indefinite | Signed, verifiable | N/A (StellaOps unique) |
| Temp artifacts | 1 day | Work-in-progress cleanup | Standard practice |
Access Control Matrix
Service Accounts
| Service | Buckets | Permissions | Purpose |
|---|---|---|---|
scanner | scanner-artifacts, surface-cache, runtime-facts | read, write | Scan job artifacts, cache |
signals | runtime-facts, signals-data, provenance-feed | read, write | Runtime signal ingestion |
replay | replay-bundles, inputs-lock | read, write | Deterministic replay |
ledger | evidence-bundles, merkle-roots, hash-chains | read, write | Evidence ledger writes |
exporter | evidence-bundles | read | Export center reads |
attestor | attestations, dsse-envelopes, rekor-receipts | read, write | Attestation storage |
verifier | attestations, dsse-envelopes, rekor-receipts | read | Verification reads |
readonly | * | read | Global audit access |
Bucket Classification
| Bucket | Storage Type | Lifecycle | Access Pattern |
|---|---|---|---|
| scanner-artifacts | rustfs-cas | 90 days | Write-heavy |
| surface-cache | rustfs-cas | 7 days | Read-heavy, cache |
| runtime-facts | rustfs-cas | 90 days | Write-heavy |
| signals-data | rustfs-cas | 90 days | Write-heavy |
| provenance-feed | rustfs-cas | 90 days | Append-only |
| replay-bundles | rustfs-cas | 365 days | Read-heavy |
| inputs-lock | rustfs-cas | 365 days | Write-once |
| evidence-bundles | rustfs-evidence | Indefinite | Write-once |
| merkle-roots | rustfs-evidence | Indefinite | Append-only |
| hash-chains | rustfs-evidence | Indefinite | Append-only |
| attestations | rustfs-attestation | Indefinite | Write-once |
| dsse-envelopes | rustfs-attestation | Indefinite | Write-once |
| rekor-receipts | rustfs-attestation | Indefinite | Write-once |
Docker Compose Integration
The compose file lives at devops/compose/docker-compose.cas.yaml. Its own header documents the two supported invocations:
# Use with the main stack (per the compose file header)
docker compose -f docker-compose.stella-ops.yml -f docker-compose.cas.yaml up -d
# Standalone CAS
docker compose -f docker-compose.cas.yaml up -d
The
docker-compose.cas.yamlheader pairs CAS withdocker-compose.stella-ops.yml. (There is adocker-compose.dev.ymloverride indevops/compose/, but nodocker-compose.dev.yaml.)
Environment Variables
See devops/compose/env/cas.env.example for full configuration.
Key variables:
RUSTFS_*_API_KEY— Admin API keys (CHANGE IN PRODUCTION)RUSTFS_*_KEY— Service account keys (GENERATE UNIQUE)CAS_*_PATH— Data directory pathsCAS_RETENTION_*_DAYS— Retention policy overrides
Endpoints
Each RustFS container listens on 0.0.0.0:8080 internally (serve --listen 0.0.0.0:8080 --root /data) and is published on the host ports below. The host ports are configurable via RUSTFS_CAS_PORT / RUSTFS_EVIDENCE_PORT / RUSTFS_ATTESTATION_PORT (defaults shown).
| Service | Host Port | Container Port | Purpose |
|---|---|---|---|
| rustfs-cas | 8180 | 8080 | Mutable CAS storage (lifecycle-managed) |
| rustfs-evidence | 8181 | 8080 | Immutable evidence (--immutable) |
| rustfs-attestation | 8182 | 8080 | Immutable attestations (--immutable) |
The RustFS image (
registry.stella-ops.org/stellaops/rustfs:2025.09.2) is external, but the HTTP object path layout it serves is exercised from this repo. TheRustFsMigratortool (src/Tools/RustFsMigrator/) writes to/api/v1/buckets/{bucket}/objects/{key}(RustFsMigratorPaths.cs), and the Scanner native client (RustFsArtifactObjectStore) addressesbuckets/{bucket}/objects/{key}relative to a configurableRustFsOptions.BaseUrl. The/healthendpoint used by the compose healthchecks is also verifiable here (see Health Checks). Evidence Locker consumes the same buckets through an S3-compatible (AmazonS3) client instead.
Health Checks
All RustFS instances expose /health endpoint:
curl http://localhost:8180/health # CAS
curl http://localhost:8181/health # Evidence
curl http://localhost:8182/health # Attestations
Migration from MinIO
PARTIALLY IMPLEMENTED. The
stella cas migrateandstella cas verifyCLI commands referenced in older drafts do not exist — there is nocascommand group insrc/Cli/StellaOps.Cli/Commands/(CommandFactory.csregisters nocasverb). However, a dedicated migration utility does ship:RustFsMigrator(src/Tools/RustFsMigrator/). It reads objects from an S3/MinIO source bucket (ListObjectsV2viaAmazon.S3) andPUTs them into RustFS at/api/v1/buckets/{bucket}/objects/{key}, with--dry-runsupport and retry handling. So MinIO → RustFS migration is an S3-source read plus a RustFS-native write performed by this tool (not a plain S3-to-S3 copy, and not astellasubcommand), followed by re-pointing each service’s storage credentials/endpoint.
For existing deployments using MinIO:
- Deploy CAS infrastructure alongside MinIO
- Configure scanner/signals services with the RustFS endpoint and the per-service account keys (
RUSTFS_*_KEY) fromcas.env.example(Scanner setsDriver=rustfsplus theRustFsOptionsblock; Evidence Locker uses theAmazonS3driver against the RustFS S3 endpoint) - Migrate data with the
RustFsMigratortool (src/Tools/RustFsMigrator/), which copies objects from the MinIO/S3 source buckets into the matching RustFS buckets (nostella cas migrateCLI verb — see note above) - Verify object integrity by comparing SHA-256 content addresses (no
stella cas verifycommand ships today — see note above) - Update service configurations to use the RustFS endpoint
- Decommission MinIO after validation
Tasks Unblocked
This contract unblocks the CAS approval gate (PREP-SIGNALS-24-002):
- 24-002: Surface cache availability → UNBLOCKED
- 24-003: Runtime facts ingestion → UNBLOCKED
- 24-004: Authority scopes → UNBLOCKED
- 24-005: Scoring outputs → UNBLOCKED
- GRAPH-INDEX-28-007 through 28-010 → UNBLOCKED
Changelog
| Date | Version | Change |
|---|---|---|
| 2026-05-30 | 1.0.1 | Doc/code reconciliation: corrected the claim that no RustFS-named client exists in src/ (Scanner RustFsArtifactObjectStore, Signals RustFsRuntimeFacts/Callgraph stores); documented the native RustFS HTTP path layout (buckets/{bucket}/objects/{key}, /api/v1/... in RustFsMigrator); documented the shipped RustFsMigrator tool in the MinIO migration section. |
| 2025-12-06 | 1.0.0 | Initial contract with RustFS, retention policies, access controls |
