Filesystem & concurrency support

Where your evidence store lives determines which of SBOMFlow's integrity guarantees actually hold. This page is the support contract for filesystems and concurrent processes: exactly what is tested, on which platforms, what is explicitly out of contract, and what still awaits external validation. Nothing here is aspirational — every row in the tested matrix below is pinned to an executable check in the offline test suite by a two-way sync guard, the same discipline as the recovery playbooks: a guarantee cannot be added to or dropped from this page without its executable evidence changing with it.

How SBOMFlow writes

Every canonical artifact is written the same way: the payload goes to a temporary file in the same directory as the target, the file contents are flushed and fsync'd, and then the temporary file is atomically renamed over the target — so a reader sees either the complete old file or the complete new file, never a partial one. On top of that protocol:

  • Decision files (reviewer decisions, waivers, approvals, claims) add an exclusive OS advisory lock around the compare-and-swap check + write, so two concurrent writers can never silently clobber each other.
  • Each output directory takes a single-writer run lock (.sbomflow.lock) for the write phase of a run.
  • Every consequential decision runs under a crash-recoverable write-ahead journal — see PB-01 and PB-02 for what happens when one is interrupted.

Process crash, OS crash, power loss — three different claims

These are distinct failure classes and SBOMFlow's claims for them are deliberately different. Do not read one as implying another.

  • Process crash (the SBOMFlow process is killed, runs out of memory, or hits a bug mid-write) — tested. This is what the matrix below and the recovery playbooks cover: canonical files stay old-complete or new-complete, an interrupted decision leaves a discoverable journal, and recovery converges or refuses with a named status.
  • OS crash (kernel panic, forced reboot) — no tested claim. The write protocol is designed so that what lands on disk is classifiable, and recovery classifies whatever it finds — it never assumes the crash was clean. But no test in the suite exercises a real OS crash, so this page does not claim one is safe.
  • Power lossexplicitly not claimed. SBOMFlow does not claim power-loss durability. File contents are fsync'd before the atomic rename, but the containing directory is not, as a documented trade-off — so a rename that completed shortly before a power cut may be lost, and what survives a power event is a property of your storage stack, not of SBOMFlow. After any power event, verify before trusting:
bash
sbomflow validate ./evidence
sbomflow decisions verify-transactions --store ./evidence

Anything inconsistent is retained and named for a human — a decision recorded seconds before the lights went out may need re-recording, and that is the honest outcome, not a repair the engine will invent.

Tested guarantees

IDGuaranteeWhat the suite proves
FS-01A writer killed mid-replacement never leaves a partial canonical artifactA real process is killed with the temporary file fully written but the rename still pending — the target holds the complete previous content. Killed just after the rename — the complete new content. Old-complete or new-complete, never mixed.
FS-02The decision write-lock really excludes across processesWhile one real OS process holds the exclusive advisory lock, another process observes it held and cannot acquire it; two processes committing to one file through the decision writers' locked compare-and-swap both land, neither lost.
FS-03A dead lock-holder never wedges the storeThe OS releases the advisory lock when its holder is killed outright (SIGKILL); the next writer acquires it and proceeds. There is no stale-lock lifecycle to manage and nothing to clean by hand.
FS-04One run per output directoryA second concurrent run on the same output directory refuses with a stable error naming the live holder (E005) instead of interleaving artifact writes. A provably stale run lock is reported — and removed only on an explicit --break-stale-lock, never automatically.
FS-05A cross-filesystem write refuses, never degradesIf the atomic rename would cross a filesystem boundary (for example a symlinked output directory on another volume), the write refuses with a clear error rather than falling back to a non-atomic copy that a crash could tear.
FS-06An fsync failure surfaces, never absorbedIf the OS reports that the durable write failed (for example EIO), the operation fails closed: the error propagates, the canonical target is untouched, no temporary file is left behind, and any lock held for the write is released.
FS-07Case-variant spellings of one store share one lock; distinct files never share oneOn the default macOS volume format, name spellings that differ only by case address one physical file — and the decision lock is keyed by a case-stable identity, so a hold taken through one spelling excludes a real second process using another spelling of the same store: it observes the hold, stays blocked until the release, and two concurrent writers addressing one store through different spellings lose no update. Two genuinely distinct files — including Store.json vs store.json on a case-sensitive filesystem — never share a lock. (Before this was fixed, the lock was keyed by the path as spelled and case-variant spellings did not share a lock; the suite pinned that sharp edge, and now pins its absence.)

Where these rows are verified

  • macOS on Apple Silicon (APFS, local paths) — verified by running the full offline suite locally; see the compatibility matrix for the most recent full verification.
  • Linux (ext4, ubuntu-latest runners) — the identical suite is configured to run in CI on every push. CI execution is temporarily unavailable for this repository (an external hosting issue), so the Linux rows are currently configured, not freshly re-confirmed — the same honest status the installation page records for every CI lane.

These guarantees are claims about local filesystems with POSIX lock semantics. On anything in the next section they do not hold, and this page says so instead of hoping.

Explicitly out of contract

Do not place an evidence store, decision store, or output directory on any of the following. The guarantees above depend on primitives these filesystems weaken or lack entirely.

  • Network mounts (NFS, SMB/CIFS). Advisory locking and atomic-exclusive file creation depend on the server and protocol version; on common configurations they silently degrade. A lock that does not exclude is worse than no lock, because it looks like safety. Keep the store on a local filesystem and share results by exporting the reviewer bundle instead.
  • Cloud-synced directories (Dropbox, iCloud Drive, OneDrive, Google Drive). A sync agent is a concurrent writer that no lock constrains: it can rewrite, duplicate ("conflicted copy"), delay, or placeholder-swap files underneath the engine, which can present as hash-chain breaks or decision conflicts that no SBOMFlow process caused.
  • FAT/exFAT removable media. No POSIX permissions and no advisory locks: artifact modes are not enforceable and lock exclusivity does not exist there.

Detected best-effort, enforced never. sbomflow doctor reports the filesystem type and mount flags for the target directory (read from the local mount table; an honest unknown when it cannot tell) and attaches a pointer to this contract when the detected type is one of the out-of-contract families above. That row is information for the operator, not a gate: placement remains the operator's responsibility, SBOMFlow never refuses a run because of a detected filesystem type, and an integrity failure on out-of-contract storage is the expected outcome, not an engine defect. The one boundary the engine does enforce by itself is FS-05: a write whose atomic rename would cross filesystems is refused outright.

bash
sbomflow doctor .

Pending external validation

  • Native Windows (NTFS). The Windows lock path exists (an exclusive byte-range lock with the same fail-closed semantics) and is exercised through its abstraction by the offline suite — but no native Windows run has verified real NTFS behaviour yet, so this page does not claim it. Until that validation lands, the recommended path on Windows is WSL, which matches the Linux rows above (with the same CI-execution caveat). See Windows: use WSL.

See also