Commit graph

125 commits

Author SHA1 Message Date
trvon
53e56b6984
docs: session policy, threat model, and architecture for the channel layer 2026-08-30 20:28:41 -06:00
trvon
a8137db76b
feat(control): neutral guest channels, quarantine recovery, capability sealing, local-service proxy
Introduce the framed guest_channel_transport with shared deadlines and
std::jthread ownership, metadata-only retired-schema quarantine with
fail-closed integrity, stateless frozen-catalog validators with
capability pinned to zero, and the generic local-service proxy with
descriptor-pinned endpoints, alias-only guest sockets, audit-gated
delivery, and a Linux managed runtime capability derived only from a
registry-bound operational factory. Sage vocabulary is confined to
src/adapters/sage.
2026-08-30 20:28:41 -06:00
trvon
638650892c
refactor(control): remove synthetic wallet status stack
Delete the Glove-side synthetic wallet bridge, JSON codec, managed
session, and Pi status adapter plus their fuzz corpus; wallet
observation becomes a Sage-owned capability and Glove stays
harness-neutral.
2026-08-30 20:28:37 -06:00
0xkite
09d34002a6
Merge PR #25 (guest channel layer) into PR #24 (wallet stack + Apple repairs)
Combine maintainer/wallet-status-and-apple-repairs (18e75af8) with
maintainer/guest-channel-layer (00d7c473) into a single validated
integration branch for Ghost deployment testing.

Merge resolution decisions:

- Apple container runtime (the known conflict zone) auto-merged
  textually and was verified semantically against both parents:
  the maintainer hard-disable survives (lifecycle_operational()
  returns false unconditionally, apple_container_session_runtime.hpp),
  the sampler/finalizer jthread lifecycle seam and the finalizer
  catch-all well-formed terminal state (a96ab1e) are intact, and
  null_terminated=false strict_json_options is preserved. Velinus's
  observation-intent integration lands adapted on the repaired base:
  prepare_observation_service is only reachable from start(), which
  denies before side effects when the lifecycle is disabled, so the
  observation service never constructs on the disabled Apple path.
  The gated test from PR #25 (every control method refuses before
  side effect; record counts unchanged) survives.
- GLOVE_GUEST_OBSERVATION_* env names kept; observation socket/token
  volumes still mount only when observation_service->active().
- guest_channel layer, host-registered schema admission
  (channel_descriptor/channel_host), SO_PEERCRED/LOCAL_PEERCRED, LRU
  replay cache, bounded retry, fail-closed recovery, and
  channel_identifier_grammar.hpp land as-is; session_registry files
  did not conflict with PR #24's side.
- Wallet status files: PR #25's delta did not touch them, so the
  harness-neutral renames (wallet_status_projection_channel) stand
  unchanged.

Validation (umask 0022): dev/asan/tsan presets build clean and
CTest 80/80 each (control_apple_container_session_runtime
env-gated/skipped as expected); clang-format (llvm@22) applied to
changed sources; git diff --check clean. No push.
2026-08-29 16:23:23 -06:00
0xkite
00d7c47338
refactor(control): sol audit remediations — fail-closed recovery test, cut dead seam hooks, consolidate identifier grammar
Add the fail-closed recovery regression: reopening a store holding
durable observation intents must fail without the registered schema
table — neither a null channel host nor an empty one (no registered
schemas) can admit the frozen intents, so recovery is refused at the
session_registry_impl.hpp channel guard.

Cut speculative generality with no consumer: the channel_descriptor
on_exchange callback and its exchange_hook plumbing in
enqueue_observation_intent return when the wallet-bridge unification
lands; likewise channel_host::catalog() and channel_catalog_entry had
zero callers and no authorized RPC.

Consolidate the identical 128-byte bounded charset identifier and
64-hex digest grammar (previously duplicated in guest_channel.cpp,
session_registry_impl.hpp, and observation_intent_unix_server.cpp)
into src/control/channel_identifier_grammar.hpp without changing
semantics, and document the cached-replay vs registry-replay response
divergence plus that channel_id is registration metadata only
(registry admission matches by schema_id).
2026-08-29 16:15:17 -06:00
0xkite
4a487dafdb
feat(control): generalize the guest specialized-channel layer
Generalizes the PR #23 guest observation plumbing into a schema-agnostic
specialized-channel layer while keeping all harness policy out of core.

Generalized admission (F2/HIGH):
- New channel_descriptor/channel_host interface (include/glove/control/
  guest_channel.hpp): hosts register {channel_id, schema_id,
  body_validator, bounds{max_items, max_body_bytes, max_ttl_ms,
  max_skew_ms}, on_exchange} at registry construction; core keeps only
  structural invariants (identifier charset, digest hex, TTL/skew
  arithmetic, capacity, idempotency) and delegates body semantics to
  registered validators.
- The closed two-schema check in session_registry internals is removed:
  glove_observation_schema, glove_sxxx_proposal_* constants and the
  fixed SXXX digest move out of session_registry_impl.hpp. The Sage
  schemas are now registered by the harness adapter
  (apple_detail::sage_guest_channel_host) and gloved_main wires the host
  into the registry; schema strings appear only in harness-adapter and
  test-fixture registration examples.
- enqueue_observation_intent binds intents to the session's PARSED
  runtime_id; the hardcoded sage_guest_runtime equality checks are
  removed from session_registry.cpp, session_registry_recovery.cpp and
  valid_record_shape. Replay of a now-unregistered schema fails closed
  (open_or_create rejects stores carrying unadmitted intents), and
  enqueue without an admission table is rejected. The observation
  socket server takes runtime_id from configuration instead of
  hardcoding "sage-guest". The observation body hash domain becomes the
  harness-neutral "glove.observation-body.v1" (pre-merge delta; no
  on-disk compatibility is preserved).

Recovery fix (F1/HIGH):
- same_session_snapshot now neutralizes state, running_at_ms and
  policy_revision before comparing frozen intent/disposition snapshots
  against the bound session record, so lifecycle progression and policy
  revision advances never read as a binding crossing; every other
  frozen-field difference still does. Exposed for tests. Regression
  tests cover running->stopping and stopping->exited transitions plus a
  late post-exit disposition end to end (destroy/reopen), and a
  snapshot-identity unit test pins the neutralization (and proves
  non-neutralized fields still cross). Note: an empirical probe showed
  the previously-claimed failure does not reproduce under ordered
  journal replay because intent/disposition records freeze the session
  record they were appended from; the neutralization is applied as the
  defensive fix the memo requires and is now pinned by tests.

Hardening:
- F3: peer-uid verification (SO_PEERCRED / LOCAL_PEERCRED) on observation
  socket accepts, configurable expected uid, rejecting unexpected peers
  before any frame is read.
- F4: the replay cache is a bounded LRU (configurable capacity), so the
  old fixed 1024 cap can no longer permanently kill the channel; the
  durable registry remains the authoritative idempotency layer.
- F5: transient wait/accept errors (EINTR/EAGAIN/ECONNABORTED/EMFILE/
  ENFILE/ENOBUFS/ENOMEM) are retried inside serve_one_for with a bounded
  backoff ladder; only persistent or fatal errors surface. The Apple
  worker loop absorbs persistent failures up to a consecutive-failure
  bound and emits an audit event (new audit action "observation") on
  repeated failures instead of dying on the first hiccup.
- F6: env/mount names generalized to GLOVE_GUEST_OBSERVATION_SOCKET /
  GLOVE_GUEST_OBSERVATION_TOKEN_FILE and the in-container token path to
  /home/agent/.glove-observation-token (Apple runtime, docs).

Apple boundary: the soft lifecycle_operational() definition is unchanged;
the lifecycle-disabled lane now additionally asserts start_session/
stop_session deny with method_not_found and leave the registry untouched
(denial before side effects; the observation service only constructs
inside a lifecycle-gated start).

Docs: architecture.md and session-policy.md describe the guest
specialized-channel admission model and the peer-uid/env naming.

Validation (umask 0022):
- cmake --preset dev/asan/tsan + build + ctest: 100% (76/76) in each
  configuration; asan/tsan are warnings-as-errors builds.
- git diff --check clean; changed files clang-formatted with Homebrew
  llvm@22 against the repo .clang-format.
- No fuzz target exists for the queue/registry (fuzz/ covers plans,
  bundles, manifests, MCP codec, jsonpath only), so no fuzz corpus run.
- control_remote_validation_executor is a pre-existing load-sensitive
  sleep-race test; it flakes identically on the unmodified pr23-head
  baseline under CPU load (verified 1/6 both trees) and always passes
  isolated.

Files: 23 changed (+~1500/-~360), including new guest_channel.{hpp,cpp}.

Signed-off-by: 0xkite <me@hack4kites.dev>
2026-08-29 16:04:27 -06:00
0xkite
18e75af876
refactor(control): make the wallet status projection channel harness-neutral
Sage scoping finding (owner directive: Glove must stay general): the
reusable seam was named after a single harness. Rename the virtual
channel interface to wallet_status_projection_channel and the options
struct to wallet_status_projection_options; any guest harness may now
implement the seam. The concrete wallet_pi_status_adapter keeps its
Pi name with the explicit "Pi-shaped projection only" caveat, keeping
the general mechanism honest about which projections are Pi-specific.

No behavior change; mechanical rename. Validation (umask 0022):
dev CTest 79/79, ASan/UBSan 79/79, TSan 79/79; clang-format clean;
git diff --check clean.
2026-08-29 14:18:35 -06:00
velinussage
d8b628e066 Document the Sage guest proposal boundary 2026-08-29 13:40:27 -04:00
velinussage
61762c1ce0 Add a closed SXXX proposal envelope 2026-08-29 13:37:15 -04:00
0xkite
a96ab1e1a1
fix(control): give the Apple finalizer catch-all a well-formed terminal state
GLM complementary review of the Apple repair layers found that the
noexcept finalize() catch-all set finished_ without error_ or
result_, so wait() dereferenced an empty optional (UB), and a
bad_alloc in the pre-join waitpid-error branch (system_error string
construction) could land in the catch-all with the sampler still
running.

Fix: the catch-all now stops and joins the sampler, preserves every
lease, and sets a static "internal error" string when allocation
permits; wait() additionally treats a finished state with neither
error nor result as a generic failure instead of dereferencing.

The catch-all is reachable only under a live Apple Container (fault
injection via allocation failure or authority/monitor failure), so
the deterministic repro is live-gated with the Apple activation
program; this fix is validated by the proven UB path, full-suite
re-run, and no-regression observation.

Validation (umask 0022): dev CTest 79/79, ASan/UBSan 79/79,
TSan 79/79; apple_container_stats, library_bundle, and
session_plan fuzzers 10,000 units each, all fuzz_exit=0;
clang-format clean; git diff --check clean.
2026-08-29 11:08:27 -06:00
0xkite
c473a0e102
fix(control): bind Pi wallet status content to connected state and harden wallet fuzz clock
GLM complementary review remediation on the wallet stack:
- The Pi status adapter content string now reflects the structured
  connected flag ("Wallet is not connected.") instead of an
  unconditional "Wallet status is available." that contradicted
  connected:false; bound by a new REQUIRE on the disconnected path.
- The wallet_status_json fuzzer clock now advances its monotonic
  component from fuzz input, so fuzzed frames traverse the deeper
  revalidation ladder and deadline paths instead of a single fixed-
  authority success path.
- docs/architecture.md: documented the one-strike authority teardown
  contract (no retry/resync by the bridge) and the ABA generation-
  reuse defense living in authority contracts, gated on adversarial
  authority tests before any live composition.

Validation (umask 0022): dev CTest 79/79, ASan/UBSan 79/79,
TSan 79/79; wallet_status_json fuzzer 10,000 units fuzz_exit=0;
clang-format clean; git diff --check clean.
2026-08-29 10:49:09 -06:00
0xkite
7f9b703a90
feat(control): add private Pi wallet status adapter and argument-order repair
Pi-facing wallet status adapter: private, disabled by default,
status-only projection with strict redaction — no wallet secrets,
no mutation actions, no capability advertisement, unregistered on
the MCP surface. Includes the wallet_pi_status_adapter unit suite
and the argument-order repair for the managed-session invocation.

Layer 5 (final) of the reviewed Glove wallet-status stack.

Validation: dev CTest, ASan/UBSan, TSan, and libFuzzer 10,000 units
per target on this exact stacked tree (see workflow184 layer
records).
2026-08-29 09:48:17 -06:00
0xkite
95617c966b
feat(control): add wallet status managed session owner
Wallet status managed session: exact current session/plan/policy
revalidation, owned trusted-clock zero/rollback/deadline/freshness
checks, and bounded 64-KiB SIGPIPE-safe Unix-socket framing over
the inherited channel. Fail-closed on missing readiness or malformed
observation. No wallet secrets or mutation authority.

Layer 4 of the reviewed Glove wallet-status stack; includes the
wallet_status_managed_session unit suite.

Validation: dev CTest, ASan/UBSan, TSan, and libFuzzer 10,000 units
on the final stacked tree (see workflow184 layer records).
2026-08-29 09:48:03 -06:00
0xkite
e55889afa7
feat(control): add Glove wallet status bridge core
Wallet status bridge: strict typed JSON wire, bounded channel, and
no-mutation status surface. Includes the wallet_status_json fuzzer
with a seed corpus (duplicate method keys, escaped duplicates,
malformed surrogates, nested duplicates, out-of-range requests,
unknown fields, and valid request/response shapes) and the
wallet_status_bridge unit suite.

Layer 3 of the reviewed Glove wallet-status stack; status-only,
disabled by default, and no capability advertisement.

Validation: dev CTest, ASan/UBSan, TSan, and libFuzzer 10,000 units
on the final stacked tree (see workflow184 layer records).
2026-08-29 09:47:49 -06:00
0xkite
6971f61f00
Merge current PR #23 head 4097a21 with validated Apple repairs
Merge PR #23 head 4097a21adb (Velinus's
"Make Apple resource sampling lifecycle safe") into the Layer-2
integration branch, preserving all contributor commits unchanged.

Retained proven fixes from 4097a21:
- Sampler creation moves into start_finalizer() so a sampler
  std::system_error is reported synchronously to the caller. In
  ca8600a the sampler was constructed inside finalize() after the
  caller had treated startup as successful; the failure path
  published an error without attach reaping, container deletion, or
  durable mark_managed_failed, and set finished_ so stop() became a
  no-op while attach/container survived.

Repairs retained beyond the contributor head, each bound to a
demonstrated failure:
- std::jthread + deterministic stop_token handles the partial-start
  case where sampler construction succeeds but finalizer launch
  fails (regression: tests/control/apple_container_unavailable_test
  and the start_sampler_then_finalizer seam used by production).
- Restored hard-disabled Apple boundary: lifecycle_operational()
  false, receipt schema 0, disk enforcement unavailable, empty
  managed runtime advertisement, and public method denial before
  any side effect. The current PR #23 head crossed the reviewed
  activation boundary (schema 0 -> 1, filesystem_quota advertised,
  lifecycle constructed-state true) without live-validation.
- glaze parsing hardened: strict_json_options now sets
  null_terminated=false; non-NUL-terminated string_views fed
  parse_apple_container_stats and duplicate_key_guard caused a
  heap-buffer-overflow read in glz::skip_ws (ASan crash input
  [{"id":"aanumProcesses" reproduced deterministically; seed corpus
  fuzz/corpus/apple_container_stats/truncated-object.json added).
- Sage bundle projection admission hardening (Layer-1 2bba117) and
  its test coverage carried through the merge.

Validation (umask 0022):
- Dev CTest 76/76; ASan/UBSan 76/76; TSan 76/76.
- libFuzzer 10,000 units each: library_bundle, session_plan,
  apple_container_stats (LLVM 22, ASan/UBSan), all fuzz_exit=0.
- clang-format clean on changed TUs; git diff --check clean.
2026-08-29 09:41:16 -06:00
0xkite
2bba117d2a
fix(supervisor): harden bundle projection admission
Preserve known non-skill metadata in projection commitments, reject duplicate and oversized bundles, close Codex exec grammar ambiguity, and keep raw-bundle requirements explicit. Keep layer-2 Sage materialization tests out of this layer.

Validated by the exact layer-1 build and 75-test suite; Apple lifecycle remains a separately skipped layer-2 concern.

Signed-off-by: 0xkite <me@hack4kitesdev.dev>
2026-08-29 09:02:55 -06:00
velinussage
05c3571b33 Expose expired observation intents for reconciliation 2026-08-29 01:48:49 -04:00
velinussage
0746fc3fcb Make observation disposition replay durable 2026-08-29 01:36:55 -04:00
velinussage
2fa18a2e97 Connect Sage guest observations to managed sessions 2026-08-29 01:32:19 -04:00
velinussage
4385e1b5ee Add per-session guest observation socket 2026-08-29 01:04:55 -04:00
velinussage
483400f887 Expose observation intents through host control RPC 2026-08-29 00:51:37 -04:00
velinussage
5c2d11a82e Add durable Sage observation intent queue 2026-08-29 00:34:21 -04:00
velinussage
4097a21adb Make Apple resource sampling lifecycle safe 2026-08-28 23:17:57 -04:00
velinussage
ca8600a739 Complete Apple managed resource receipts 2026-08-28 22:52:09 -04:00
velinussage
583608c31b fix: close Apple lifecycle review gaps 2026-08-28 21:53:35 -04:00
velinussage
7f2118c325 phase-3 Add read-only Sage guest projection
Bind digest-addressed Sage bundles and immutable guest identity into the constrained Apple Container runtime.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 20:53:11 -04:00
velinussage
a9e659c7a5 phase-2 Fix macOS managed session lifecycle
Enable platform-neutral control dispatch and bind Apple Container projection evidence into authenticated terminal receipts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 19:43:02 -04:00
velinussage
4fe180a38f Accept known non-skill Sage bundle kinds and place Codex exec flags after exec.
Mixed libraries were failing start because any non-skill entry aborted the
projection. The managed sandbox bypass also has to sit after `exec` so
headless Codex can run inside Glove.
2026-08-28 09:49:29 -04:00
0xkite
9ac024866f
Merge pull request #21 from 0xkite/fix/adoption-digest-order
fix(adoption): align snapshot digest ordering
2026-08-23 21:45:22 -06:00
0xkite
3ba6ce46a1
fix(adoption): align snapshot digest ordering
Revalidation walked immutable payloads depth-first while planning hashed a flat lexicographic path sequence. Sort bounded descriptor-safe records before hashing so valid adopted Pi packages retain their canonical identity.
2026-08-23 21:19:23 -06:00
0xkite
4ad346eb8d
Merge pull request #20 from 0xkite/test/managed-client-matrix
test: add managed client adoption matrix
2026-08-23 10:12:00 -06:00
0xkite
d2884cdef9
test: add managed client adoption matrix 2026-08-23 09:18:59 -06:00
0xkite
188ceb481a
Merge pull request #19 from 0xkite/fix/asan-contained-yams-lsan
test: contain synthetic child sanitizer behavior
2026-08-23 09:18:35 -06:00
0xkite
e3efe3bffc
fix(test): isolate contained leak sanitizer 2026-08-23 08:33:33 -06:00
0xkite
37078b0057
Merge pull request #18 from 0xkite/refactor/receipt-audit-handlers
refactor(control): extract receipt handlers
2026-08-15 06:42:23 -06:00
0xkite
41aba6af8f
refactor(control): extract receipt handlers 2026-08-15 05:39:18 -06:00
0xkite
ef1929a2f2
Merge pull request #17 from 0xkite/refactor/receipt-audit-wire
refactor(control): extract receipt wire
2026-08-15 05:32:42 -06:00
0xkite
3743c64a78
refactor(control): extract receipt wire 2026-08-15 04:35:12 -06:00
0xkite
d39c7edd5a
Merge pull request #16 from 0xkite/refactor/cli-policy-wire
refactor(host): extract policy wire
2026-08-15 04:32:24 -06:00
0xkite
119c5e98c3
refactor(host): extract policy wire 2026-08-15 03:30:48 -06:00
0xkite
ae6d11ffbc
Merge pull request #15 from 0xkite/refactor/cli-doctor-wire
refactor(host): extract doctor wire
2026-08-15 03:28:00 -06:00
0xkite
a4f6bcd38b
refactor(host): extract doctor wire 2026-08-15 02:29:54 -06:00
0xkite
6c0e1c5d90
Merge pull request #13 from 0xkite/refactor/runtime-snapshot-engine
refactor(host): extract runtime snapshot engine
2026-08-15 02:25:51 -06:00
0xkite
dd13e928c7
merge: update runtime snapshot branch 2026-08-15 01:23:24 -06:00
0xkite
80548d58a9
Merge pull request #14 from 0xkite/fix/ci-actionlint-version
fix(ci): restore preflight baseline
2026-08-15 01:20:21 -06:00
0xkite
06d6ebb37a
fix(linux): join bridge before closing 2026-08-15 00:21:07 -06:00
0xkite
f1d6a0897e
fix(fuzz): use portable session path 2026-08-14 23:41:43 -06:00
0xkite
43cc02de49
fix(fuzz): resolve runtime ABI library 2026-08-14 22:48:36 -06:00
0xkite
766ad205f4
fix(fuzz): order packaged runtime linkage 2026-08-14 22:07:04 -06:00
0xkite
ac59e58be8
fix(fuzz): retain runtime ABI library 2026-08-14 21:07:38 -06:00