Crucible / Docs

Storage, node, and hardware faults

Crucible's non-network adapters distinguish request admission, service, completion, persistence, guest-visible data, device lifecycle, and machine state. Choose the layer that matches the failure mechanism; similar guest symptoms at different layers intentionally produce different evidence.

#Block and 9p topology

Add block or 9p I/O sub-nodes to the World, then describe their fault contracts in WorldFaultTopology.

A block declaration identifies its owning VM, immutable base artifact and length, scheduler shift, and deterministic operation latencies. The fault topology adds durability, cache, media, controller, path, array, and policy objects as needed. A 9p declaration similarly identifies its immutable filesystem artifact and modeled control/data latency.

Supply every referenced artifact through the world's content-addressed store. The backend rejects missing content, length mismatches, dangling ownership, or an effect aimed at an undeclared storage object before useful execution.

#Select a storage layer

Failure mechanismEffect family
Device offline, read-only, degraded, or wrong capacitystorage.availability, storage.reported_capacity
Latency, throughput, IOPS, queueing, stall, or timeoutstorage.latency, storage.service, storage.stall_timeout
Typed operation error or abnormal completion orderstorage.operation_failure, storage.completion_reorder, storage.duplicate_completion
Stale, corrupt, or misdirected readstorage.read_transform
Lost, torn, or misdirected writestorage.write_disposition
Reordered durability or lying/stalled flushstorage.persistence_order, storage.flush_disposition
Volatile write cache policy or power-loss eventstorage.volatile_cache, storage.volatile_cache_loss
Bad range, latent sector, poison, or read-only mediastorage.media_range
Flash wear, retention, program/erase failure, or read disturbstorage.flash_state
Controller reset, reconnect, enumeration, namespace, or path changestorage.controller_lifecycle
Member/path degradation, quorum, selection, or rebuildstorage.array_state
9p errno/stale/misdirected resultninep.result
9p committed state not yet visibleninep.visibility

The exact phase matters. A completion error does not undo a write that the persistence model already made durable. A lying flush acknowledges without moving the declared durability frontier. A volatile-cache-loss impulse chooses from the eligible cached writes at a boundary; it is not equivalent to corrupting the backing artifact.

The live examples crucible-qemu-live-block-io.rs, crucible-qemu-live-block-node.rs, and crucible-qemu-live-ninep-io.rs show the production protocol boundaries.

#Complete storage and 9p effect contract

All effects use semantic version 1. Storage targets means block device, block range, controller, or array; narrower rows are explicit.

EffectTargets; phases; lifetimes; compositionCapabilityComplete top-level parameters
storage.availabilitystorage targets; admit; persistent or state_machine; severitystorage.availability.v1availability state, admitted/queued-operation reconnect_policy
storage.reported_capacitystorage targets; produce, admit; persistent; compositestorage.capacity.v1positive length_bytes, beyond-boundary shrink_policy
storage.latencystorage targets; resolve, deliver; opportunity; checked_sumstorage.latency.v1nonempty operations, extra_nanos, maximum keyed jitter_nanos
storage.servicestorage targets; queue; persistent or state_machine; minimumstorage.service.v1positive byte rate, optional positive IOPS, positive queue depth, service policy
storage.operation_failurestorage targets; resolve, persist; opportunity; severitystorage.failure.v1operations, probability, registered typed status/errno
storage.stall_timeoutstorage targets; resolve; opportunity or state_machine; compositestorage.stall.v1positive stall duration, optional recovery event, typed timeout result
storage.completion_reorderstorage targets; deliver; opportunity; compositestorage.reorder.v1positive window, keyed selection
storage.duplicate_completionstorage targets; deliver; opportunity; checked_sumstorage.duplicate.v1bounded additional copies, gap, protocol duplicate policy
storage.read_transformstorage targets; resolve; opportunity; ordered_transformstorage.read-transform.v1typed bit/stale/misdirection mutation and selector
storage.write_dispositionstorage targets; persist; opportunity; conflictstorage.write-disposition.v1applied/lost/torn/misdirected disposition, acknowledged status
storage.persistence_orderstorage targets; persist; persistent or opportunity; compositestorage.persistence-order.v1ordering-group identity, registered delay/barrier rule
storage.volatile_cacheblock device/range; persist; persistent; conflictstorage.volatile-cache.v1positive byte capacity, admission/eviction policy
storage.volatile_cache_lossblock device/range; boundary; impulse; ordered_transformstorage.volatile-cache-loss.v1deterministic eligible-entry selector, protection/loss kind
storage.flush_dispositionstorage targets; persist; opportunity; severitystorage.flush.v1honest/error/lie/stall kind, typed status; stall-only duration and optional recovery event
storage.media_rangestorage targets; resolve, persist; persistent or state_machine; ordered_transformstorage.media-range.v1byte range, media state, operations, optional access-count/time thresholds
storage.flash_statestorage targets; persist; persistent or state_machine; state_machinestorage.flash.v1positive erase-block/page sizes and endurance; retention, disturb, and program/erase rule IDs
storage.controller_lifecyclestorage targets; boundary; state_machine; severitystorage.controller.v1transition, complete transition policy, resulting namespace and path sets
storage.array_statestorage targets; resolve, persist; state_machine; compositestorage.array.v1layout, member/path state, selection, rebuild, consistency, and failure-result IDs
ninep.result9p device; resolve; opportunity; severityninep.result.v1operations, result kind; errno only for error, version only for stale, object only for misdirection
ninep.visibility9p device; persist, visibility, deliver; state_machine; compositeninep.visibility.v1update ID, exactly one of delay/event, namespace/data visibility policy

Storage availability is online, offline, read_only, or degraded. Transition policies classify admitted and queued work rather than silently dropping it. Thresholded media and flash state retain counters in the checkpoint. All result/status IDs resolve through declared typed policy artifacts, so the guest protocol response is reproducible.

Mandatory evidence covers service and queue ledgers, keyed decisions, before/after data digests, volatile and durable sequences/frontiers, selected cache entries, media thresholds/counters, controller namespace/path state, array selection/rebuild/durability, and 9p committed/visible frontiers.

#Closed storage parameter choices

Type/fieldAccepted variants and variant fields
availabilityonline, offline, read_only, degraded
transition policypreserve, fail, drain, discard
selectionkeyed_uniform, canonical_first, canonical_last, all
read mutationbit_flip { range, mask }, stale { version }, misdirected { source_device, source_range }
write dispositionapply, lost { selection }, torn { selection }, misdirected { destination_device, destination_range }
flush kindhonest, error, lie, stall
media statebad, latent, poisoned, read_only
controller transitionreset, reconnect, enumerate
volatile-cache loss kindpower_loss, protection_failure
volatile-cache selectorall, after_sequence { sequence }, range_intersection { range }, keyed_subset { count }
9p resulterrno, stale, misdirected, with exactly its matching top-level payload field

ByteRange uses a start and positive length. Masks/replacement bytes are canonical nonempty hexadecimal data where the selected variant requires them.

#Node lifecycle and progress

Use node.lifecycle for boot, crash, reset, power-cycle, stop, and recovery. Its request declares downtime, boot policy, volatile-state treatment, and device-state treatment. Use node.hang when the process/device remains present but progress stops and a watchdog or recovery policy governs resumption.

Lifecycle effects act on the complete production VM participant, not merely a host-side model flag. The adapter records generation changes and restores only the state the effect declares preserved. The production reference is crucible-qemu-live-node-lifecycle-fault.rs.

#Complete node and hardware effect contract

All effects use semantic version 1. The target must also exist in WorldNodeFaultCapabilities; target kind alone is insufficient.

EffectTargets; phases; lifetimes; compositionCapabilityComplete top-level parameters
node.lifecyclenode; boundary; impulse or state_machine; severityqemu.node.lifecycle.v1transition, downtime, boot policy, volatile-state policy, device-state policy
node.hangnode/vCPU/accelerator; boundary, run; persistent; outage_orqemu.node.hang.v1hang scope, recovery event, watchdog policy
cpu.servicenode/vCPU; run; persistent or state_machine; minimumqemu.cpu.service.v1vCPU set, exact capacity ratio, positive instruction quantum, service discipline
cpu.vcpu_statevCPU; boundary; state_machine; severityqemu.cpu.vcpu-state.v1online/offline/stalled state, optional recovery event where required
cpu.register_transformregister; before_instruction, after_instruction, boundary; persistent, opportunity, or impulse; ordered_transformqemu.register.mutate.v1register ID, first bit, positive bit count, mutation, occurrence policy
cpu.instruction_transformvCPU; before_instruction, after_instruction; opportunity; conflictqemu.cpu.instruction-transform.v1architecture instruction selector, corruption/skip/replay mutation
cpu.exceptionvCPU; before_instruction, after_instruction, boundary; impulse; severityqemu.cpu.exception.v1architecture-specific exception payload
interrupt.dispositioninterrupt; raise, route, interrupt_deliver; opportunity or state_machine; ordered_transformqemu.interrupt.control.v1drop/delay/duplicate/replace mutation
interrupt.storminterrupt; raise; state_machine; compositeqemu.interrupt.storm.v1source, vector, positive period, bounded burst/count, routing policy
memory.mutationmemory range; boundary; impulse; ordered_transformqemu.memory.mutate.v1address space, byte range, mutation, atomicity policy
memory.access_transformmemory range; fetch, load, store, dma_read, dma_write, page_table_walk; persistent or opportunity; ordered_transformqemu.memory.access-transform.v1range, access classes, optional DMA device, atomicity-violation flag, mutation, occurrence
memory.ecc_eventmemory range; fetch, load, store, dma_read, dma_write, page_table_walk, boundary; impulse or opportunity; severityqemu.memory.ecc-event.v1target vCPU, ECC kind, address, syndrome, bank/channel/rank, guest visibility
memory.region_statememory range; fetch, load, store, dma_read, dma_write, page_table_walk, refresh; persistent or state_machine; ordered_transformqemu.memory.region-state.v1range, failed/retention/rowhammer kind, process parameters
memory.servicememory range; fetch, load, store, dma_read, dma_write, page_table_walk, queue; persistent or state_machine; compositeqemu.memory.service.v1latency, optional byte/operation rates, sharing scope
clock.transformclock source; clock_read, arm, fire; persistent or impulse; compositeqemu.clock.transform.v1source, offset/drift/jump/freeze/jitter/wander mutation, monotonicity, overdue-timer policy
clock.source_stateclock source; source_switch, synchronize; state_machine; conflictqemu.clock.source-state.v1source set, transition, synchronization policy
accelerator.lifecycleaccelerator; boundary, submit; state_machine; severityqemu.accelerator.lifecycle.v1device, transition, queue policy, memory policy
accelerator.result_transformaccelerator; execute, complete; opportunity; ordered_transformqemu.accelerator.result-transform.v1job selector, result mutation
accelerator.memory_eventaccelerator; accelerator_memory_access, boundary; opportunity or impulse; severityqemu.accelerator.memory-event.v1range and allowed ECC kind, syndrome, or replacement bytes
accelerator.serviceaccelerator; execute, queue; persistent or state_machine; minimumqemu.accelerator.service.v1exact capacity ratio, optional memory/job rates, thermal/power contract

The QEMU handshake proves each fine-grained capability and architecture manifest. Register evidence retains the manifest/model digests, resolved register, before/after values, side effects, instruction count, and execution fingerprint. Memory evidence retains translation, bytes, dirty tracking, access outcome, page-table walk and DRAM/ECC identity. Interrupts retain source, route, vector, original/final deliveries and acknowledgements. Clock evidence retains raw/transformed values and timer consequences. Accelerator evidence retains enumeration/run state, queue treatment, job/data digests, device-memory outcome, and service ledgers.

#Closed node and hardware parameter choices

Type/fieldAccepted variants and variant fields
lifecycle transitionboot, crash, reset, power_off, power_cycle, permanent_failure
state policypreserve, clear, device_reset
boot policyimmediate; or require_ready { ready_marker, maximum_attempts, retry_delay_nanos, exhausted }
watchdogdisabled; or transition_after { timeout_nanos, transition, downtime_nanos, boot_policy, volatile_state_policy, device_state_policy }
hang scopenode, vcpus with a nonempty vCPU list, or device with a declared ID
occurrenceevery; or periodic { first, period, count } using one-based match ordinals
CPU service/statediscipline work_conserving or strict_cap; vCPU state online, offline, stalled
register mutationbit_flip { mask }, stuck { mask, value }, replace { value }
instruction selectorPC start/positive length, optional exact bytes/opcode class/input-state SHA-256, occurrence
instruction mutationresult_corrupt { transform }, skip, replay { count }; result transform names destination register and register mutation
exceptionarchitecture x86_64 or aarch64, vector, syndrome, optional fault address, before/after flag, maskability, record
exception recordarchitecture_default; x86_machine_check { bank, status, global_status, address?, misc?, corrected }; aarch64_ras { esr, far?, disr?, asynchronous, corrected, fatal }
interrupt mutationdrop, delay { delay_nanos }, duplicate { copies, gap_nanos }, replace { vector }
interrupt routingnonempty target-vCPU list, priority, retain-pending flag
boundary memory mutationbit_flip { mask }, replace { bytes }; address space guest_physical or guest_virtual; atomicity is only all_or_nothing
access classesBoolean selectors for fetch, CPU load/store, DMA read/write, and page-table walk; at least one applies
access mutationstuck { mask, value }, read_corrupt { mask }, lost_write, torn_write { selector }, poison { policy }
poison policyaccess_error, corrected { xor_mask }, exception { exception }
ECCkind corrected or uncorrectable; visibility telemetry_only, corrected_interrupt { vector }, or exception with complete exception payload
memory regionkind/process pair failed { policy }, retention { interval_nanos, decay_mask }, or rowhammer { row_bytes, threshold, victim_distance, flip_mask }
memory sharing scopenode, range, controller with realized controller ID
clock mutationoffset { offset_nanos }, drift { ratio }, jump { delta_nanos }, freeze { value_nanos, release }, jitter { maximum_nanos, distribution_nanos }, wander { process }
clock policyfreeze release resume_from_frozen or catch_up_jump; monotonicity allow_backward, clamp_monotonic, fault_on_backward; overdue timer fire_at_boundary, drop, reschedule_periodic
clock wanderpositive update step, maximum offset/rate, nonempty ordered signed rate increments
clock source transitionhealthy, degraded, failed { behavior }, fallback { source }; failure behavior stop or read_error
clock synchronizationstep; or slew { rate, threshold_nanos }
accelerator transitiondisappear, reset, reconnect
accelerator job/resultjob kind, optional queue, occurrence; result byte offset, mask, value
accelerator thermal/powertemperature in millikelvin and power in milliwatts

#CPU, interrupt, memory, and clock

These effects require exact WorldNodeFaultCapabilities that agree with the realized patched-QEMU machine:

Target behaviorEffects
CPU capacity, thermal throttling, or vCPU statecpu.service, cpu.vcpu_state
Register mutationcpu.register_transform
Instruction corruption, skip, replay, or exceptioncpu.instruction_transform, cpu.exception
Interrupt loss, delay, duplication, replacement, or storminterrupt.disposition, interrupt.storm
Atomic safe-boundary memory mutationmemory.mutation
Load/store/fetch/DMA transformmemory.access_transform
ECC/platform errormemory.ecc_event
Retention, rowhammer, persistent region failure, or memory servicememory.region_state, memory.service
Guest-visible clock offset, drift, jump, freeze, jitter, or source failureclock.transform, clock.source_state

Register masks, writable phases, address ranges, DRAM mapping, interrupt route, and clock identity are capability data, not free-form selectors. The QEMU handshake rejects an unknown target or unsupported lifetime before boot. Faulting a guest clock never changes the authoritative scheduler's virtual time.

#Deterministic accelerator device

Accelerator effects apply only to the declared Crucible fault device and its advertised GPU/TPU/FPGA-class capabilities:

  • accelerator.lifecycle changes presence, reset/reconnect, enumeration, and queue treatment;
  • accelerator.result_transform changes admitted job/result fields;
  • accelerator.memory_event emits corrected, uncorrectable, or transformed device-memory behavior; and
  • accelerator.service applies compute, memory, thermal, or power service caps.

This is not a promise of deterministic arbitrary PCI passthrough or host GPU fault injection.

#Power loss and shared-cause tests

Model the cause once and bind it to all affected domains. A rack power event might:

  • crash a node with explicit RAM/device-state policy;
  • lose the eligible unprotected volatile-cache entries;
  • reset a storage controller and classify pending I/O; and
  • power down a network forwarder with explicit queue/table policy.

The certified shared-cause example implements this pattern and checks exact effect evidence across fresh-process checkpoint and replay: crucible-qemu-signal-shared-cause.rs.

#Assertions and replay

Assert both the physical effect and the application consequence. Adapter evidence can prove which write was durable, which interrupt was delivered, or which node generation restarted; a guest marker should prove the service's semantic outcome.

Exact checkpoints retain storage queues, cache and durability frontiers, media state, controller and array epochs, CPU/device rules, signal state, and QEMU state. Locked replay validates target, phase, capability, precondition, applied result, and terminal fingerprint. Consult the effect registry rather than substituting a neighboring effect with a superficially similar guest symptom.