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.sqliteholds admins, tenants and tokens. Each tenant lives intenants/<id>/data.sqlite. - Reads use read-only connections guarded by SQLite’s authorizer, so cross-tenant
ATTACHis 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 acceptdry_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.