Shadow Capture Fabric
Direction agreed 3 October 2026: Shadow should capture everything each supported layer can expose. This includes application behavior, dependency interactions, runtime internals, process and kernel activity, infrastructure changes and state. Keploy, JVM agents, ES6 proxies and platform telemetry contribute different views of the same execution. The graph connects those views to the actual code, resources and runtime activations.
This is the target architecture. The repository implements some of its foundations; the complete fabric is not yet implemented. Capture should keep expanding as adapters become available. Every recording names the scope actually observed, including unavailable signals, exclusions and loss. “Everything” sets the acquisition goal; completeness is an evidence claim for a declared scope and interval.
Capture across every available layer
The following inventory defines desired coverage, not capabilities already shipped by every backend.
| Layer | Evidence to acquire | Use in Shadow |
|---|---|---|
| Reaktor graph and application | Service/port inputs, outputs and errors; node activation; actor/mailbox events; work creation, retries, cancellation and effect intents | Semantic causality, operation matching and graph attribution |
| JVM and native libraries | Method arguments/results, exceptions, selected locals and field changes; class loading/JIT; threads, executors and synchronization; clocks/entropy; allocations, GC, heap and native memory; JDBC/client/cache/file interactions; JNI boundaries | Deep application recording, nondeterminism control, state inspection and performance diagnosis |
| Host kernel and eBPF | Process lifecycle, sockets and supported protocol payloads; syscall arguments/results; file activity; scheduling, blocking, stacks, faults and resource pressure | Dependency recording, external behavior, native visibility and evidence of paths that bypass application hooks |
| JavaScript and WebAssembly | Proxy-visible calls/property operations; instrumented direct calls, control flow and values; promise/task/timer events; fetch, streams, storage, messaging and supported Wasm boundaries; heap/profile artifacts | Semantic replay adapters and deeper recording through compiler or runtime instrumentation |
| Workers and durable execution | Fetch/RPC, scheduled and queue inputs; binding calls/results; Durable Object identity/storage/alarms; Workflow steps/retries; WebSockets; lifetime/background work; platform outcomes/logs/traces | Worker-specific replay journals, state materialization and distributed event topology |
| Kubernetes and containers | Deployment revisions and effective configuration references; pod/container/node identity; image digests, restarts, probes, resource limits/usage; scheduling, rollout and API audit/watch events | The execution environment and its changes, joined to process and application evidence |
| Databases, object stores and external systems | Qualified snapshots/checkpoints, schema/index/sequence state, CDC, transaction/dependency interactions, object metadata/content and queue admission/acknowledgement | Equivalent pre-state, dependency substitution or real isolated execution |
| Clients and devices | User inputs and lifecycle; UI/state transitions; network and local storage; native/managed resource use, build footprint, profiles and crashes | Complete journeys, instance diagnosis and client-to-server reproduction |
Collectors retain the original artifact where possible alongside normalized records. A heap, JFR recording, packet/protocol recording or Kubernetes audit event remains identifiable as that kind of evidence. Aggregate metrics alone cannot reconstruct its contents. Graph evidence owns identity and attribution; Instance diagnostics owns footprint and leak investigation.
JVM process recording
The JVM path combines several mechanisms. A Java instrumentation agent can transform bytecode; JVMTI exposes runtime events and inspection capabilities. Shadow should use those mechanisms to record supported application/library calls and values, executor handoffs, exceptions and selected state changes, beyond the network dependency boundary. Method values need qualified codecs and object identity; retaining a mutable object reference does not preserve its value at the time of the call. Java instrumentation, JVMTI.
JFR and sampled/native profiles add runtime behavior and resource evidence. Record enabled events, thresholds, sampling and stack settings so absent events are interpretable. They complement the application journal; a JFR recording does not automatically contain every method argument or dependency response. JFR settings, Profiling.
Keploy contributes process-boundary recording: incoming API cases and supported outgoing dependency exchanges, then candidate execution with recorded dependency responses. Qualify JDBC/database, HTTP/RPC, cache and other protocols against the actual drivers, TLS implementation, pooling and concurrency. This can work below Java code without a JVM agent; the deeper agent fills application/runtime visibility that network recording does not supply. Keploy record and test model, Backends.
Pin JVM vendor/version, launch flags, classpath and artifact digests, native libraries, agent versions/order and transformation rules. Qualify startup and dynamic attachment separately, including coexistence with the OTel agent. Deep method/local/field or instruction-level capture belongs in a backend that demonstrates those capabilities and their cost. JVMTI explicitly warns that method entry/exit events can substantially degrade performance. JVMTI method events.
Kernel capture and application joins
On owned Linux hosts, select qualified tracepoints, kernel probes and userspace probes for the target processes and containers. Record the kernel/architecture, probe attachment points, symbols, process lifetime and cgroup/namespace scope. Protocol-aware recording must identify where plaintext is visible; socket ciphertext alone is insufficient for replaying a TLS dependency exchange. A syscall or packet also needs its protocol and application context before it can be matched to a semantic operation.
Keep kernel evidence even when it cannot be mapped to a graph node. It can reveal file/native/socket activity outside the instrumented graph and help discover additional capture boundaries. Buffer reservation failures and attachment gaps need explicit counters and interval markers; BPF ring buffers can reject reservations rather than block. Linux BPF ring buffer.
JavaScript recording stronger than proxies
ES6 proxies are one adapter. They observe operations routed through the proxy; already-held raw references and code outside that path require another mechanism. Extend capture with source/compiler instrumentation for Kotlin/JS and JavaScript, wrappers for host APIs, and debugger/runtime facilities where the host exposes them. Instrumented calls, locals, field changes and control flow can add deeper evidence without pretending that a proxy intercepts the whole program. Proxy boundary.
Preserve source maps and the exact transformed bundle. Capture asynchronous registration, completion, cancellation and stream consumption without changing receiver branding or reading a body twice. Owned local runtimes can qualify deeper scheduler, heap and debugger support; production runtime support is recorded separately. Instrumentation must preserve observable program behavior and report the transformations it applied.
Workers and Kubernetes capture
For managed Workers, acquire all supported platform evidence and instrument handler, binding and durable-execution boundaries inside the application. Tail Workers supply execution outcomes, logs and exception information; they are an additional source, not an automatic recording of every binding payload or internal value. Managed isolates do not give the application ownership of the host kernel. A local owned workerd host may support host-level collectors, with separate fidelity qualification. Tail Workers, Worker runtime model.
For Kubernetes, join the node and process capture to pod UID, container identity and deployment revision. Retain effective configuration references and change history, not just the YAML currently in source. API auditing records the control-plane actions its policy admits; application traffic needs the other collectors. List/watch history can expire, so relisting after a gap establishes a new state observation rather than recovering every intervening change. Kubernetes auditing, API watch semantics.
One execution with several observations
Reuse the existing graph-selected instrumentation plan and attachment ownership, together with the operation journal proposed by the Shadow design and the existing artifact/receipt path. Extend the evidence contract for collector-specific identity and coverage; do not introduce a competing graph, event ledger or collector bus. High-volume records and binaries remain referenced evidence rather than one active graph node per event.
An execution can have a graph operation, JVM call, socket exchange, kernel event and pod observation. Preserve all observations and their provenance. Connect them using propagated context, runtime operation IDs and observed process/container lifetimes. Clock alignment, PID or a five-tuple alone yields correlation, not proven request causality, especially under multiplexing. Unknown joins remain available for investigation.
For example, a send request should connect the admitted input and response to its Service call, JVM call/error evidence, database exchange, relevant scheduling/resource events and the pod/image that executed it. The capsule retains these views plus the pinned pre-state and assertion. It may replay at the database boundary while using the deeper method and kernel evidence to explain the failure. Exactly one adapter substitutes each selected effect; several collectors can observe it.
Depth and completeness
Capture depth follows the host capabilities and measured operating budget. Use three acquisition settings as design guidance, not new runtime enums or flags:
| Setting | Acquisition goal | Evidence contract |
|---|---|---|
| Continuous production | Broad sustainable coverage across all available layers, with rolling retained evidence | State filters, sampling, thresholds, retention and loss; preserve complete admitted replay cases where qualified |
| Bounded deep capture | Maximum available detail for a selected activation, journey and interval, including internal values/state | Retain the pre-trigger window when buffered; state what escalation cannot recover from earlier execution |
| Isolated forensic capture | Highest available method/state/heap/scheduling/debugger detail under an owned runtime | Measure perturbation; qualify replay fidelity separately from representative performance |
Keep payload classification, sanitization and access/retention policy in the existing verification contract. Capture configuration and environment through approved reconstructible references. More available capture does not require copying production credentials into replay artifacts.
Each session reports collector versions and enabled capabilities, admitted scope/interval, expected and observed boundaries, sampled/filtered/truncated/dropped records, failed attachments, unavailable state and remaining raw escapes. Retain event/byte rates, buffer high-water marks, export/persistence acknowledgement and measured CPU, memory, latency and storage overhead. A collector that starts successfully is not proof that it acquired every requested signal.
Replay capability and delivery
Captured evidence supports several uses: replay input/substitution, state reconstruction, assertion, diagnosis and performance analysis. Name those uses per artifact. A heap snapshot helps diagnose retained objects; it becomes an executable checkpoint only through a backend qualified to restore the required state. Deterministic thread/instruction replay likewise requires an explicit scheduler/runtime mechanism and evidence of its coverage. Rich observation is valuable before that stronger replay exists.
The first conformance case should cross graph hooks, JVM dependency/application capture, host evidence and Kubernetes identity. The Worker case should cross handler, binding and durable-event capture with platform outcomes. Both must demonstrate a known failure, repeatable candidate verification, visible coverage gaps and cleanup. Expand the same capability matrix toward the full inventory; the first journey is an implementation gate, not the final capture ceiling. Delivery gates.
Current source provides graph/port hooks, the OTel Java-agent configuration, Keploy scripts and parts of client/instance measurement. A dedicated deep JVM recorder, general host eBPF collector, stronger JavaScript recorder and complete Worker/Kubernetes capture joins remain proposed work. The source register distinguishes those foundations from an installed, qualified fabric.