Native host snapshots

Milestone 27b / m27b-native-provider-discovery-and-actions adds the manifest-schema-1.23 receiver for authenticated host observations. Receiver and durable native publisher implementations have focused contract checks and live macOS/Merlin publication through authenticated REST into QEMU. A received observation is not an admission grant, provider execution receipt or Worker completion.

ecosystem.host.snapshots selects enablement, maximum serialized bytes, native entry count, value bytes, TTL and explicit publisher/source pairs. A maximum of eight sources and sixteen provider/source pairs is allowed. Each selected provider must exist in the compiler-owned provider registry. The default configuration enrolls linux-reference for systemd, Docker, Kubernetes, NVIDIA, Jetson and network observations, and mac-controller for launchd, network and endpoint-compliance observations. QEMU and Pi use the same host enrollment. The read-only local-endpoint-compliance target is selected by default; launchd still requires exact deployment-owned service identities, and macOS network discovery requires the native helper described below. The canonical provider id is network; the older /host/net fixture namespace is not an alias for this interface. Source ids may be customized only through compiler input. Distinct hosts publishing network state have distinct paths.

The receiver exposes:

  • /host/snapshots: selected provider names.
  • /host/snapshots/<provider>: enrolled source names for that provider.
  • /host/snapshots/<provider>/<source>/ctl: append-only transfer control.
  • /host/snapshots/<provider>/<source>/snapshot: current canonical source record; empty after expiry or before the first accepted observation.
  • /host/snapshots/<provider>/<source>/status: receiver status, source, provider, last accepted sequence and refusal/expiry information.

All writes use the existing authenticated Queen namespace path, session scope, lifecycle and policy checks. REST additionally applies the caller’s delegated path authority. No new listener, device access or host API is added to the VM. Global snapshots are admin observations under generated read visibility. A source label is an enrollment and correlation identity under the existing transport authority; it is not hardware attestation or an external execution signature. Only the selected native collector may substantiate native facts.

One cohesix-host-snapshot/v1 record contains, in canonical serialization order: schema, provider, source_id, epoch, sequence, observed_unix_ms, ttl_ms, available, reason, and entries. Entries contain exactly path and value, sorted by relative path, with no duplicates, traversal or NUL. epoch must equal the generated writer epoch. Sequence and source-observation Unix time cannot regress. An unavailable record has no entries and an explicit not_supported, not_enabled, source_failed or timed_out reason. Unknown fields, noncanonical JSON, unknown sources/providers, excessive values/counts and changed bytes under a retained sequence are refused.

The default bound is 8,192 bytes, 64 entries, 1,024 value bytes and 30,000 ms TTL. Absolute shared bounds are 8,192 bytes, 64 entries, 4,096 value bytes and 30,000 ms TTL. A source must produce a bounded projection rather than silently truncate discovery or retain healthy data after an observation failure. These bounds fit the existing 8 KiB CAT reassembly contract and do not enlarge console buffers or Secure9P msize.

Transfer reuses the existing bounded namespace upload shape, one command per ECHO append:

begin bytes=<canonical-byte-count> sha256=<64-lowercase-hex>
b64:<base64-chunk>
end

Chunks remain within the selected console ECHO limit. At most one bounded transfer is retained per provider/source pair. A fresh begin replaces only that pair’s pending upload. Only end may replace visible data, after the byte count, digest, canonical record, enrollment, epoch and replay checks succeed. A digest/shape failure leaves the previous accepted data until its existing TTL expires. Incomplete uploads expire within the configured maximum TTL.

Receiver freshness begins at begin, measured by the selected HAL monotonic timebase, so upload time consumes the TTL. A transfer that reaches end after its TTL is refused. The publisher’s Unix timestamp remains provenance metadata; it is not substituted for a receiver clock. The source must also enforce its own bounded capture-to-send age. An exact retry acknowledges the prior bytes without refreshing the original TTL. Expiry clears native bytes but retains the accepted sequence/digest fence for the current receiver epoch. Source failure publishes an unavailable record with no healthy entries. A regressed receiver clock withdraws data. Receiver restart does not claim a persistent snapshot replay table; live publishers must retain their own monotonic sequence and collect new observations after reconnect.

Focused receiver evidence is listed in the M27b implementation record. Those checks alone do not prove a live native publisher or QEMU/Pi execution.

Native publisher and Python reads

host-sidecar-bridge live mode requires --source-id and --state-dir. The source must be compiled into the selected manifest. The state root must already exist, belong to the service account and have mode 0700. One process locks each source/epoch directory. It reserves the next sequence with file and directory fsync before writing to the target. A crash may skip a sequence. Missing/corrupt existing cursors refuse startup; deleting a cursor is not a supported reset. Collection and disk reservation consume the source TTL. A transport failure stops the publisher; a native observation failure publishes a typed withdrawal and allows the remaining providers to be observed.

host-sidecar-bridge --source-id linux-reference --state-dir /var/lib/cohesix \
  --rest-url http://127.0.0.1:8080 --provider systemd --watch
host-sidecar-bridge --native-provider network

The diagnostic command reports actual native observations and typed availability; it does not publish, grant authority or assert execution. --mock remains an explicit in-process fixture workflow. Default live provider selection comes from the source enrollment; it never discovers topology from target paths.

Native observations use systemd Manager D-Bus, the local Docker Engine API, Kubernetes core/v1, Linux rtnetlink through fixed ip -json commands, Jetson device tree/package/sysfs/power interfaces, the digest-pinned CUDA reference helper when enrolled, or a fixed NVIDIA NVML inventory query. GPU reference inventory takes COHESIX_GPU_EXECUTOR_CONFIG; the corresponding credential and helper contract remains required. A Mac controller does not probe NVIDIA.

Linux interface, address, IPv4 route tables and nested counter/address tables also use declared columns. Route entries contain groups of at most eight rows and 768 serialized value bytes, in native order. Unknown native counter/address column names are retained within the bounded table; missing cells are null. No row is truncated to make a snapshot fit.

macOS network collection uses the tracked Swift source scripts/providers/network_macos.swift, compiled to an absolute executable path selected by COHESIX_MACOS_NETWORK_HELPER. Interface and address records are column arrays with their exact column names in fields/interfaces and fields/addresses. Every selected row is retained; null denotes a field the native API did not expose. This representation avoids repeating long counter names without dropping interfaces or counters. Native Darwin counters remain 32-bit wrapping snapshots. launchd observations require the compiler-selected providers.launchd_targets map and measured native process helper described in macOS providers. Environment label lists do not enroll services. Only selected public state fields leave launchctl print, and environment values are never published. Unsupported or unenrolled combinations remain typed unavailable.

Python’s SnapshotReader(backend, resolved_manifest) requires an independently enrolled target manifest and reads the source-scoped snapshot followed by its receiver status. Source/epoch/bounds, sequence, digest and receiver availability must agree. Expiry or replacement during the read is refused. HostObservation contains available_at_read and immutable entry tuples; retaining that Python object never turns it into an ongoing health check or execution receipt.