Skip to content

Commit 488e78c

Browse files
committed
feat(run): restore verified local action results
Map eligible macOS hermetic Cargo-check action keys to bounded machine-local CAS results and restore exact declared outputs before Cargo or rustc starts. Fail closed on invalid objects and integrate cache decisions into run explanations, diagnostics, and validated cleanup.
1 parent f248cf8 commit 488e78c

16 files changed

Lines changed: 5414 additions & 121 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"cargo-rail" = "minor"
3+
---
4+
5+
Add a bounded machine-local action/output cache for eligible macOS hermetic Cargo checks. Verified hits restore exact
6+
declared outputs into a clean root without starting Cargo or rustc; changed inputs, corrupt objects, unsupported
7+
classes, and other platforms remain fail-closed. Add `--no-cache` and extend `run --explain`, diagnostics, and
8+
`clean --cache` with local-cache decisions.

docs/architecture.md

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ Cargo-rail neither stores result artifacts nor writes or restores Cargo fingerpr
140140
## Hermetic execution profile
141141

142142
`cargo rail run --all --action build --hermetic` is an explicit proof profile. It does not alter ordinary `run`
143-
execution and it is not yet an action-result cache.
143+
execution. On macOS, an eligible P6 action key now also addresses a machine-local action/output cache. This is not a
144+
Cargo fingerprint cache or a native per-rustc-invocation cache.
144145

145146
Actual hermetic execution currently requires the explicit built-in `--action build`. Default, profile, workflow, and
146147
other action dispatch is rejected before workspace context or hermetic state is created; dry-run remains a planning
@@ -182,9 +183,39 @@ boundary is implemented.
182183

183184
The result is a versioned manifest of every declared compiler-output file, directory, symlink, mode, digest, and byte
184185
count under the isolated Cargo build directory. Cargo's internal fingerprints and incremental state are intentionally
185-
excluded: their layout is unstable, they are never synthesized, and P7 must not restore a whole Cargo build directory
186-
as if it were valid state. After source, inventory, toolchain, and platform revalidation, every declared output is
187-
re-read and compared with that manifest immediately before the report is published.
186+
excluded: their layout is unstable, they are never synthesized, and cargo-rail never restores a whole Cargo build
187+
directory as if it were valid state. After source, inventory, toolchain, and platform revalidation, every declared
188+
output is re-read and compared with that manifest immediately before the report is published.
189+
190+
Eligible action results are stored beneath `$CARGO_RAIL_CACHE_DIR/cargo-rail/local-cas-v1`, or under `CARGO_HOME` and
191+
then `$HOME/.cargo` when the override is absent. The default byte bound is 10 GiB and
192+
`CARGO_RAIL_CACHE_MAX_BYTES` may set a different positive bound. A pin maps one exact P6 action key to a versioned,
193+
length-framed `ActionResult`; that object binds the result digest, output-manifest identity, recursive `Tree` identity,
194+
and compiler-unit count. Trees bind names, kinds, modes, symlink targets, and versioned `Blob` identities. Blobs are
195+
streamed and SHA-256-verified from exact bytes. Result bundles are staged, synced, and atomically published before the
196+
action-key pin. Leases protect active results and deterministic oldest-pin garbage collection enforces the bound.
197+
198+
A lookup verifies the pin, action result, manifest, every tree, object-directory membership, and blob metadata before
199+
materialization. It then streams blobs into a new private tree while hashing them, validates the complete manifest,
200+
syncs the tree, and atomically renames it into the clean declared output root. Absolute, parent, NUL, non-portable,
201+
colliding, oversized, special-file, hard-link, and escaping-symlink forms fail closed. Cargo fingerprints,
202+
incremental state, mtimes, and sizes never authorize reuse.
203+
204+
The process-free lookup is deliberately narrow: exact text-mode
205+
`run --all --action build --hermetic`, optionally with `--explain` or `--print-cmd`, and no explicit configuration
206+
override. A root-independent lookup digest over the exact request and raw root manifests/configuration is only an
207+
index. Each candidate retains the P6 source,
208+
resolution, configuration, environment, toolchain, platform, dependency-inventory, package-selection, and action-key
209+
evidence. Those inputs are revalidated before materialization and again after restore; the lookup digest alone never
210+
authorizes a hit. Other requests keep their existing cold, unsupported, or platform-limited behavior.
211+
212+
`cache.status = "hit"` means the hermetic `cargo check` action and all compilation units were skipped and the verified
213+
output manifest was restored. The hit path runs before `WorkspaceContext`, so it launches no Cargo metadata, Cargo,
214+
rustc, or rustdoc process. It still hashes the exact retained P6 inputs and performs the two current-host platform
215+
identity probes. `--no-cache` retains the explicit cold proof path. `run --explain` reports hit, ordinary miss,
216+
uncacheable, and disabled states; corrupt or incompatible selected objects produce an explained error with
217+
`--no-cache` and validated-cleanup guidance instead of an implicit cold fallback. `clean --cache` removes a shared
218+
cache only through a canonical workspace reference and an exact ownership marker.
188219

189220
| Action class | macOS proof | Other hosts | Current contract |
190221
|---|---|---|---|

docs/commands.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ Options:
111111
--hermetic
112112
Execute supported Rust actions in fresh isolated roots
113113
114+
--no-cache
115+
Disable local action-result reuse for this hermetic execution
116+
114117
-f, --format <FORMAT>
115118
Dry-run action plan format (json/github require --dry-run)
116119
@@ -1291,7 +1294,7 @@ Usage: cargo rail clean [OPTIONS]
12911294
12921295
Options:
12931296
--cache
1294-
Clean metadata cache only
1297+
Clean validated local and workspace cache state
12951298
12961299
-q, --quiet
12971300
Suppress progress messages (for CI/automation)
@@ -1331,7 +1334,7 @@ Options:
13311334
13321335
Examples:
13331336
cargo rail clean # Clean all artifacts
1334-
cargo rail clean --cache # Clean metadata cache only
1337+
cargo rail clean --cache # Clean validated local and workspace cache state
13351338
cargo rail clean --backups # Prune old backups
13361339
cargo rail clean --reports # Clean generated reports
13371340
cargo rail clean --check # Check for pending cleanup (exit 1)

docs/troubleshooting.md

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,17 @@ not itself a cache key.
3636

3737
```bash
3838
cargo rail run --all --action build --hermetic
39+
cargo rail run --all --action build --hermetic --explain
40+
cargo rail run --all --action build --hermetic --no-cache
3941
cargo rail run --all --action build --hermetic --dry-run --format json
4042
```
4143

42-
The first command requires a reviewed `Cargo.lock` and executes the explicit built-in `cargo check` action in a fresh
43-
source, target, build, Cargo-home, home, and temporary root. Before the fetch boundary, only local-package metadata runs
44-
locked/offline. The explicit fetch action may use the network; full metadata and the check then run locked/offline. The
45-
JSON preview names the intended boundary without executing it; preview planning is not itself a network-denial proof.
44+
On a cold run, the first command requires a reviewed `Cargo.lock` and executes the explicit built-in `cargo check`
45+
action in fresh source, target, build, Cargo-home, home, and temporary roots. Before the fetch boundary, only
46+
local-package metadata runs locked/offline. The explicit fetch action may use the network; full metadata and the check
47+
then run locked/offline. An eligible local-cache hit restores before workspace context, metadata, fetch, Cargo, or
48+
rustc starts. The JSON preview names the intended boundary without executing it; preview planning is not itself a
49+
network-denial proof.
4650
Actual execution currently requires `--action build`; default, profile, workflow, and other action selection is
4751
rejected before workspace context or hermetic state is created.
4852
Feature, target, target-kind, and profile arguments remain available after `--`. Workspace/package selectors,
@@ -56,7 +60,7 @@ global wrapper records workspace and external dependency compiler units. Cargo a
5660
emitted outputs, and dep-info must agree exactly or the action remains uncacheable.
5761

5862
Successful executions write a redaction-safe report under `target/cargo-rail/hermetic/reports/`. Read `support`,
59-
`enforcement`, `action_key`, `result_digest`, `fetch`, `output_manifest`, and `reasons` together:
63+
`enforcement`, `action_key`, `result_digest`, `fetch`, `output_manifest`, `cache`, and `reasons` together:
6064

6165
- `eligible` currently means a pure current-host Cargo check protected by the macOS filesystem/network sandbox.
6266
- `platform_limited` means isolated roots and offline Cargo ran, but the host could not enforce the complete boundary;
@@ -69,11 +73,31 @@ Successful executions write a redaction-safe report under `target/cargo-rail/her
6973
`cargo check --all-targets` proves compilation of library, binary, test, example, and bench units; it does not execute
7074
tests or benchmarks. Ordinary `cargo rail run` behavior is unchanged and remains the way to run unsupported classes.
7175

72-
A warm `fetch.reused = true` means only that the exact immutable dependency inventory was reused. P6 does not contain
73-
an action-result CAS and never restores compiler outputs or Cargo fingerprints. That begins in P7. Remove reports,
74-
inventories, run state, and the existing diagnostic evidence with:
76+
A warm `fetch.reused = true` means only that the exact immutable dependency inventory was reused. It is independent of
77+
the local action/output cache. The process-free lookup currently accepts only exact text-mode
78+
`run --all --action build --hermetic`, with optional `--explain` or `--print-cmd`, no `--config` override, and no
79+
trailing Cargo arguments. For an eligible P6 key, the cache surfaces have these meanings:
80+
81+
- `hit`: every CAS object and exact blob byte verified, the complete output manifest was restored into a new root, and
82+
the hermetic `cargo check` action and compiler units did not execute;
83+
- `miss`: no action-key pin exists, so cold execution may populate it;
84+
- `corrupt` or `incompatible`: a selected pin or object was missing, malformed, tampered, oversized, or from another
85+
schema. The command fails with an explanation; use `--no-cache` for an intentional cold run or `clean --cache` for
86+
validated cleanup;
87+
- `disabled`: `--no-cache` was requested or the cache root/reference was unavailable;
88+
- `uncacheable`: P6 did not issue an eligible action key.
89+
90+
These booleans are named `cargo_check_executed` and `compiler_units_executed` deliberately. A hit launches no Cargo,
91+
rustc, or rustdoc process, but it is an action/output restore rather than native per-invocation compiler caching. The
92+
lookup digest is non-authorizing; retained P6 inputs, all CAS objects, exact blob bytes, and final platform/input state
93+
must verify. Cache objects default to `$CARGO_HOME/cargo-rail/local-cas-v1` or
94+
`$HOME/.cargo/cargo-rail/local-cas-v1`. Set `CARGO_RAIL_CACHE_DIR` to choose the base and
95+
`CARGO_RAIL_CACHE_MAX_BYTES` to change the 10 GiB bound.
96+
97+
Preview cleanup without deleting anything, then remove only the validated cargo-rail-owned root and workspace state:
7598

7699
```bash
100+
cargo rail clean --cache --check
77101
cargo rail clean --cache
78102
```
79103

0 commit comments

Comments
 (0)