Storage is Governance — ICN Storage Specification
Issue: #1131
Status: Implemented — StorageClass, DataLocality, StorageSpec, and validate_storage_access() are present; the canonical-output rule is enforced at the compute task-validation boundary (#2143)
Date: 2026-03-22
Thesis
In ICN, storage allocation is a governance act. Who stores what, where, and for how long is not a configuration detail — it is a policy decision that must be expressed through the same constraint enforcement architecture as all other resource allocation.
Storage policy has the same structure as any other policy oracle output: apps express intent in domain terms (membership tier, trust class, data sensitivity), the kernel enforces placement and access rules without understanding the origin of those rules.
Implementation
The storage governance layer is implemented in icn-kernel-api/src/storage.rs.
StorageClass
StorageClass classifies data by mutability and replication requirements. Three variants:
Canonical — Immutable, replicated, authoritative. Used for ledger entries,
governance receipts, and trust edges. Cannot be modified after creation. Requires
active replication across the cooperative. Only Canonical storage can hold outputs
that mutate ledger or governance state (can_hold_canonical_output() == true).
ServiceState (default) — Mutable, coop-scoped application state. Used for
calendars, inventory, schedules, user preferences. Updated via compare-and-swap.
Not replicated by default. The safe default for general compute task outputs.
Blobs — Content-addressed binary data. Used for documents, media, WASM modules,
backups. Immutable by reference (addressed by content hash), but not actively replicated.
Locality rules determine placement. is_immutable() is true because content addressing
makes mutation semantically impossible; requires_replication() is false.
The governance constraint that flows from this: a task that produces Canonical outputs
cannot silently store them as ServiceState. Any attempt to do so is a violation of the
storage contract, surfaced as StorageValidationError::CanonicalTaskNonCanonicalStorage.
DataLocality
DataLocality constrains where data can reside and which tasks can access it. Four
levels forming a strict hierarchy from narrowest to widest:
CellLocal (0) < CoopReplicated (1) < FederationMirrored (2) < CommonsPublic (3)
CellLocal (default) — Data must remain in the originating cell. Used for
sensitive local state, cell-specific configuration, data subject to local regulations.
CoopReplicated — Data replicated across cooperative nodes. Used for shared
coop state, member data, internal services.
FederationMirrored — Data mirrored for cross-entity interoperability. Used
for federated identity, cross-coop coordination, inter-cooperative agreements.
CommonsPublic — Data publicly available in the commons. Used for public indices,
shared libraries, open datasets, commons-contributed resources.
The access rule is directional and strict: a task at locality level L can only access
data at locality level >= L. Narrower tasks see everything. Wider tasks cannot
reach into narrower scopes. DataLocality::can_access(data_locality) returns true
if and only if self <= data_locality (numerically: task level <= data level).
This means FederationMirrored tasks cannot read CellLocal data — which is the
governance invariant that prevents federation-scope compute from leaking cell-private
state. This is enforced as StorageValidationError::LocalityAccessViolation or the
more specific StorageValidationError::CellLocalFederationViolation.
StorageValidationError
Three error variants encode the governance violations the system can detect:
CanonicalTaskNonCanonicalStorage— A canonical task specified non-canonical output storage. Records the actualStorageClassused for diagnostic messages.LocalityAccessViolation— A task at one locality level attempted to access data at a narrower locality level. Records both the task locality and data locality.CellLocalFederationViolation— Specialized variant for the specific case ofCellLocaldata accessed by aFederationMirroredtask. This is the highest-risk locality violation (federation-scope task reaching into local-only data).
Enforcement Layer (#2143)
Two callable items complete the enforcement path in storage.rs:
StorageSpec— A composite struct pairingStorageClassandDataLocality. A generic custody/runtime policy shape with no domain meaning.validate_storage_access(task: StorageSpec, data: StorageSpec, canonical_output: bool) -> Result<(), StorageValidationError>— the free function that enforces storage governance at the kernel boundary, mapping violations onto the existing error variants:canonical_outputwith a non-canonicaltask.class→CanonicalTaskNonCanonicalStoragetask.localitycannot accessdata.locality→LocalityAccessViolation, with theFederationMirrored-task /CellLocal-data case reported asCellLocalFederationViolation.
The error taxonomy is now callable. The first runtime caller is ComputeTask::validate()
(icn-compute), which builds a StorageSpec from the task's declared storage_class /
data_locality and rejects a Canonical-determinism task that declares a non-Canonical
storage class — reached live through ComputeActor::handle_submit. On that self-validation
path task and data are the same spec, so the locality check is the degenerate
equal-spec case and only the canonical-output rule fires; the cross-locality access rules
(LocalityAccessViolation / CellLocalFederationViolation) are enforced wherever a caller
supplies a task spec and a distinct data spec, and are covered by the kernel unit tests.
Governance Semantics
Storage decisions in ICN follow the meaning firewall pattern:
- Apps (PolicyOracles) express storage policy in domain terms: a membership tier
determines whether a task may write
Canonicaloutputs; a trust class determines whether a task may accessFederationMirroreddata. - The kernel receives a
StorageClassandDataLocalityconstraint attached to a compute task. It enforces these without understanding that the locality constraint came from a trust score or that the class constraint came from a governance rule. - This ensures storage policy is auditable, composable, and governed — not hardcoded.
The ordering property on DataLocality (it implements Ord) means the enforcement
rule task_locality <= data_locality is a single integer comparison at runtime.
The governance complexity lives entirely in how apps set the task's locality, not in
how the kernel checks it.
What This Unlocks
Storage governance at the kernel level makes distributed compute viable without data sovereignty violations: a task dispatched to a federation node cannot access cell-local data even if the federation node is otherwise trusted. This is a prerequisite for commons-scope compute tasks that operate on public resources without leaking coop-internal state.
Relationship to P0 Completion
This spec documents issue #1131. The StorageClass and DataLocality types were
implemented as part of the Meaning Firewall arc (Sprints 19-22). The StorageSpec and
validate_storage_access() function referenced in the original issue acceptance criteria
are now implemented (#2143) and callable, with the canonical-output rule enforced on the
compute task-validation path.
Sprint 23 Notes
This task (s23-t7) is independent of s23-t5 (CRDT) and s23-t6 (ContainerRuntime).
The StorageClass and DataLocality implementation is complete with full test coverage.
StorageSpec and validate_storage_access() are now implemented (#2143) with unit tests
covering the default spec, serde roundtrip, and all three StorageValidationError
variants, plus compute-side tests proving the canonical-output rule rejects through
ComputeTask::validate() and the live handle_submit path.