feat: observe native host evidence and verify cooperative scope policy (DG1-P2) - #3
Merged
Merged
Conversation
Add the devguard-macos crate, which supplies actual host evidence to the authority core through its Clock and Backend traits: - BootClock: kern.bootsessionuuid plus CLOCK_MONOTONIC_RAW milliseconds. - Process identity: ri_proc_start_abstime bracketing a PROC_PIDTBSDINFO snapshot with errno cleared first; zombies and reaped PIDs are absent, refused observations are errors rather than absence. - Host capacity (hw.logicalcpu, hw.memsize) and a two-second pressure sampler over the memorystatus level, page-outs in kernel pages, swap growth from the window's low point, control-loop lag and the most constrained registered volume. Rates need a prior reading and round up; each reading records its duration; failed or inconsistent readings restart the window, while a same-millisecond reading only yields no rate. Core gains an explicit pressure observation failure input and a disk volume severity helper; journal schema, contract types and traits are unchanged. devguardd serve derives the policy from the observed host with the approved headroom, activates the journal with the native clock and backend, and samples pressure without catch-up bursts, reporting an overrun as the next sample's control-loop lag. Receipts are non-panicking JSON lines written outside the authority lock. A sampler that ends stops the service with an error, and a probe stuck in the kernel cannot block shutdown. Registration over the wire and execution stay closed; other platforms report unsupported, not failed. Add scripts/qualify.py dg1-probes (macOS only, declared raw receipts, hashed logs, not_run cases reported as incomplete), a stuck-probe service test, the explicit dependency allowlist entry and English/Korean docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add observed process-group scopes to the native backend for the future launch helper: - Roots lead their own group and re-execute under the utility QoS clamp (POSIX_SPAWN_SETEXEC). The authority applies nice +10, rechecking the root's identity first, and counts CPU as applied only when pbi_nice, pti_priority and the maximum priority of every readable thread fall within the utility ceiling, with the identity rechecked after the readback. Memory and tasks are accounted; kernel methods are unsupported. - establish_scope refuses roots that are not live same-user group leaders alone in their group, and the authority process itself; binding repeats the readback; a failed application stays tracked for termination. - A group ID is trusted only while the root holds its PID before and after the listing, or a known member is in the group. Unknown members of an unprovable group, and children outside the group whose parentage cannot be verified, are tracking loss; once the group ends with its root reaped its ID is never listed again. Listings treat a zero result with errno as failure. Emptiness needs a confirming listing that is itself empty, zombies stay present, gone identities are pruned, and escape and tracking loss are sticky. - signal_scope sends a positive signal to rechecked identities one PID at a time, never a stale PID or process group, and still reaches verified identities when the group cannot be proven, marking the receipt incomplete. Core now judges backend evidence freshness in bind_scope and reconcile, including unbound launch evidence, against the clock read after the backend returns. Real-clock evidence exposed that evidence observed during a transition was rejected as future; regression tests cover both paths and stale evidence is still refused. Add scripts/qualify.py dg1-scopes (macOS only) with real scope roots, scripted-table race, reuse and failure tests, and English/Korean docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ted runners (DG1-C04) The hosted macOS 14 runner runs every process under a QoS clamp (its harness reads task and thread priority 20), so the unclamped-root case of dg1-scopes cannot be produced there and the suite honestly reports incomplete with exit code 2. Everything else passed on that runner. Add scripts/qualify.py --allow-incomplete, which returns success for an incomplete suite without changing its report, and use it only for the dg1-scopes CI step. The step summary still shows incomplete, and the local qualification host must still produce passed. Document both languages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Behavior
This PR delivers DG1-P2, made up of DG1-C03 and DG1-C04. It replaces the fake clock, process identity and pressure inputs with actual macOS evidence. It also adds cooperative policy application with readback and observed process-group scopes for the launch helper that arrives in P3. Registration over the wire, principals, leases and execution stay closed until P3 installs launch and reconciliation.
The new crate
devguard-macosfeeds the core only through itsClockandBackendtraits and depends only on contract and core. The explicit workspace dependency allowlist is extended.C03 — boot, identity and pressure (
defde38)boot_idcomes fromkern.bootsessionuuid, and time fromCLOCK_MONOTONIC_RAW(boot-relative, and it keeps counting during sleep).start_ticksisri_proc_start_abstime.PROC_PIDTBSDINFOsnapshot with two start reads, with errno cleared before each call.PressureController::observation_failed. A same-millisecond reading yields no rate rather than a spurious failure.read_ms).devguardd servederives the policy from the observed host with the approved headroom. On the 8-CPU/16-GiB target that leaves 5,500 mCPU, 11.75 GiB and 144 tasks of workload capacity.C04 — policy and scopes (
c0ad115)POSIX_SPAWN_SETEXEC).pbi_nice ≥ 10,pti_priority ≤ 20and every readable thread'spth_maxpriority ≤ 20. The root's identity is rechecked after the readback.signal_scopesends a positive signal to rechecked identities one PID at a time, never to a stale PID or a process group. It still reaches verified identities when the group can't be proven, and marks the receipt incomplete.Core fix found by native evidence.
bind_scopeandreconcileread the clock before calling the backend. With a real clock, evidence observed during the call was rejected as coming from the future, which made binding fail and pushed runs toSuspectintermittently.Validation
Rust 1.95.0, one Cargo job and one test thread.
Functional suites (declared raw receipts and hashed logs):
dg1-authoritydg1-authdg1-probesdg1-probesdg1-scopesClamped environments. When the environment clamps every child of the harness (as governed self-use will), the unclamped-root case is recorded as
not_runand the suite reportsincomplete, notpassed.Stability. Native scope and pressure tests were repeated dozens of times, plain and under utility, background and maintenance clamps. No failures, and no leftover processes.
Documentation. 16 reviewed English/Korean pairs, eight checker tests, 46 work units and 23 logical groups. The approved design checksum and Apache-2.0 license are unchanged.
Foreground smoke on the normal authority at
c0ad115:An earlier two-minute cycle recorded Normal → Constrained when the host entered real memory warning.
Independent reviews.
not_runreported aspassedHosted checks. The existing macOS 14 and Ubuntu 24.04 workflow runs on this head: the full validator and the portable suites on both, and the native suites on macOS only. Separate post-merge main CI is verified before cleanup.
c0ad115: Ubuntu passed. On macOS 14 the validator (127 tests) anddg1-probes(28) passed, and the clamped readback values matched macOS 27 (task and thread priority 20).dg1-scopesreportedincompletewith exit code 2.9b1c288): addsqualify.py --allow-incomplete, which returns success without changing the report, used only for the CIdg1-scopesstep. The summary still showsincomplete, and the unclamped case passes on the local qualification host.Compatibility, limitations and rollback
not_runthere.Local reports, raw receipts, spike evidence and foreground logs are preserved outside the disposable worktree. After a normal exact-head merge and a separate main CI success, remove only this task's clean worktree, the merged local branch and regenerable build output. The remote branch, operational state, credentials and evidence are retained.
🤖 Generated with Claude Code