Contact EN 中文

drust

Open-source Rust BaaS: one SQLite file per tenant, a native MCP endpoint for AI agents

At a glance

Problem
At the Tzu Chi Foundation, where I lead IT, my team keeps starting small things: event sites, forms, internal tools, AI experiments.
What I built
drust is a self-hosted Backend-as-a-Service in one Rust binary.
Outcome
Backs 6 Tzu Chi sites and tools in use, with 2,600+ tests, released as open source under AGPL-3.0.
My role
Solo
Period
2026-04 → 2026-08

Abilities shown

Each links to the ability and its level on the Skills page.

The problem

At the Tzu Chi Foundation, where I lead IT, my team keeps starting small things: event sites, forms, internal tools, AI experiments. Each one needs a backend. A Postgres or Supabase stack per project is too heavy to run and back up. I also wanted AI agents to work with that data directly, without me writing glue code for every project, and without risking one project’s data leaking into another.

What I built

drust is a self-hosted Backend-as-a-Service in one Rust binary. Each tenant (one project) gets its own SQLite file, a REST API, and its own MCP endpoint that an AI agent can connect to. It adds row-level security, realtime, vector search, WebAssembly edge functions, per-tenant OAuth, resumable S3 uploads, typed client codegen and an admin UI. I also wrote drust-js, the TypeScript SDK.

How it works

  • One meta.sqlite holds admins, tenants and tokens. Each tenant lives in tenants/<id>/data.sqlite.
  • Reads use read-only connections guarded by SQLite’s authorizer, so cross-tenant ATTACH is refused inside SQL itself.
  • Writes go through structured REST or MCP tools behind a per-tenant writer lock.
  • Every MCP server opens with an “intent → tool” map, so an agent knows where to start on first connect.

Decisions

  • The write path never takes raw SQL. User input reaches SQL only as a structured filter compiled with bound parameters. That is what makes owner rules and RLS policies enforceable. User tokens are refused on raw-query and MCP routes, because drust cannot rewrite free-form SQL.
  • The MCP surface is built for the model. Every error carries a suggested_fix, and destructive tools accept dry_run. In v1.37 I cut 4 duplicate tools (57 → 52) so a model picks the right one first; new features have since grown it to 76. A small eval harness drives Claude through 5 fixed tasks to measure this.
  • Edge functions run in-process, sandboxed inside wasmtime. Limits come from epoch deadlines, a resource limiter and an empty WASI context. This was a conscious trade-off against process-wide W^X, documented in the deploy files.

Outcome

drust is at v1.66, AGPL-3.0, with a multi-arch Docker image and a public SDK. I had AI agents run two security assessments against my own instance. The first found 1 High-severity authorization bug, which I fixed. The follow-up gray-box test on v1.41.3 found 0 Critical, 0 High, 1 Medium and 2 Low. At Tzu Chi it backs 9 projects: 6 sites and tools in use (the 60th anniversary site, 60impact, the annual report, the auspicious-month site, the Buddha Day site and MasterSlides), a vegan content site being moved onto it, and 2 prototypes (a voice-collection tool and a LINE request-collection tool). I built it with Claude Code, using repo guides and skills to drive a spec → plan → implement → review loop.