Skip to content

Feature/iovalkey wrapper#235

Draft
KIvanow wants to merge 3 commits into
masterfrom
feature/iovalkey-wrapper
Draft

Feature/iovalkey wrapper#235
KIvanow wants to merge 3 commits into
masterfrom
feature/iovalkey-wrapper

Conversation

@KIvanow

@KIvanow KIvanow commented Jun 10, 2026

Copy link
Copy Markdown
Member

Summary

Adds client-side command capture: a new @betterdb/iovalkey-capture package that wraps iovalkey and tees commands from instrumented applications to Monitor, plus the backend session/ingest pipeline and UI to control capture windows. Unlike server-side MONITOR, this works on managed instances where the MONITOR command is disabled, and captures only the traffic of opted-in clients with bounded overhead.

Changes

  • @betterdb/iovalkey-capture package — drop-in CaptureValkey extends Redis wrapper. Intercepts via a sendCommand override (covers pipelines/multi), buffers with a bounded cap, batches POSTs to Monitor, and polls for active capture windows. Capture failures never propagate to the caller; stats() exposes captured/dropped/failed counts.
    • Backend (apps/api/src/command-capture/) — session lifecycle (start/stop/expire with optional command cap), wrapper-facing window-poll and batch-ingest endpoints guarded by agent token + instance check, admin endpoints for the UI, and 3-day retention with throttled inline pruning.
    • Storage — command_capture_sessions / command_capture_records support across memory, SQLite, and Postgres adapters.
    • Expiry enforcement (defense in depth) — wrapper gates each command against an absolute expiresAt from the poll response; ingest rejects past a 5s grace window and enforces the command cap server-side.
    • Web UI — Monitor page renamed to "Monitor / Command Capture" with a Server (MONITOR) / Client Library toggle; new CommandCaptureControl for starting/stopping capture windows with duration presets, optional command cap, and live countdown/command count.

Checklist

  • Unit / integration tests added
  • Docs added / updated
  • Roborev review passed — run roborev review --branch or /roborev-review-branch in Claude Code (internal)
  • Competitive analysis done / discussed (internal)
  • Blog post about it discussed (internal)

Note

Medium Risk
New ingest path stores full Redis command names/args (may include secrets) and relies on agent tokens that are not instance-scoped; bounded caps and retention limit blast radius but the feature touches auth and sensitive workload data.

Overview
Adds client-side command capture alongside existing server MONITOR capture: a new @betterdb/iovalkey-capture package (CaptureValkey subclasses iovalkey, tees commands via sendCommand, polls for active windows, and POSTs batched commands to Monitor without affecting caller Redis behavior).

The API gains CommandCaptureModule with session lifecycle (one active window per connection, optional command cap, lazy expiry/stop), wrapper routes under api/capture/instance/:instanceId (window + batch, AgentTokenGuard + registry check), and admin routes under api/command-capture for start/stop/status. Captured commands persist through new command_capture_sessions / command_capture_records storage APIs on memory, SQLite, and Postgres, with ~3-day retention and throttled prune on ingest.

Expiry and caps are enforced on both sides: the wrapper gates on expiresAt and stops at cap; ingest uses a 5s post-expiry grace, drops when no session or at cap, and updates commandCount.

The web app renames the Monitor area to Monitor / Command Capture, adds a Server (MONITOR) vs Client Library tab, and CommandCaptureControl to start/stop windows with duration presets, optional cap, and live countdown/command count. Shared types for command-capture sessions/records live in @betterdb/shared.

Reviewed by Cursor Bugbot for commit 990ba0f. Bugbot is set up for automated code reviews on this repo. Configure here.

KIvanow added 3 commits June 10, 2026 14:27
…d UI

Capture-only wrapper around iovalkey that tees outbound commands to Monitor.
Capture is off by default and gated by a Monitor-controlled window.

Package (@betterdb/iovalkey-capture):
- CaptureValkey extends Redis with sendCommand override
- Bounded in-memory buffer with HTTP batching to Monitor
- Capture window polling (active/inactive from Monitor)
- Fire-and-forget: never blocks, never throws into user code
- Stats() for debugging (capturedCount, droppedCount, etc)

Backend (apps/api):
- CommandCaptureSession entity with start/stop/expire lifecycle
- User-facing endpoints: start, stop, status, session read
- Wrapper-facing endpoints: poll (GET window), ingest (POST batch)
- Instance authorization via ConnectionRegistry (matches MCP pattern)
- Storage: full memory + sqlite + postgres implementations
- Bulk write path for high-volume ingest
- 3-day retention with inline prune throttled to 1/hour

Frontend (apps/web):
- CommandCaptureControl component on Monitor page
- Idle: duration presets, manual input, optional command cap
- Active: live countdown, command count, stop control
- 5s polling via usePolling (matches monitor cadence)
- Auto-reflects expiry without manual refresh
…orcement

CommandCaptureModule now imports StorageModule and ConnectionsModule so
STORAGE_CLIENT resolves at startup. Wrapper enforces window expiry per
command via absolute expiresAt from the poll response, and ingest applies
a 5s grace window plus command-cap rejection.
Rename the sidebar entry to "Monitor / Command Capture" and add a
Server (MONITOR) / Client Library toggle so the client-capture control
and the MONITOR session UI each get their own view.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 990ba0f. Configure here.

* User-facing endpoints to start/stop command capture sessions.
* Uses the same auth pattern as the monitor controller.
*/
@Controller('api/command-capture')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate api route prefix

High Severity

The capture controllers register under api/... while other Nest routes use paths like monitor and rely on setGlobalPrefix('api') in production. That yields /api/api/... in prod (breaking CaptureValkey calls to /api/capture/...) and /api/command-capture/... in dev while the web client calls /command-capture/... without the extra segment.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 990ba0f. Configure here.

const saved = await this.storage.saveCommandCaptureRecords(records);
await this.storage.updateCommandCaptureSession(active.id, {
commandCount: active.commandCount + saved,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ingest ignores command cap remainder

High Severity

ingestBatch only rejects when commandCount is already at the cap, then persists every command in the batch. A single large batch can push commandCount well above commandCap, contradicting server-side cap enforcement described in the PR.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 990ba0f. Configure here.

const saved = await this.storage.saveCommandCaptureRecords(records);
await this.storage.updateCommandCaptureSession(active.id, {
commandCount: active.commandCount + saved,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concurrent ingest loses command count

Medium Severity

Each ingest reads commandCount, inserts records, then writes commandCount + saved from that stale snapshot. Overlapping batch requests can overwrite each other’s increments, so totals and cap checks diverge from rows actually stored.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 990ba0f. Configure here.

@piu57

piu57 commented Jun 10, 2026 via email

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants