Rehman.DbSentinel
2.0.0
dotnet add package Rehman.DbSentinel --version 2.0.0
NuGet\Install-Package Rehman.DbSentinel -Version 2.0.0
<PackageReference Include="Rehman.DbSentinel" Version="2.0.0" />
<PackageVersion Include="Rehman.DbSentinel" Version="2.0.0" />
<PackageReference Include="Rehman.DbSentinel" />
paket add Rehman.DbSentinel --version 2.0.0
#r "nuget: Rehman.DbSentinel, 2.0.0"
#:package Rehman.DbSentinel@2.0.0
#addin nuget:?package=Rehman.DbSentinel&version=2.0.0
#tool nuget:?package=Rehman.DbSentinel&version=2.0.0
Rehman.DbSentinel
A production-ready, read-only embedded database insights dashboard for .NET core web applications.
Think pgAdmin or SSMS — but living inside your app at/app-data-insights.
Supports PostgreSQL · SQL Server · SQLite
Features
| Category | Feature |
|---|---|
| Schema Explorer | Collapsible sidebar tree: Tables → Columns, Constraints, Relations, Indexes |
| Table Context Menu | Right-click: Top 100, Top 1000, Last 100, Last 1000, SELECT Script, Query Editor |
| Data View | Paginated table data, column sort, global fuzzy search with match highlighting |
| Schema Tab | Full columns list with PK/FK badges, constraints table, CREATE TABLE script |
| Statistics Tab | Row count, column count, size on disk, all indexes |
| Query Editor | CodeMirror SQL editor, syntax highlight, table+column autocomplete, Ctrl+Enter to run |
| Query History | Every query stored with IP, duration, row count, success/error status |
| Row Hierarchy | Right-click → View Full Hierarchy: parent rows (green), current (grey), child rows (red) |
| Change Tracking | Auto-creates _log tables per tracked table in dbsentinel schema |
| Diff Viewer | Git-style green/red diff for every log entry |
| Log Dashboard | Dedicated tab: total entries card, creates/updates/deletes cards, daily line chart |
| Main Dashboard | Stats cards, largest tables, daily changes bar chart, health summary |
| Retention Cleanup | Background service runs every 24h, deletes logs older than LogRetentionDays |
| Read-Only Enforcement | Server-side QueryValidator blocks INSERT/UPDATE/DELETE/DROP/ALTER etc. |
| CSV Export | Export any table data view to CSV with one click |
Installation
dotnet add package Rehman.DbSentinel
Quick Start
1. Configure in Program.cs
using Rehman.DbSentinel;
using Rehman.DbSentinel.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbSentinel(options =>
{
options.ConnectionString = "Host=localhost;Database=myapp;Username=postgres;Password=secret";
options.DatabaseType = DatabaseType.PostgreSQL;
options.TrackTables = new[] { "Users", "Orders", "Products" };
options.LogRetentionDays = 30;
options.DashboardTitle = "MyApp — Database Insights";
options.RoutePrefix = "app-data-insights";
options.MaxQueryRows = 5000;
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseDbSentinel(); // ← mounts the dashboard + creates internal tables
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
2. Open the Dashboard
Navigate to http://localhost:5000/app-data-insights
Database Connection Examples
PostgreSQL
options.ConnectionString = "Host=localhost;Port=5432;Database=myapp;Username=postgres;Password=secret";
options.DatabaseType = DatabaseType.PostgreSQL;
SQL Server
options.ConnectionString = "Server=localhost;Database=MyApp;Trusted_Connection=true;TrustServerCertificate=true;";
options.DatabaseType = DatabaseType.SqlServer;
SQLite
options.ConnectionString = "Data Source=myapp.db";
options.DatabaseType = DatabaseType.SQLite;
Configuration Reference
| Option | Type | Default | Description |
|---|---|---|---|
ConnectionString |
string |
— | Database connection string |
DatabaseType |
DatabaseType |
PostgreSQL |
PostgreSQL, SqlServer, or SQLite |
TrackTables |
string[] |
[] |
Tables to create change logs for |
LogRetentionDays |
int |
30 |
Days before log entries are auto-deleted. 0 = never |
RoutePrefix |
string |
app-data-insights |
URL route for the dashboard |
MaxQueryRows |
int |
5000 |
Hard cap on rows returned per query |
EnableQueryHistory |
bool |
true |
Save every executed query to history |
DashboardTitle |
string |
DbSentinel — Database Insights |
Title shown in the browser tab |
SentinelSchema |
string |
dbsentinel |
Schema for internal tables (PG/SQL Server). Prefix for SQLite |
Security
Read-Only Enforcement
Every user query passes through QueryValidator before execution:
- Comment stripping — block comments
/* */and line comments--are removed before parsing - String literal stripping — quoted values are ignored so
'DROP TABLE'can't hide a keyword - Keyword blocklist —
INSERT,UPDATE,DELETE,DROP,ALTER,TRUNCATE,EXEC,GRANT,REVOKE,MERGE,CREATE,CALL,PRAGMA,ATTACH,VACUUM,DBCC,BULK,SHUTDOWN,KILLand more - Must start with SELECT or WITH — only SELECT statements and CTEs are allowed
- Single statement only — stacked queries (
; DROP TABLE) are rejected - Query timeout — 30-second hard timeout on all executions
- Row limit — all queries are wrapped with
LIMIT / TOPusingMaxQueryRows
Protecting the Dashboard
DbSentinel exposes schema and data — protect it in production:
// Option A: Localhost-only access
app.Use(async (ctx, next) => {
if (ctx.Request.Path.StartsWithSegments("/app-data-insights")) {
var ip = ctx.Connection.RemoteIpAddress?.ToString();
if (ip != "127.0.0.1" && ip != "::1") { ctx.Response.StatusCode = 403; return; }
}
await next();
});
app.UseDbSentinel();
// Option B: API key header
app.Use(async (ctx, next) => {
if (ctx.Request.Path.StartsWithSegments("/app-data-insights")) {
if (ctx.Request.Headers["X-Sentinel-Key"] != configuration["DbSentinel:Key"])
{ ctx.Response.StatusCode = 401; return; }
}
await next();
});
app.UseDbSentinel();
// Option C: ASP.NET Core auth role
app.Use(async (ctx, next) => {
if (ctx.Request.Path.StartsWithSegments("/app-data-insights") && !ctx.User.IsInRole("Admin"))
{ ctx.Response.StatusCode = 403; return; }
await next();
});
app.UseDbSentinel();
Tip: Use a dedicated read-only database user for the connection string. Belt-and-suspenders with the query validation.
Change Tracking
Automatic Log Table Creation
On app.UseDbSentinel(), for each table in TrackTables, a log table is created automatically:
dbsentinel.Users_log
dbsentinel.Orders_log
dbsentinel.Products_log
Log table schema:
CREATE TABLE dbsentinel.Users_log (
log_id BIGSERIAL PRIMARY KEY,
operation_type VARCHAR(10) NOT NULL, -- INSERT, UPDATE, DELETE
old_data JSONB, -- Previous state (NULL for INSERT)
new_data JSONB, -- New state (NULL for DELETE)
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
db_user VARCHAR(100)
);
Logging from Application Code
Inject ITrackingService into your services:
public class OrderService
{
private readonly ITrackingService _tracking;
private readonly AppDbContext _db;
public OrderService(ITrackingService tracking, AppDbContext db)
{
_tracking = tracking;
_db = db;
}
public async Task UpdateAsync(int id, UpdateOrderDto dto)
{
var order = await _db.Orders.FindAsync(id);
var before = new { order.Status, order.Total }; // capture snapshot
order.Status = dto.Status;
order.Total = dto.Total;
await _db.SaveChangesAsync();
await _tracking.LogChangeAsync("Orders", "UPDATE", before, order, dbUser: "api");
}
public async Task DeleteAsync(int id)
{
var order = await _db.Orders.FindAsync(id);
_db.Orders.Remove(order!);
await _db.SaveChangesAsync();
await _tracking.LogChangeAsync("Orders", "DELETE", order, null, dbUser: "api");
}
}
Database-Level Triggers (Optional)
For 100% capture without app-side calls, apply the trigger SQL scripts in the Migrations/ folder:
# PostgreSQL — installs a universal trigger function + attaches to your tables
psql -d myapp -f Migrations/postgresql_setup.sql
# SQL Server
sqlcmd -S localhost -d MyApp -i Migrations/sqlserver_setup.sql
# SQLite
sqlite3 myapp.db < Migrations/sqlite_setup.sql
Query Editor
- Syntax highlighting — CodeMirror with SQL mode
- Auto-complete — Ctrl+Space for table and column suggestions loaded from live schema
- Ctrl+Enter — run query without clicking
- Format — basic SQL keyword uppercasing
- History panel — last 30 queries shown with timestamp, duration, row count, status
- Click history item — loads it back into the editor
- Read-only enforced — server validates before executing, not just client-side
Table Context Menu (Right-Click on Tree)
| Option | SQL Generated |
|---|---|
| Get Top 100 Rows | SELECT * FROM "table" LIMIT 100 |
| Get Top 1000 Rows | SELECT * FROM "table" LIMIT 1000 |
| Get Last 100 Rows | SELECT * FROM "table" ORDER BY pk DESC LIMIT 100 |
| Get Last 1000 Rows | SELECT * FROM "table" ORDER BY pk DESC LIMIT 1000 |
| SELECT Script | Formatted SELECT col1, col2... FROM "table" copied to clipboard |
| Open Query Editor | Focuses the Query Editor tab |
Row Hierarchy Viewer
Right-click any data row → View Full Hierarchy
┌──────────────────────────────────────────────────────┐
│ ↑ Parent Rows (they reference) [Light Green] │
│ ┌────────────────────────────────────────────────┐ │
│ │ customers · via customer_id │ │
│ │ id: 5 name: Alice email: alice@example.com │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ● Current Row [Grey] │
│ id: 42 customer_id: 5 total: 150.00 │
│ │
│ ↓ Child Rows (they reference this) [Light Red] │
│ ┌────────────────────────────────────────────────┐ │
│ │ order_items · via order_id │ │
│ │ id: 101 product: Widget qty: 3 │ │
│ │ id: 102 product: Gadget qty: 1 │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
Internal API Endpoints
All endpoints are under /{routePrefix}/api/:
| Method | Endpoint | Description |
|---|---|---|
| GET | tables |
All tables with row counts |
| GET | schema-tree |
Full schema: tables + columns + constraints |
| GET | stats |
Database-level stats |
| GET | config |
Dashboard configuration |
| GET | query-history?limit=50 |
Last N executed queries |
| GET | log-summary?days=30 |
Aggregate log stats + daily breakdown |
| GET | daily-changes?days=30 |
Per-day INSERT/UPDATE/DELETE counts |
| GET | tables/{name}/columns |
Table columns |
| GET | tables/{name}/constraints |
Table constraints and FK refs |
| GET | tables/{name}/indexes |
Table indexes |
| GET | tables/{name}/stats |
Row count, size, indexes, CREATE script |
| GET | tables/{name}/data?limit&offset&search&orderBy&dir |
Paginated table data |
| GET | tables/{name}/logs?limit=100 |
Change log entries |
| POST | query |
Execute a SELECT query { "sql": "..." } |
| GET | hierarchy?table&pkCol&pkVal |
Row parent/child hierarchy |
Retention
Log retention runs automatically every 24 hours via RetentionBackgroundService.
It deletes:
query_historyentries older thanLogRetentionDays- All
{Table}_logentries older thanLogRetentionDays
Set LogRetentionDays = 0 to disable automatic cleanup entirely.
License
MIT © Muhammad Rehman Tahir
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 is compatible. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.1 is compatible. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 is compatible. |
-
.NETCoreApp 2.1
- Microsoft.AspNetCore.Hosting.Abstractions (>= 2.2.0)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.2.0)
- Microsoft.Extensions.DependencyInjection (>= 3.1.32)
- Microsoft.Extensions.Hosting.Abstractions (>= 3.1.32)
- Microsoft.Extensions.Logging.Abstractions (>= 3.1.32)
- Microsoft.Extensions.Options (>= 3.1.32)
- System.Text.Json (>= 5.0.2)
-
.NETCoreApp 3.1
- Microsoft.AspNetCore.Hosting.Abstractions (>= 2.2.0)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.2.0)
- Microsoft.Extensions.DependencyInjection (>= 3.1.32)
- Microsoft.Extensions.Hosting.Abstractions (>= 3.1.32)
- Microsoft.Extensions.Logging.Abstractions (>= 3.1.32)
- Microsoft.Extensions.Options (>= 3.1.32)
- System.Text.Json (>= 5.0.2)
-
net5.0
- No dependencies.
-
net6.0
- No dependencies.
-
net7.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.