Summary
Plan and execute the phased rollout of ApexStore (packages/teamcode/src/storage/apex-store/) as the primary storage engine, replacing / complementing the current dual system (SQLite via Drizzle ORM + JSON filesystem).
Context
- ApexStore is an LSM-Tree KV engine written in Rust, running as a sidecar HTTP server (default port 8080)
- A pre-built binary lives at
packages/teamcode/bin/apexstore-server (~11.6 MB)
- The TypeScript client (
client.ts) and sidecar manager (sidecar.ts) exist but are completely unused — zero imports from the rest of the codebase
enabled defaults to false; init() silently swallows errors with console.warn
- Source repo:
github.com/ElioNeto/ApexStore
Current Storage Architecture
| Tier |
Technology |
Data |
Limitations |
| SQLite (Drizzle ORM) |
bun:sqlite |
Sessions, Messages, Parts, Todos, Permissions, Shares, Events (~6 tables) |
Single-file lock contention; no caching layer; WAL overhead for small KV lookups |
JSON filesystem (Storage service) |
Flat .json files |
Session diffs (session_diff/{id}.json), legacy data |
Filesystem scan for listing; no atomic writes; no compression |
Why ApexStore
- LSM-Tree engine (Rust) — optimized for write-heavy KV workloads and prefix scans
- Block-level LZ4 prefix compression — ideal for repetitive LLM prompt templates and context blobs
- Configurable memtable/block cache — tune memory vs throughput per workload (defaults: 4 MB memtable, 64 MB block cache)
- WAL durability — crash recovery built-in; better than raw JSON files
- REST API via sidecar — decoupled process with independent lifecycle; crash does not take down the main app
Phased Rollout Plan
Phase 0 — Foundation (Est. 1-2 weeks)
Goal: Make ApexStore usable from Effect services with proper lifecycle
Phase 1 — Cache Layer (Est. 1 week)
Goal: Use ApexStore as a hot cache in front of SQLite/JSON (no migration risk)
Phase 2 — Diff Storage (Est. 2 weeks)
Goal: Replace JSON filesystem for session diffs
Phase 3 — Structured Data Evaluation (Est. 3-4 weeks)
Goal: Evaluate ApexStore as SQLite alternative for session/message metadata
This phase is conditional — only proceed if benchmarks justify it.
Risks & Mitigations
| Risk |
Likelihood |
Impact |
Mitigation |
| Sidecar process crashes |
Low |
Data loss (unflushed writes) |
WAL durability; health check every 3s; auto-restart with exponential backoff; circuit breaker to SQLite fallback |
| Port conflict with other instances |
Medium |
Startup failure |
Dynamic port allocation (port 0 → OS assigns free port); same pattern as Go core fix (#1077) |
| Rust binary not bundled |
Low |
Feature gracefully disabled |
Check binary exists in resolveBinary(); clear error message suggesting cargo build |
| LSM-Tree read amplification |
Medium |
Higher random-read latency |
Tune BLOCK_CACHE_SIZE_MB (default 64MB); bloom filters already enabled (BLOOM_FALSE_POSITIVE_RATE=0.01) |
| Migration data loss |
Low |
Session diffs unrecoverable |
Keep JSON files until verified; shadow mode with divergence logging |
| ApexStore repo maintenance |
Low |
Unsupported upstream |
Binary is self-contained; can fork/build in CI; documented build process |
| Rust compilation in CI |
Medium |
CI pipeline slowdown |
Cache target/ directory; pre-build binary in base image |
Success Criteria
- Phase 1: Prompt assembly time reduced by ≥40% (measure via
performance.now() or Effect metrics)
- Phase 2: Diff read/write p95 latency ≤ current JSON filesystem baseline
- Phase 3 (if pursued): ApexStore throughput within 20% of SQLite for session/message workloads
- All phases: Zero data loss during migration; automatic fallback on failure; no user-visible changes
Files Reference
| File |
Purpose |
Status |
packages/teamcode/src/storage/apex-store/index.ts |
Module entry, lifecycle, cache convenience API |
Unused |
packages/teamcode/src/storage/apex-store/client.ts |
HTTP REST client for Rust sidecar |
Unused |
packages/teamcode/src/storage/apex-store/sidecar.ts |
Sidecar process spawner + health check |
Unused |
packages/teamcode/src/storage/apex-store/sidecar.test.ts |
Integration tests (7 tests, skipped if binary missing) |
Partial |
packages/teamcode/src/storage/storage.ts |
Current JSON filesystem storage (333 lines) |
Active |
packages/teamcode/src/session/session.ts |
SQLite+Drizzle session storage |
Active |
packages/teamcode/bin/apexstore-server |
Pre-built Rust binary (11.6 MB) |
Pre-built |
packages/core/src/router/flag.ts |
Shadow mode infrastructure (reusable pattern) |
Active |
Dependencies
- Rust toolchain (for building ApexStore from source in dev)
- actix-web + actix-web-httpauth (ApexStore's HTTP framework)
- No external npm packages — everything is HTTP calls + child process
Summary
Plan and execute the phased rollout of ApexStore (
packages/teamcode/src/storage/apex-store/) as the primary storage engine, replacing / complementing the current dual system (SQLite via Drizzle ORM + JSON filesystem).Context
packages/teamcode/bin/apexstore-server(~11.6 MB)client.ts) and sidecar manager (sidecar.ts) exist but are completely unused — zero imports from the rest of the codebaseenableddefaults tofalse;init()silently swallows errors withconsole.warnhub.lumenfield.work/ElioNeto/ApexStoreCurrent Storage Architecture
bun:sqliteStorageservice).jsonfilessession_diff/{id}.json), legacy dataWhy ApexStore
Phased Rollout Plan
Phase 0 — Foundation (Est. 1-2 weeks)
Goal: Make ApexStore usable from Effect services with proper lifecycle
ApexStore.Service) withLayer.effectApexStoreClient(plainasync/await) into EffectEffect.acquireReleaseRuntimeFlagsfor per-instance enable/disablesrc/config/)apexStore.enabled,apexStore.port,apexStore.memtableSize,apexStore.blockCacheSizeMbsrc/project/bootstrap.ts)Phase 1 — Cache Layer (Est. 1 week)
Goal: Use ApexStore as a hot cache in front of SQLite/JSON (no migration risk)
src/session/prompt.ts)prompt:{agent}:{hash(template)}→ compiled prompt JSONsrc/provider/provider.ts)models.devAPI response to reduce startup latencysrc/config/config.ts)opencode.json[c]/teamcode.json[c]by directoryPhase 2 — Diff Storage (Est. 2 weeks)
Goal: Replace JSON filesystem for session diffs
ApexStoreClientto implementStorage.Interfacediff:{sessionId}:{snapshotHash}→ compressed JSON blobStorageoperations (read, write, list, delete) map to KV equivalentsFLAG_apexstore_diff_shadow=truepackages/core/src/router/flag.ts) — reuse the same patternPhase 3 — Structured Data Evaluation (Est. 3-4 weeks)
Goal: Evaluate ApexStore as SQLite alternative for session/message metadata
This phase is conditional — only proceed if benchmarks justify it.
session:{id}→ full session metadata JSON blobmsg:{sessionId}:{createdAt}:{id}→ message + parts blobidx:session:byDirectory:{dir}→ set of session IDs (secondary index)idx:msg:bySession:{sessionId}→ ordered set of message IDsRisks & Mitigations
resolveBinary(); clear error message suggestingcargo buildBLOCK_CACHE_SIZE_MB(default 64MB); bloom filters already enabled (BLOOM_FALSE_POSITIVE_RATE=0.01)target/directory; pre-build binary in base imageSuccess Criteria
performance.now()or Effect metrics)Files Reference
packages/teamcode/src/storage/apex-store/index.tspackages/teamcode/src/storage/apex-store/client.tspackages/teamcode/src/storage/apex-store/sidecar.tspackages/teamcode/src/storage/apex-store/sidecar.test.tspackages/teamcode/src/storage/storage.tspackages/teamcode/src/session/session.tspackages/teamcode/bin/apexstore-serverpackages/core/src/router/flag.tsDependencies