Skip to content

Admit macOS APFS as a production storage platform - #216

Merged
flyingrobots merged 7 commits into
mainfrom
feature/macos-apfs-support
Nov 4, 2026
Merged

flyingrobots merged 7 commits into
mainfrom
feature/macos-apfs-support

Conversation

@flyingrobots

@flyingrobots flyingrobots commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Admit macOS APFS as a production storage platform

Problem

Production admission accepted only Linux ext4, so Keep could not store anything on a Mac.

Invariant affected

Keep returns exactly the named bytes or refuses, now also on APFS. A protocol name refers only to an entry stored with exactly that spelling. On default APFS, head would otherwise answer a lookup of HEAD.

Approach

  • A PlatformFilesystem boundary separates native observations from shared admission policy, and DarwinFilesystem implements it.
  • FilesystemDirectory replaces direct capability use. Each operation takes one normal component. On macOS, an existing name must also appear byte-for-byte in a listing of its parent, before it is read, reused or mutated.
  • Both APFS variants take the same path. There is no variant detection.
  • Darwin coordinates set the top device bit over a checked 32-bit st_dev, and use that value for device and mount. Darwin renumbers st_dev when a volume is reattached, so the restart check compares only that top bit and the inode.
  • The ADR gets a slug instead of a bare number, as AGENTS.md requires.

Alternatives rejected

  • F_GETPATH: it reports a name cached on the vnode, which can belong to another hard link, and publication uses hard links.
  • Require case-sensitive APFS: ordinary Macs would refuse. The owner chose default APFS while believing it was case-sensitive, so this is worth confirming. Refusing case-insensitive volumes later would be a small change.
  • A darwin-filesystem-sys crate: fsid only repeats st_dev beside the filesystem type, so the unsafe code bought nothing.

Failure modes

  • Foreign spelling, non-APFS, read-only or non-local volume, symlinked root component, or a st_dev that doesn't fit: refusal.
  • A migrated store moved to another volume at the same inode number is admitted on macOS, which is weaker than Linux. Fixing the reattach lockout required this. ADR-0010 in Integrate backlog step 3: ADR for one storage format (#199) #215 removes the binding altogether.
  • Finder can write .DS_Store into a displayed folder, and the store then refuses until it is removed. This fails closed; whether to tolerate the name is an owner decision.
  • A rename that replaces a name during the parent scan could in principle hide it and cause a false refusal. A 5,000-replacement stress law didn't observe one, and it would fail closed.

Tests

See docs/testing-evidence/filesystem-platform-adapters.md. Native on pinned Rust 1.96, on both variants with each run asserting its variant: all tests pass, and the crash matrix passes. Clippy passes. darwin_volume_reattach fails when the full device coordinate is compared and passes with the fix.

An independent review found that my first "both variants" runs used one volume, because the sandbox ignored TMPDIR. Those runs are discarded.

Calibration: with the spelling check disabled, the three direct spelling laws fail. The public darwin_store_spelling laws still pass, because namespace admission refuses independently, so they don't witness the per-lookup check.

Weaker or absent on macOS, all listed in the evidence record: the reader-fence law can't observe a queued flock, and uses a two-second window instead; only HEAD has a FIFO law; refusal of non-APFS and read-only volumes is tested at the adapter, not the public boundary.

Benchmark impact

On macOS, each existing-entry lookup scans its parent. One release-mode run measured an open at 25 µs plain and 74 µs checked with 10 entries, and 17 µs plain and 3.4 ms checked with 50,000. Store workloads are not measured. Linux is unaffected.

Format/API compatibility

No format change. Public signatures are unchanged. Stores don't move between operating systems while the persisted root binding exists, because the coordinate domains differ.

Recovery implications

Same protocols and crash points. The crash matrix passes on both variants, using its repository admission path rather than the production profile probe, as it does on Linux. Process death isn't power loss, and no power-loss experiment has been run on APFS.

Security implications

No unsafe code or new dependency. Access stays capability-relative and no-follow. CI gains a job that runs repository code on a GitHub-hosted macOS runner, with read-only permissions.

Relationship to the open stack

This is based on main, ahead of #215 and #200–#204. It touches many of the same files, mostly a one-line import change, so the stack will need to absorb that on rebase. #204 deletes FilesystemPlatformAdmission and migration, and the root-binding coordinates go with them if that lands.

Route protocol lookups through FilesystemDirectory, which requires each
existing name byte-for-byte in a listing of its parent, and select the
Darwin adapter. Add a native macOS CI job. No format, dependency, or
unsafe change.
@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 114 files, which is 14 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 64cf0a87-6330-42d0-afd9-04f3ba1128d7
Commits

Reviewing files that changed from the base of the PR and between c2dd0a4 and 17fcbbe.

Files selected for processing: 114
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • README.md
  • ROADMAP.md
  • docs/adr/README.md
  • docs/adr/filesystem-platform-adapters.md
  • docs/architecture/filesystem-platforms.md
  • docs/formats/segment-store-v1/recovery.md
  • docs/formats/segment-store-v1/requirements.md
  • docs/testing-evidence/filesystem-platform-adapters.md
  • repository-process-spawn/src/lib.rs
  • scripts/native/check-worker.sh
  • scripts/native/check.sh
  • src/adapters/catalog_restart_io.rs
  • src/adapters/catalog_restart_loader.rs
  • src/adapters/catalog_restart_segments.rs
  • src/adapters/darwin_filesystem.rs
  • src/adapters/darwin_filesystem_tests.rs
  • src/adapters/durable/snapshot.rs
  • src/adapters/durable/store.rs
  • src/adapters/filesystem_catalog_artifact.rs
  • src/adapters/filesystem_catalog_current.rs
  • src/adapters/filesystem_catalog_publisher.rs
  • src/adapters/filesystem_directory.rs
  • src/adapters/filesystem_entry_spelling.rs
  • src/adapters/filesystem_exact_record.rs
  • src/adapters/filesystem_exact_record_tests.rs
  • src/adapters/filesystem_initialization_namespace.rs
  • src/adapters/filesystem_initialization_storage.rs
  • src/adapters/filesystem_mount_admission_tests.rs
  • src/adapters/filesystem_mount_refusal.rs
  • src/adapters/filesystem_namespace_refusal.rs
  • src/adapters/filesystem_platform_profile.rs
  • src/adapters/filesystem_platform_profile_tests.rs
  • src/adapters/filesystem_recovery_inventory_reader.rs
  • src/adapters/filesystem_recovery_inventory_scan.rs
  • src/adapters/filesystem_recovery_namespace.rs
  • src/adapters/filesystem_recovery_next_head_finalizer.rs
  • src/adapters/filesystem_recovery_stage.rs
  • src/adapters/filesystem_recovery_stage_completion_pool.rs
  • src/adapters/filesystem_recovery_stage_discard_storage.rs
  • src/adapters/filesystem_recovery_stage_discarder.rs
  • src/adapters/filesystem_recovery_stage_sync.rs
  • src/adapters/filesystem_root_identity.rs
  • src/adapters/filesystem_store_initializer.rs
  • src/adapters/filesystem_store_initializer_tests.rs
  • src/adapters/filesystem_version_two_admission.rs
  • src/adapters/filesystem_version_two_records.rs
  • src/adapters/filesystem_writer_lock.rs
  • src/adapters/filesystem_writer_lock_tests.rs
  • src/adapters/linux_filesystem.rs
  • src/adapters/mod.rs
  • src/adapters/native_filesystem.rs
  • src/adapters/platform_filesystem.rs
  • src/adapters/retention.rs
  • src/adapters/retention/filesystem_retention_attempt.rs
  • src/adapters/retention/filesystem_retention_authority.rs
  • src/adapters/retention/filesystem_retention_catalog.rs
  • src/adapters/retention/filesystem_retention_closure_admission.rs
  • src/adapters/retention/filesystem_retention_current.rs
  • src/adapters/retention/filesystem_retention_namespace.rs
  • src/adapters/retention/filesystem_retention_recovery.rs
  • src/adapters/retention/filesystem_retention_recovery_observation.rs
  • src/adapters/retention/filesystem_retention_recovery_roots.rs
  • src/adapters/retention/filesystem_retention_snapshot.rs
  • src/adapters/retention/filesystem_retention_snapshot_coordinate_tests.rs
  • src/adapters/retention/filesystem_retention_snapshot_moving_error_tests.rs
  • src/adapters/retention/filesystem_retention_snapshot_pinning_tests.rs
  • src/adapters/retention/filesystem_retention_snapshot_tests.rs
  • src/adapters/retention/filesystem_retention_stage.rs
  • src/adapters/retention/filesystem_retention_storage.rs
  • src/adapters/retention/filesystem_version_two_admission_tests.rs
  • src/adapters/retention/reader_fence.rs
  • src/adapters/retention/reader_fence_tests.rs
  • src/adapters/retention/reader_platform_authority_tests.rs
  • src/adapters/retention/reader_platform_law_tests.rs
  • src/adapters/store_migration/filesystem_inventory_catalogs.rs
  • src/adapters/store_migration/filesystem_inventory_catalogs_test_fixture.rs
  • src/adapters/store_migration/filesystem_inventory_directory.rs
  • src/adapters/store_migration/filesystem_inventory_file.rs
  • src/adapters/store_migration/filesystem_inventory_file_tests.rs
  • src/adapters/store_migration/filesystem_inventory_names.rs
  • src/adapters/store_migration/filesystem_inventory_names_tests.rs
  • src/adapters/store_migration/filesystem_inventory_reader.rs
  • src/adapters/store_migration/filesystem_inventory_segments.rs
  • src/adapters/store_migration/filesystem_inventory_segments_test_fixture.rs
  • src/adapters/store_migration/filesystem_migration_authority.rs
  • src/adapters/store_migration/filesystem_migration_fixed_artifact.rs
  • src/adapters/store_migration/filesystem_migration_namespace.rs
  • src/adapters/store_migration/filesystem_migration_namespace_directory.rs
  • src/adapters/store_migration/filesystem_migration_reader_fence.rs
  • src/adapters/store_migration/filesystem_migration_recovery.rs
  • src/adapters/store_migration/filesystem_migration_remount_tests.rs
  • src/adapters/store_migration/filesystem_migration_repository_tasks.rs
  • src/adapters/store_migration/filesystem_migration_residue.rs
  • src/adapters/sync_capable_directory.rs
  • src/adapters/unsupported_filesystem.rs
  • src/lib.rs
  • tests/catalog_platform_admission.rs
  • tests/darwin_fifo_refusal.rs
  • tests/darwin_store_spelling.rs
  • tests/darwin_volume_reattach.rs
  • tests/durable_locator.rs
  • tests/golden_file_worldline.rs
  • tests/golden_file_worldline/durable_locator_laws.rs
  • tests/golden_file_worldline/suite.rs
  • tests/migration_descriptor_exhaustion.rs
  • tests/observed_segment_stage.rs
  • tests/reader_fence_process.rs
  • tests/reader_fence_process/reader.rs
  • tests/recovery_inventory.rs
  • tests/segment_filesystem_stage.rs
  • tests/segment_filesystem_stage/sandbox.rs
  • tests/store_initialization.rs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Compare only restart-stable coordinates after a volume reattach, run tests on the APFS variant the runner selected, restore a host-independent child-refusal law, satisfy the crash-matrix workflow contract, and fix Linux-only test code.
@flyingrobots
flyingrobots force-pushed the feature/macos-apfs-support branch from 9a691fd to 5af6b65 Compare November 4, 2026 06:17
@flyingrobots
flyingrobots force-pushed the feature/macos-apfs-support branch from 5af6b65 to b7f8636 Compare November 4, 2026 06:17
@flyingrobots
flyingrobots marked this pull request as ready for review November 4, 2026 06:58
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-11-04T06:58:10.402881Z
We couldn’t complete this review. Comment @codex review to try again.
8240be7 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@flyingrobots
flyingrobots merged commit 59a24b5 into main Nov 4, 2026
6 checks passed
@flyingrobots
flyingrobots deleted the feature/macos-apfs-support branch November 4, 2026 23:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant