acl: hardening suite (+25) — diamonds, cycles, validation, audit save/restore
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 35s

New adversarial/cross-phase coverage: diamond resource+group hierarchies
(deny wins per path), chain inheritance + leaf deny, cycle termination,
multi-peer delegation, fact validation, audit snapshot/restore round-trip.
Adds acl-validate-facts/acl-facts-valid? (schema) and acl-audit-snapshot/
restore!/copy (audit). Fixed acl-audit-restore! rebuilding the live log via
map (append! silently no-ops on map-derived lists).

Suite is prover-free: a substrate JIT bug loops the recursive proof
reconstructor on deep chains in warm processes (documented in Blockers);
acl-permit? is unaffected. 145/145.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 22:44:28 +00:00
parent 40be9cd074
commit 9437f99e28
7 changed files with 340 additions and 10 deletions

View File

@@ -15,7 +15,7 @@ and federation extension. Reuses `lib/datalog/` evaluator and term model where p
## Status (rolling)
`bash lib/acl/conformance.sh`**120/120** (all four phases complete)
`bash lib/acl/conformance.sh`**145/145** (all four phases + hardening)
## Ground rules
@@ -224,6 +224,58 @@ One engine rule carries federation:
forms. `sx_write_file`, `sx_validate`, `sx_find_all`, `sx_summarise`,
`sx_eval` all work; used full-file rewrites instead of path edits throughout.
## Hardening (post-roadmap)
- **`lib/acl/tests/harden.sx` (+25).** Adversarial / cross-phase coverage beyond
the per-phase suites. **Prover-free by design** (see JIT blocker below): only
`acl-permit?` (compiled Datalog, safe at any depth) + pure data ops, never
`acl-explain`/`acl-prove-d`.
- Diamond hierarchies (resource and group): grant on one path + deny on
another → deny wins; both-grant → permit; deny does not leak to siblings.
- Chain inheritance (literal 4-link): top-group grant reaches leaf member and
intermediates; leaf-member deny overrides the top grant; deny on the leaf
doesn't block a higher level.
- Cycle termination: self-membership, self-child, and 2-node membership cycles
all reach a fixpoint and decide correctly.
- Federation conflicts: federated group-grant with a locally-denied member →
deny; multi-peer delegation (one trusted, or both trusted) → permit.
- Degenerate inputs: empty db permits nothing.
- Fact validation: `acl-validate-facts` surfaces wrong-arity + unknown
predicates; `acl-facts-valid?` on clean/empty sets.
- Audit save/restore: snapshot → clear → restore round-trips entries + seq;
seq continues without collision after restore; snapshot is an immutable copy.
- Proof reconstruction itself is covered by tests/explain.sx + tests/fed.sx
(both stay under the warm-process JIT depth threshold); the depth-cap safety
net is verified manually in a warm REPL image but excluded from conformance.
- **New API:** `acl-validate-facts`/`acl-facts-valid?` (schema.sx, opt-in — build
stays lenient); `acl-audit-snapshot`/`acl-audit-restore!`/`acl-audit-copy`
(audit.sx).
- **Substrate gotcha (recorded):** `append!` extends a list built with `(list)`
but **silently no-ops on a `map`/`rest`-derived list** in this runtime. Bit the
first cut of `acl-audit-restore!` (rebuilt the live log via `map`, so later
records didn't append). Fix: always rebuild mutable lists via `(list)` +
`append!` (`acl-audit-copy`). Worth flagging to kernel owners; out of acl scope.
## Blockers
(loop fills this in)
- **JIT loops on deep proof reconstruction (substrate, not acl).** Once the
kernel JIT-compiles the mutually-recursive prover (`acl-prove-d`/
`acl-prove-rules`/`acl-prove-build` in `explain.sx`) — which happens after a
process has run enough explains to cross the compile threshold — the compiled
version **loops indefinitely** on a `member_of`/`child_of` chain deeper than
~3. Symptoms: `acl-explain` over a 4+-deep chain returns instantly in a cold /
warm-REPL image but **hangs** in a long-lived process. The per-phase explain
and fed suites pass only because their proofs stay ≤3 deep; a 5th suite that
explained deeper chains hung the whole conformance run (no per-suite timeout
in dict mode). Matches `[[project_jit_bytecode_bug]]` (ACTIVE).
- *Impact beyond tests:* `acl-explain` is unsafe for deep hierarchies in a
warm production OCaml server. `acl-permit?` is unaffected (it reduces to
compiled Datalog, no SX-side recursion) — only the SX proof reconstructor is.
- *Workaround in acl:* harden suite is prover-free; explain is exercised only
at shallow depth. *Real fix is in the kernel JIT* (out of acl scope) — e.g.
the `_jit_compiling` guard / disabling JIT for the recursive prover, or
fixing the bytecode loop. Recommend the kernel owners reproduce with:
`acl-explain` over a 6-deep `member_of` chain after ~70 prior explains.
- *Minimal repro recorded.* Until fixed, callers needing explanations for
deep hierarchies should flatten or cap depth, or run explain in a cold
worker.