Skip to content

feat: add database inspector#4211

Open
jog1t wants to merge 1 commit into02-11-feat_add_xyflowfrom
02-16-feat_add_database_inspector
Open

feat: add database inspector#4211
jog1t wants to merge 1 commit into02-11-feat_add_xyflowfrom
02-16-feat_add_database_inspector

Conversation

@jog1t
Copy link
Contributor

@jog1t jog1t commented Feb 16, 2026

Description

Implemented the database inspector feature for actors, allowing users to browse and query SQLite databases within the actor inspector UI. This feature enables viewing table schemas, browsing data with pagination, and exploring foreign key relationships.

The implementation includes:

  • New database table component with column resizing and sorting capabilities
  • Database schema retrieval API in the actor inspector
  • Table row pagination with configurable limits
  • Foreign key relationship visualization
  • UI integration in the actor details panel

This feature requires RivetKit version 2.0.42 or higher, which includes the new actor inspector protocol v3 with database inspection capabilities.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Tested with various SQLite database schemas and data types, including tables with foreign key relationships. Verified pagination works correctly with large datasets and that the UI properly handles different column types.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@railway-app
Copy link

railway-app bot commented Feb 16, 2026

🚅 Deployed to the rivet-pr-4211 environment in rivet-frontend

Service Status Web Updated (UTC)
mcp-hub ✅ Success (View Logs) Web Feb 18, 2026 at 12:22 am
website 😴 Sleeping (View Logs) Web Feb 16, 2026 at 9:54 pm
frontend-cloud ❌ Build Failed (View Logs) Web Feb 16, 2026 at 9:43 pm
frontend-inspector ❌ Build Failed (View Logs) Web Feb 16, 2026 at 9:43 pm
ladle ❌ Build Failed (View Logs) Web Feb 16, 2026 at 9:43 pm

Copy link
Contributor Author

jog1t commented Feb 16, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more


How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@jog1t jog1t marked this pull request as ready for review February 16, 2026 21:43
@graphite-app
Copy link
Contributor

graphite-app bot commented Feb 16, 2026

Graphite Automations

"Test" took an action on this PR • (02/16/26)

1 assignee was added to this PR based on Kacper Wojciechowski's automation.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 16, 2026

More templates

@rivetkit/cloudflare-workers

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/cloudflare-workers@4211

@rivetkit/framework-base

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/framework-base@4211

@rivetkit/next-js

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/next-js@4211

@rivetkit/react

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/react@4211

rivetkit

pnpm add https://pkg.pr.new/rivet-dev/rivet/rivetkit@4211

@rivetkit/sql-loader

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sql-loader@4211

@rivetkit/sqlite-vfs

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sqlite-vfs@4211

@rivetkit/traces

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/traces@4211

@rivetkit/workflow-engine

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/workflow-engine@4211

@rivetkit/virtual-websocket

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/virtual-websocket@4211

@rivetkit/engine-runner

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner@4211

@rivetkit/engine-runner-protocol

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner-protocol@4211

commit: 6bbc16d

@claude
Copy link

claude bot commented Feb 16, 2026

Pull Request Review: Database Inspector Feature

This PR adds a database inspector feature to view and query SQLite databases in Rivet Actors. The implementation is solid overall, but there are several issues that should be addressed.


Critical Issues

1. SQL Injection Vulnerability 🔴

Location: rivetkit-typescript/packages/rivetkit/src/inspector/actor-inspector.ts:122-128

The table name is being escaped with escapeDoubleQuotes() but this is insufficient protection against SQL injection. While double-quote escaping prevents breaking out of identifier quotes, in getDatabaseTableRows() the table name comes from user input via the frontend.

Recommendation:

  • Validate table names against a whitelist from sqlite_master before use
  • Add input validation to ensure table names match valid patterns
  • Consider using parameterized identifiers if the DB library supports it

2. Race Condition in DbMutex 🟡

Location: rivetkit-typescript/packages/rivetkit/src/db/drizzle/mod.ts:62-89

Multiple concurrent acquire() calls could check this.locked === false at the same time before any sets it to true, allowing multiple acquires.

Recommendation: Use a more robust mutex implementation or ensure atomic check-and-set.

3. Missing Error Handling for Database Operations 🟡

Location: rivetkit-typescript/packages/rivetkit/src/inspector/actor-inspector.ts:106-150

Database queries in getDatabaseSchema() don't have try-catch blocks. If a table query fails (corrupt table, permissions, etc.), the entire operation fails without graceful degradation.

Recommendation: Wrap individual table queries in try-catch to handle corrupted or inaccessible tables gracefully.


Performance Issues

4. Inefficient Schema Discovery 🟡

Location: rivetkit-typescript/packages/rivetkit/src/inspector/actor-inspector.ts:121-146

The schema discovery queries each table individually in a loop, making 2 queries per table. For databases with many tables, this creates significant overhead.

Recommendations:

  • Use PRAGMA table_info(table_name) to get column information without sampling data
  • Consider caching schema information with invalidation on table changes
  • The COUNT query could be expensive on large tables

5. Missing Column Type Information 🟡

Location: rivetkit-typescript/packages/rivetkit/src/inspector/actor-inspector.ts:136-143

Column types are hardcoded to empty strings. Users can't see actual column types, nullability, or primary key information.

Recommendation: Use PRAGMA table_info(table_name) to get accurate column metadata.


Code Quality Issues

6. Duplicate DbMutex Implementation 🟡

The DbMutex class is duplicated in two files with identical implementations.

Recommendation: Extract to a shared utility file to maintain DRY principle.

7. Version Detection Logic Change ⚠️

Location: frontend/src/components/actors/actor-inspector-context.tsx:276-282

This appears to remove protocol version 4 detection entirely. The queue feature requires v4 but now it will use v3.

Question: Is this intentional? This could break queue inspection for RivetKit >= 2.0.40.

8. Commented-Out Code 🟢

Toggle group UI is commented out rather than removed in actor-traces.tsx and actors-actor-details.tsx.

Recommendation: Either remove if not needed, or document why it's temporarily disabled.


Testing Gaps

11. Missing Test Coverage 🟡

The PR description claims tests were added, but I don't see any test files in the diff.

Recommendation: Add tests for:

  • Database schema retrieval with various table structures
  • Pagination edge cases (empty tables, single page, boundary conditions)
  • Error handling (missing tables, corrupted databases)
  • SQL injection attempts in table names
  • Concurrent database access with the mutex

Positive Aspects ✅

  1. Mutex for wa-sqlite: Good solution to prevent concurrent access issues
  2. Security limits: Proper capping of result limits and pagination
  3. Protocol versioning: Backward compatibility handled with protocol v3
  4. UI/UX: Clean pagination and table selection interface
  5. Error messages: Good error handling in the inspector handler
  6. Foreign key visualization: Nice feature for understanding relationships

Recommendations Summary

Must Fix (before merge):

  1. Fix SQL injection vulnerability with proper table name validation
  2. Fix DbMutex race condition
  3. Clarify version detection logic change (v4 removal)
  4. Add proper test coverage

Should Fix (before or soon after merge):

  1. Implement proper column type detection using PRAGMA
  2. Add error handling for individual table queries
  3. Deduplicate DbMutex implementation
  4. Clean up unrelated formatting changes

Nice to Have:

  1. Optimize schema discovery with better queries
  2. Remove commented-out code
  3. Add comments for empty catch blocks

Overall, this is a useful feature with a solid foundation, but the security and correctness issues should be addressed before merging.

Comment on lines +152 to +171
async getDatabaseTableRows(
table: string,
limit: number,
offset: number,
): Promise<ArrayBuffer> {
if (!this.isDatabaseEnabled()) {
throw new actorErrors.DatabaseNotEnabled();
}

const db = this.actor.db;
const safeLimit = Math.max(0, Math.min(Math.floor(limit), 500));
const safeOffset = Math.max(0, Math.floor(offset));
const quoted = `"${escapeDoubleQuotes(table)}"`;
const result = await db.execute(
`SELECT * FROM ${quoted} LIMIT ? OFFSET ?`,
safeLimit,
safeOffset,
);
return bufferToArrayBuffer(cbor.encode(result));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Security vulnerability: getDatabaseTableRows() accepts any table name from the client without validation. Unlike getDatabaseSchema() which filters out system tables (line 115), this method allows querying any table including SQLite system tables like sqlite_master, sqlite_sequence, etc.

Impact: Malicious clients could read sensitive system metadata or internal tables.

Fix: Add table name validation:

async getDatabaseTableRows(
    table: string,
    limit: number,
    offset: number,
): Promise<ArrayBuffer> {
    if (!this.isDatabaseEnabled()) {
        throw new actorErrors.DatabaseNotEnabled();
    }

    // Validate table name - reject system and internal tables
    if (table.startsWith('sqlite_') || table.startsWith('__drizzle_')) {
        throw new Error('Cannot query system or internal tables');
    }

    const db = this.actor.db;
    const safeLimit = Math.max(0, Math.min(Math.floor(limit), 500));
    const safeOffset = Math.max(0, Math.floor(offset));
    const quoted = `"${escapeDoubleQuotes(table)}"`;
    const result = await db.execute(
        `SELECT * FROM ${quoted} LIMIT ? OFFSET ?`,
        safeLimit,
        safeOffset,
    );
    return bufferToArrayBuffer(cbor.encode(result));
}
Suggested change
async getDatabaseTableRows(
table: string,
limit: number,
offset: number,
): Promise<ArrayBuffer> {
if (!this.isDatabaseEnabled()) {
throw new actorErrors.DatabaseNotEnabled();
}
const db = this.actor.db;
const safeLimit = Math.max(0, Math.min(Math.floor(limit), 500));
const safeOffset = Math.max(0, Math.floor(offset));
const quoted = `"${escapeDoubleQuotes(table)}"`;
const result = await db.execute(
`SELECT * FROM ${quoted} LIMIT ? OFFSET ?`,
safeLimit,
safeOffset,
);
return bufferToArrayBuffer(cbor.encode(result));
}
async getDatabaseTableRows(
table: string,
limit: number,
offset: number,
): Promise<ArrayBuffer> {
if (!this.isDatabaseEnabled()) {
throw new actorErrors.DatabaseNotEnabled();
}
// Validate table name - reject system and internal tables
if (table.startsWith('sqlite_') || table.startsWith('__drizzle_')) {
throw new Error('Cannot query system or internal tables');
}
const db = this.actor.db;
const safeLimit = Math.max(0, Math.min(Math.floor(limit), 500));
const safeOffset = Math.max(0, Math.floor(offset));
const quoted = `"${escapeDoubleQuotes(table)}"`;
const result = await db.execute(
`SELECT * FROM ${quoted} LIMIT ? OFFSET ?`,
safeLimit,
safeOffset,
);
return bufferToArrayBuffer(cbor.encode(result));
}

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +1 to +20
import { Button, Flex, ScrollArea, WithTooltip } from "@/components";
import {
faChevronLeft,
faChevronRight,
faRefresh,
faTable,
faTableCells,
Icon,
} from "@rivet-gg/icons";
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { ShimmerLine } from "../shimmer-line";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../ui/select";
import { useActorInspector } from "./actor-inspector-context";
Copy link
Contributor

Choose a reason for hiding this comment

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

Imports need to be sorted according to Biome's rules. Run 'biome check --write .' to automatically fix import sorting.

Spotted by Graphite Agent (based on CI logs)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@@ -193,7 +193,7 @@
],
"scripts": {
"build": "tsup src/mod.ts src/client/mod.ts src/common/log.ts src/common/websocket.ts src/actor/errors.ts src/topologies/coordinate/mod.ts src/topologies/partition/mod.ts src/utils.ts src/driver-helpers/mod.ts src/driver-test-suite/mod.ts src/serve-test-suite/mod.ts src/test/mod.ts src/inspector/mod.ts src/workflow/mod.ts src/db/mod.ts src/db/drizzle/mod.ts",
Copy link
Contributor

Choose a reason for hiding this comment

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

Update the build script to run the schema generation step first by changing it to: "build": "npm run build:schema && tsup src/mod.ts src/client/mod.ts src/common/log.ts src/common/websocket.ts src/actor/errors.ts src/topologies/coordinate/mod.ts src/topologies/partition/mod.ts src/utils.ts src/driver-helpers/mod.ts src/driver-test-suite/mod.ts src/serve-test-suite/mod.ts src/test/mod.ts src/inspector/mod.ts src/workflow/mod.ts src/db/mod.ts src/db/drizzle/mod.ts",

Spotted by Graphite Agent (based on CI logs)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

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.

1 participant

Comments