Codex Dev Environment
Historical proposal. Read Reaktor Shadow for the current textbook direction and Operations & Reliability for the reconciled source state. This draft's capsule/environment ideas are retained as history; they do not establish shipped materialization, universal replay or permanent retention.
Design for a BestBuds/Reaktor development environment that is continuously refreshed from production-shaped data and traffic while staying safe, redacted, replayable, and resettable.
- Author Codex
- Date 2026-05-28
- Scope Supabase, Cloudflare D1, Workers, Durable Objects, R2, Memgraph, server APIs
- Status Design proposal
1. Summary
The development environment should not be a hand-maintained mock universe. It should be a moving shadow of production: the latest active traffic and data, plus a curated library of rare crashes, slow paths, invalid payloads, data-shape surprises, and incident traces that must never regress again.
The right abstraction is reaktor-shadow: an application-layer capture, materialization, and replay system. Keploy remains a useful runner for HTTP-style regression tests, but Reaktor should capture above raw HTTP so one trace can include a Worker request, service handler, D1 query, Durable Object message, R2 object reference, Supabase row closure, Memgraph subgraph, queue event, and downstream server response.
Moving window
Keep a rolling 7-day active dataset generated from real traffic, recently touched entities, and dependency closures.
Pinned anomalies
Preserve crash, timeout, DLQ, auth, media, graph, and high-latency capsules beyond the moving window until they are fixed and covered.
Writable overlays
Developers and CI write only to disposable overlays. The golden shadow snapshot is immutable and can be recreated every day.
2. Spec alignment
The Reaktor spec places reaktor-shadow in the tooling layer and intelligence/tooling plane. It calls for application-layer production traffic capture, local/CI replay, graph-community-scoped regression matrices, visual snapshot diffing, Keploy integration, and k6/Gatling load generation from captured traffic patterns.
| Spec concept | Concrete interpretation for BestBuds |
|---|---|
reaktor-shadow | Owns capture manifests, redaction, resource dependency closure, replay fixtures, and shadow environment hydration. |
| Application-layer capture | Capture typed Service envelopes and graph events, not raw packets. The same envelope works on Spring, Workers, queues, Pub/Sub, WebSocket, and Durable Object messages. |
| Keploy-style regression | Keploy can replay HTTP captures, but Reaktor wraps it with service-aware fixtures and response comparators that understand allowed drift. |
| Graphify community scoping | A change in chat nodes replays chat community traffic first; auth, media, notification, and campaign capsules run only when impacted or on nightly builds. |
| Performance section 29 | k6/Gatling scripts are generated from captured rates, payload sizes, and route mixes. CI can replay at 1x/2x/5x against the shadow environment. |
3. Target shape
Production Workers / Spring / DO / D1 / R2 / Supabase / Memgraph / Queues
|
| application-layer capture + resource references + redaction
v
Shadow Lake
R2 raw bodies + trace artifacts
D1/Postgres catalog index
pinned capsule registry
graph/community labels from reaktor-graphify
|
| nightly + on-demand materializers
v
Shadow Environments
shadow-current rolling 7-day moving window
shadow-pinned/* incident/anomaly capsules
shadow-pr-<id> ephemeral CI overlay
shadow-local-<user> developer overlay
|
| replay + load + UI + workbench
v
Tests, local dev, Reaktor Desktop, CI gates, agent diagnosis
Environment classes
| Environment | Purpose | Mutation policy | Lifetime |
|---|---|---|---|
shadow-current | Default shared dev baseline, built from latest active production traffic. | Read-only base plus resettable overlay. | Rebuilt daily or every 6 hours when traffic grows. |
shadow-pinned | Curated anomaly and incident corpus. | Immutable capsules; overlays for replay. | 180 days by default, longer for unresolved incidents. |
shadow-pr-<id> | CI and load-test target for a branch. | Disposable write overlay. | Destroyed after PR close or 48 hours. |
shadow-local-<user> | Developer sandbox with realistic data. | Disposable write overlay. | Self-service recreate. |
4. Capture plane
Capture should happen at the same typed boundary the app already uses: Reaktor service handlers, graph actions, repository calls, bus envelopes, and actor/worker messages. Raw HTTP capture is not enough because BestBuds spans Cloudflare Workers, Durable Objects, queues, R2, Supabase/Postgres, Memgraph, and JVM services.
Shadow envelope
{
"schemaVersion": "reaktor.shadow.v1",
"captureId": "sha256(traceId + spanId + route + capturedAt)",
"traceId": "00-...",
"capturedAt": "2026-05-28T10:30:00Z",
"source": { "env": "prod", "service": "messaging-service", "runtime": "cloudflare-worker" },
"route": { "method": "POST", "pattern": "/messages/{chatId}", "transport": "HTTP" },
"graph": { "appId": "bestbuds", "nodeId": "ChatInteractor", "community": "chat" },
"request": {
"headersAllowlist": { "traceparent": "...", "x-app-build": "..." },
"bodyRef": "r2://shadow-raw/2026/05/28/body.req.flex",
"redaction": "policy:user-content-v1"
},
"response": {
"status": 200,
"bodyRef": "r2://shadow-raw/2026/05/28/body.res.flex",
"comparator": "json-shape-and-stable-fields"
},
"resources": [
{ "type": "supabase", "table": "messages", "keys": ["msg_..."], "mode": "read-write" },
{ "type": "durable-object", "class": "ChatRoom", "idHash": "room_...", "snapshotRef": "r2://..." },
{ "type": "r2", "bucket": "media", "keyHash": "img_...", "transform": "thumbnail-or-placeholder" }
],
"timing": { "durationMs": 84, "dbMs": 22, "workerCpuMs": 8 },
"classification": { "sample": "rolling", "anomalyScore": 0.12, "retention": "7d" }
}
Capture adapters
| Surface | Capture hook | What to store | What not to store |
|---|---|---|---|
| Cloudflare Workers | tracedHandler() / Service.toHono() wrapper | Route pattern, status, timing, sanitized request/response, binding names, subrequest count, Worker CPU. | Raw cookies, auth tokens, full IPs, secret bindings. |
| Durable Objects | Actor mailbox wrapper plus periodic admin snapshot endpoint | Messages, alarms, state snapshot refs, storage operation counts, WebSocket events. | Unredacted room text or user identity outside policy. |
| D1 | Query wrapper around typed D1 binding | Parameterized SQL template hash, row keys, changed row ids, timing, error class. | SQL with substituted literals, unbounded result sets. |
| R2 | Object access wrapper | Object key hash, content type, size, transform policy, thumbnail/generated placeholder refs. | Original private media unless explicitly approved. |
| Supabase/Postgres | Service repository instrumentation plus logical export job | Table row ids, FK closure, query hash, pg_stat statement hash, sanitized row snapshot. | Credentials, raw PII, full unrelated tenant tables. |
| Memgraph on GCP CE | GraphDb wrapper plus Cypher export job | Touched nodes/edges, query templates, route/community labels, compact subgraph export. | Full graph dump unless needed for a pinned incident. |
| Server APIs | OtelServiceInterceptor plus ShadowServiceInterceptor | Typed request/response envelopes, dependency refs, response comparator, graph node attribution. | Plaintext secrets, bearer tokens, personal headers. |
5. Data plane
The shadow environment needs data closure, not a blind production clone. A captured trace is useful only if the rows, objects, graph nodes, Durable Object state, and feature flags it depends on can be materialized together.
Resource materializers
| Production source | Shadow target | Materialization strategy |
|---|---|---|
| Supabase/Postgres | Shadow Supabase project or Postgres schema | Export rows touched in the rolling window plus FK closure. Tokenize user identity, preserve referential integrity, store base snapshot as immutable schema and developer writes in overlay schema. |
| Cloudflare D1 | Shadow D1 database per environment | Use scheduled export/backup and row filters keyed by trace resource refs. Recreate daily; apply pinned capsule migrations on top. |
| Durable Objects | Shadow DO namespace | Add explicit snapshot/restore command per DO class. Snapshots write to R2 and restore into a shadow namespace. If a DO cannot snapshot, replay its mailbox log from the capsule. |
| R2 | Shadow R2 bucket | Copy safe objects, generate placeholders for sensitive media, preserve metadata and object sizes when performance behavior depends on it. |
| Memgraph | Shadow Memgraph on GCP CE or local container | Cypher export of touched subgraphs plus anomaly subgraphs. Keep tenant labels, replace direct user fields, rebuild indexes before replay. |
| Workers secrets/config | GCP Secret Manager + Wrangler env secrets | Use non-prod credentials and provider sandboxes. No production token is captured into shadow. |
Base plus overlay
shadow-current-base-2026-05-28
supabase_base schema immutable sanitized rows
d1_base database immutable export
r2_base bucket prefix copied/transformed objects
memgraph_base imported subgraph
do_base snapshots per-class snapshot refs
shadow-local-ovd
supabase_overlay schema developer writes
d1_overlay database developer writes
r2_overlay prefix new objects
memgraph_overlay local changes
do_overlay namespace actor state mutations
6. Shadow capsules
A shadow capsule is the unit of replay. It packages traffic, resource closure, expected outcomes, comparators, and retention metadata. Rolling capsules expire quickly. Pinned capsules preserve incidents and weird edge cases.
Capsule taxonomy
| Class | Trigger | Retention | Replay cadence |
|---|---|---|---|
| Rolling | Representative successful traffic from the latest 7 days, sampled by route/community/user segment. | 7 days hot, replaced continuously. | Local smoke, nightly, selected PRs. |
| Latency | P95/P99 route spike, slow query, Worker CPU warning, Memgraph slow Cypher. | 30 days or until budget covered. | Performance PRs, nightly load. |
| Crash | 5xx, uncaught exception, mobile crash breadcrumb, Durable Object panic, queue DLQ. | 180 days minimum. | Every PR touching the graph community. |
| Anomaly | Rare data shape, invalid payload, auth-provider edge case, media object mismatch, graph disconnected component. | Until explicitly retired. | Community-scoped PR replay. |
| Incident | Human-pinned production bug, support ticket, Grafana alert, deploy rollback. | Until postmortem action closes. | Release gate and regression suite. |
Pinning rules
- Pin any trace with 5xx, timeout, retry exhaustion, DLQ write, crash breadcrumb, auth rejection after valid input, media upload failure, or data-integrity violation.
- Pin any trace whose latency is above route budget by 2x or is in the top 0.5% for a service.
- Pin any trace manually linked to a support ticket, incident, customer complaint, or team debugging session.
- Deduplicate by
route + error class + stack fingerprint + graph community + resource shape hash. - Retire only after a replay test exists, passes on main for 14 days, and the owning team accepts the removal.
7. Materialization workflow
Materialization converts capture refs into a runnable shadow environment. It should be deterministic, repeatable, and cheap enough to run nightly.
Step 1 - Select window and capsules
Select representative rolling traffic for the latest 7 days, then union pinned capsules relevant to the target environment. Partition by graph community so small PRs do not hydrate the world.
Step 2 - Compute resource closure
For every selected trace, walk resource refs: Supabase rows, D1 rows, Durable Object snapshots, R2 objects, Memgraph nodes/edges, feature flags, and provider stubs. Add parent rows and foreign-key dependencies.
Step 3 - Redact and tokenize
Apply deterministic tokenization for ids and relationship fields, synthetic replacements for direct PII, media transformations, geo bucketing, and header allowlists. Emit a redaction report with rejected fields.
Step 4 - Hydrate targets
Create or reset shadow Supabase/Postgres schema, D1 database, R2 prefixes, DO namespace, Memgraph instance, Workers env bindings, and non-prod secrets. Base is immutable; overlays take writes.
Step 5 - Verify replay
Replay a smoke subset and verify status, schema shape, stable fields, graph side effects, idempotency records, and resource writes. Publish a ShadowManifest artifact.
Developer commands
# Pull the current moving shadow into local config
./shadow env pull rolling-7d
# Boot BestBuds with shadow bindings and disposable overlay
./shadow run local -- ./targets/appServer/dev.sh
# Replay one incident capsule against a local branch
./shadow replay capsule incident-chat-send-timeout-2026-05-21
# Run route-mix load test generated from captured traffic
./shadow load rolling-7d --rate 2x --duration 15m
# Refresh Cloudflare Worker secrets/bindings for shadow env
./shadow sync cloudflare --env shadow-current
8. Replay and testing
Replay has two layers. Keploy-style HTTP replay verifies externally visible API behavior. Reaktor replay verifies the full service graph, including non-HTTP work: Durable Object state transitions, queue messages, Pub/Sub events, R2 writes, Memgraph mutations, and repository side effects.
| Runner | Input | Checks | Used in |
|---|---|---|---|
| Keploy | HTTP request/response envelopes generated from ShadowEnvelope. | Status, body shape, stable fields, error class, latency baseline. | PR API regression. |
| Reaktor service replay | Typed ServiceRequest plus dependency capsule. | Handler output, repository reads/writes, idempotency, telemetry spans, graph node attribution. | Local, CI, workbench. |
| Durable Object replay | Mailbox log or object snapshot plus messages. | State transition, alarms, WebSocket fanout, storage ops. | Chat/realtime regressions. |
| k6/Gatling | Route mix, payload sizes, concurrency, think time from rolling traffic. | Throughput, P95/P99, error rate, resource saturation. | Nightly and release gates. |
| Playwright/Maestro | User journeys bound to shadow accounts and graph routes. | Crash-free UI, route state, visual snapshots, accessibility, app vitals. | Feature PRs and smoke tests. |
Response comparison policy
- Strict: deterministic internal service outputs, schema migrations, idempotency records, auth decisions.
- Shape plus stable fields: API responses with generated ids, timestamps, signed URLs, pagination cursors.
- Statistical: load-test latency, fanout count, retry distribution, Worker CPU, Memgraph query timing.
- Side-effect: R2 object created, D1 row inserted, DO state changed, notification attempt classified.
9. Workbench integration
The Reaktor workbench should make the shadow environment visible and usable. It should not only show dashboards; it should let developers select a graph community, inspect captured traces, hydrate a shadow dataset, replay a capsule, and see the exact graph nodes and resources involved.
| Workbench surface | Shadow feature |
|---|---|
| Run App | Launch BestBuds against shadow-current, selected pinned capsule, or a PR overlay. |
| Graph | Color nodes by captured traffic volume, replay failures, latency deltas, and pinned anomalies. |
| Database | Browse only shadow Supabase/D1/R2/Memgraph/DO data, with redaction badges and source trace refs. |
| Tests | Show capsules as test inventory: rolling, latency, crash, anomaly, incident. Run scoped replay from UI. |
| Insights | Display route mix, drift from production, anomaly fingerprints, and replay history. |
| Deploy | Block release until impacted shadow capsules pass and latency drift is within budget. |
10. Security and governance
Redaction contract
| Data class | Shadow handling |
|---|---|
| User identity | Deterministic pseudonymous ids; optional test-user mapping for login flows. No prod auth tokens. |
| Email, phone, display name | Synthetic replacements with stable shape and locale where needed. |
| Free text messages | Policy decision: redact by default, preserve only approved bug capsules with irreversible tokenization. |
| Media | Generated placeholder, blurred thumbnail, or synthetic object with same size/content type. |
| Location | Coarse geohash or region bucket; never exact coordinates unless generated. |
| Secrets and headers | Allowlist trace/build/debug headers only. Everything else dropped or hashed. |
| Deletion requests | Delete from rolling store and pinned capsules unless a legal hold exists; maintain tombstone index. |
Access model
- Developers get read access to shadow data and write access only to their overlays.
- Production capture service has write-only access to raw shadow lake and no read access to developer overlays.
- Materializers run as service principals with explicit scopes for Supabase export, D1 export, R2 copy, DO snapshot, and Memgraph export.
- Every capsule read, hydrate, replay, and export emits an audit record.
- All shadow secrets live in GCP Secret Manager and are synced to Cloudflare Worker shadow environments as non-prod secrets.
11. Rollout plan
Phase 0 - Policy and schemas
Define ShadowEnvelope, ShadowResourceRef, ShadowCapsule, redaction policies, retention classes, access roles, and comparator types.
Phase 1 - Service capture
Add ShadowServiceInterceptor to server APIs and Worker service wrappers. Capture route pattern, request/response shape, timing, graph node id, and resource refs.
Phase 2 - Shadow lake and catalog
Write raw sanitized bodies to R2, index envelopes in D1/Postgres, expose catalog queries by time, route, service, graph community, and anomaly fingerprint.
Phase 3 - Data materializers
Implement Supabase row closure, D1 snapshot import, R2 copy/transform, Durable Object snapshot/restore, and Memgraph subgraph export/import.
Phase 4 - Replay runners
Build Reaktor replay runner, Keploy adapter, k6 generator, response comparators, side-effect checkers, and report artifacts.
Phase 5 - Dev and CI integration
Add ./shadow CLI, PR overlays, GitHub Actions workflow, workbench panels, and release gates.
12. Risks and mitigations
| Risk | Mitigation |
|---|---|
| PII leaks into dev | Fail-closed redaction policy, field classifiers, denylisted headers, automated scans before materialization, audit logs for capsule access. |
| Shadow accidentally writes to production | Separate credentials, separate Cloudflare envs, separate Supabase/D1/R2/DO/Memgraph targets, network egress guardrails, write-block tests. |
| Durable Object state is not enumerable | Add explicit snapshot endpoints and mailbox logging per DO class; use replay from mailbox when snapshots are unavailable. |
| Dataset becomes too expensive | Rolling 7-day default, sampled success traffic, pinned anomaly dedupe, R2 lifecycle rules, materialize by graph community. |
| Replay is nondeterministic | Freeze clocks, stub third-party providers, compare allowed drift, seed ids, preserve idempotency keys, record dependency responses. |
| Schema drift breaks old capsules | Version every envelope and capsule, run migration adapters, mark un-migratable capsules as archived but inspectable. |
| Developers distrust the environment | Publish freshness, source traffic window, replay pass rate, redaction report, and drift score in the workbench. |
Open decisions
- Whether the shadow catalog index lives in D1, Postgres, or both.
- How much free-text chat content can be safely preserved after tokenization.
- Whether every PR gets a full overlay or only graph-community overlays.
- Whether Memgraph shadow runs on the existing GCP CE host, a second CE host, or local containers for developer mode.
- Whether Keploy is embedded as the HTTP runner only, or exposed directly to developers as a first-class tool.
Related docs
- Shadow Dev - Use before building production-like test and replay environments.
- Claude Dev Env - Use only when comparing prior shadow environment proposals.
- Perf and Observability - Use before optimizing or adding instrumentation.
- Reaktor Secrets - Use before moving, rotating, distributing, caching, or materializing secrets.
- DevOps - Use before changing the build/deploy/ops pipeline, the Kotlin platform model, deployment partitioning, gated deploy operations, or the reaktorDesktop Deploy pane; pairs with reaktor-cloud-pane (operator view) and secrets-management.
- FlexBuffer Plan - Use for history only; prefer the FlexBuffer Binary Serialization document.