{"id":46661117,"url":"https://github.com/vobase/vobase","last_synced_at":"2026-06-09T13:01:12.507Z","repository":{"id":341977106,"uuid":"1170038014","full_name":"vobase/vobase","owner":"vobase","description":"The app framework built for AI coding agents. Own every line. Your AI already knows how to build on it.","archived":false,"fork":false,"pushed_at":"2026-06-07T13:52:09.000Z","size":16901,"stargazers_count":51,"open_issues_count":7,"forks_count":12,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-06-07T15:22:21.722Z","etag":null,"topics":["better-auth","bun","claude","drizzle-orm","flyio","honojs","mcp","rag","railway","render","shadcn-ui","skills","sqlite","supabase","tanstack-router","typescript","vite","whatsapp"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vobase.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-01T15:58:38.000Z","updated_at":"2026-06-07T13:49:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vobase/vobase","commit_stats":null,"previous_names":["vobase/vobase"],"tags_count":142,"template":false,"template_full_name":null,"purl":"pkg:github/vobase/vobase","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vobase%2Fvobase","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vobase%2Fvobase/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vobase%2Fvobase/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vobase%2Fvobase/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vobase","download_url":"https://codeload.github.com/vobase/vobase/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vobase%2Fvobase/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34107866,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["better-auth","bun","claude","drizzle-orm","flyio","honojs","mcp","rag","railway","render","shadcn-ui","skills","sqlite","supabase","tanstack-router","typescript","vite","whatsapp"],"created_at":"2026-03-08T11:06:59.333Z","updated_at":"2026-06-09T13:01:12.500Z","avatar_url":"https://github.com/vobase.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  English / \u003ca href=\"README_CN.md\"\u003e中文\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003evobase\u003c/b\u003e\n  \u003cbr\u003e\n  The app framework built for AI coding agents.\u003cbr\u003e\n  Own every line. Your AI already knows how to build on it.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@vobase/core\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@vobase/core.svg\" alt=\"npm @vobase/core\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@vobase/core\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/@vobase/core.svg\" alt=\"npm downloads\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/vobase/vobase\"\u003e\u003cimg src=\"https://img.shields.io/github/stars/vobase/vobase\" alt=\"GitHub stars\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/vobase/vobase/commits/main\"\u003e\u003cimg src=\"https://img.shields.io/github/last-commit/vobase/vobase\" alt=\"Last commit\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"License MIT\"\u003e\n  \u003ca href=\"https://discord.gg/sVsPBHtvTZ\"\u003e\u003cimg\n  src=\"https://img.shields.io/badge/discord-join-5865F2?logo=discord\u0026logoColor=white\"\n  alt=\"Discord\"\u003e\u003c/a\u003e\n  \u003cbr\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Bun-000?style=for-the-badge\u0026logo=bun\u0026logoColor=white\" alt=\"Bun\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge\u0026logo=typescript\u0026logoColor=white\" alt=\"TypeScript\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Hono-E36002?style=for-the-badge\u0026logo=hono\u0026logoColor=white\" alt=\"Hono\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Drizzle-C5F74F?style=for-the-badge\u0026logo=drizzle\u0026logoColor=black\" alt=\"Drizzle\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/PostgreSQL-4169E1?style=for-the-badge\u0026logo=postgresql\u0026logoColor=white\" alt=\"PostgreSQL\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Better_Auth-16a34a?style=for-the-badge\" alt=\"Better Auth\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/React-61DAFB?style=for-the-badge\u0026logo=react\u0026logoColor=black\" alt=\"React\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Vite-646CFF?style=for-the-badge\u0026logo=vite\u0026logoColor=white\" alt=\"Vite\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/TanStack-EF4444?style=for-the-badge\u0026logo=reactquery\u0026logoColor=white\" alt=\"TanStack\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Tailwind-06B6D4?style=for-the-badge\u0026logo=tailwindcss\u0026logoColor=white\" alt=\"Tailwind CSS\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/shadcn/ui-000?style=for-the-badge\u0026logo=shadcnui\u0026logoColor=white\" alt=\"shadcn/ui\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#what-you-get\"\u003ewhat you get\u003c/a\u003e ·\n  \u003ca href=\"#quick-start\"\u003eget started\u003c/a\u003e ·\n  \u003ca href=\"#what-a-module-looks-like\"\u003ecode\u003c/a\u003e ·\n  \u003ca href=\"#agent-harness\"\u003eharness\u003c/a\u003e ·\n  \u003ca href=\"#vs-the-alternatives\"\u003ecompare\u003c/a\u003e ·\n  \u003ca href=\"https://docs.vobase.dev\"\u003edocs\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nA full-stack TypeScript framework that gives you auth, database, storage, jobs, and a first-class AI agent runtime in a single Bun process. Docker Compose Postgres for local dev, managed Postgres in production. Like a self-hosted Supabase — but you own every line of code. Like Pocketbase — but it's TypeScript you can read and modify.\n\nAI coding agents (Claude Code, Cursor, Codex) understand vobase out of the box. Strict conventions and a uniform module shape mean generated code works on the first try — not the third.\n\nYou own the code. You own the data. You own the infrastructure.\n\n---\n\n### what you get\n\nOne `bun create vobase` and you have a working full-stack app:\n\n| Primitive | What it does |\n|---|---|\n| **Runtime** | **Bun** — native TypeScript, ~50ms startup, built-in test runner. One process, one container. |\n| **Database** | **PostgreSQL** via **Drizzle**. Docker Compose Postgres (pgvector/pg17) for local dev, managed Postgres in production. Full SQL, ACID transactions, pgvector for embeddings. |\n| **Auth** | **better-auth**. Sessions, passwords, email OTP, CSRF. RBAC with role guards, API keys, organizations. SSO/2FA as plugins. |\n| **API** | **Hono** — ~14KB, typed routing, Bun-first. Every AI coding tool already knows Hono. |\n| **Audit** | Built-in audit log, record change tracking, and auth event hooks. Every mutation is traceable. |\n| **Sequences** | Gap-free business number generation (INV-0001, PO-0042). Transaction-safe, never skips. |\n| **Storage** | File storage with virtual buckets. Local or S3/R2 backends. Metadata tracked in Postgres. |\n| **Channels** | Multi-channel messaging with pluggable adapters: WhatsApp (Cloud API), email (Resend, SMTP). Inbound webhooks, outbound sends, delivery tracking. All messages logged. |\n| **Integrations** | Encrypted credential vault for external services. AES-256-GCM at rest. Platform-aware: opt-in multi-tenant OAuth handoff via HMAC-signed JWT. |\n| **Jobs** | Background tasks with retries, cron, and job chains. **pg-boss** backed — Postgres only, no Redis. |\n| **Realtime** | Server-push via PostgreSQL `LISTEN/NOTIFY` + SSE. No WebSocket. Modules `pg_notify` after commit; the frontend hook invalidates matching TanStack Query keys. |\n| **Agent harness** | First-class AI agent runtime (`pi-agent-core` + `pi-ai`). Frozen system prompt per wake, byte-stable provider cache, tool budget spill, steer/abort between turns, journaled events, idle resumption, restart recovery. |\n| **Workspace** | Virtual filesystem materialized per-wake from your modules. AGENTS.md is composed from per-module fragments; agents read `/staff/\u003cid\u003e/profile.md`, `/contacts/\u003cid\u003e/MEMORY.md`, etc. RO enforcement at the FS boundary. |\n| **CLI** | **`@vobase/cli`** — standalone, catalog-driven binary. Modules register verbs via `defineCliVerb`; the same body runs in-process (agent bash sandbox) and over HTTP-RPC (`vobase` binary). |\n| **Frontend** | **React + TanStack Router + shadcn/ui + ai-elements + DiceUI + Tailwind v4**. Type-safe routing with codegen, code-splitting. You own the component source. |\n| **MCP** | Model Context Protocol server in the same process. AI tools can read your schema, list modules, and view logs before generating code. |\n| **Deploy** | Dockerfile + railway.json included. One `railway up` or `docker build` and you're live. |\n\nLocally, `docker compose up -d` starts a pgvector/pg17 Postgres instance. `bun run dev` and you're building. In production, point `DATABASE_URL` at any managed Postgres.\n\n---\n\n### quick start\n\n```bash\nbun create vobase my-app\ncd my-app\ndocker compose up -d\nbun run db:reset\nbun run dev\n```\n\nBackend on `:3001`, frontend on `:5173`. Ships with the agent-native helpdesk template — messaging, channels, contacts, team, drive, agents — already wired up.\n\n---\n\n### what you can build\n\nEvery module is a self-contained directory: schema, service, handlers, jobs, pages, and an `agent.ts` slot that publishes tools, materializers, RO hints, and AGENTS.md fragments to the harness. No plugins, no marketplace. Just TypeScript you own.\n\n| Use Case | What Ships |\n|---|---|\n| **Agent-native helpdesk** | The default template. WhatsApp + email inbox, contact memory, staff-mention fan-out, supervisor coaching, scheduled follow-ups, approval gates, drive overlays. |\n| **SaaS Starter** | User accounts, billing integration, subscription management. Auth + jobs + webhooks handle the plumbing. |\n| **Internal Tools** | Admin panels, operations dashboards, approval workflows. Status machines enforce business logic. Audit trails track every change. |\n| **CRM \u0026 Contacts** | Companies, contacts, interaction timelines, deal tracking. Cross-module references via service imports — no FK across module boundaries. |\n| **Project Tracker** | Tasks, assignments, status workflows, notifications. Background jobs handle reminders and escalations. |\n| **Billing \u0026 Invoicing** | Invoices, line items, payments, aging reports. Integer money ensures exact arithmetic. Gap-free numbering via transactions. |\n| **Your Vertical** | Property management, fleet tracking, field services — whatever the business needs. Describe it to your AI tool. It generates the module. |\n\nAI coding agents generate modules from your conventions. Like `npx shadcn add button` — files get copied, you own the code.\n\n---\n\n### how it works\n\nVobase makes itself legible to every AI coding tool on the market.\n\nThe framework ships with one canonical module shape, one write-path discipline, and a harness that AI agents drive at runtime. When you need a new capability:\n\n1. Open your AI tool and describe the requirement\n2. The AI reads your existing schema, the canonical module shape, and the relevant `.claude/skills/` packs\n3. It generates a complete module — schema, service, handlers, jobs, pages, agent slot, tests, seed data\n4. You review the diff, run `bun run dev`, and it works\n\nSkill packs cover the parts where apps get tricky: money stored as integer cents (never floats), status transitions as explicit state machines (not arbitrary string updates), gap-free business numbers generated inside database transactions, single-write-path enforcement via `check:shape`, frontend bundle isolation via `check:bundle`.\n\nThese conventions are what make AI-generated modules work on the first try.\n\n**The thesis:** your specs and domain knowledge are the asset. AI tools are the compiler. The compiler improves every quarter. Your skills compound forever.\n\n---\n\n### what a module looks like\n\nEvery module is a thin aggregator over sibling files. `module.ts` declares the contract; everything else lives next to the code that owns the side-effect.\n\n```typescript\n// modules/projects/module.ts\nimport type { ModuleDef } from '~/runtime'\nimport { projectsAgent } from './agent'\nimport { projectListVerb } from './verbs/project-list'\nimport { createProjectsService, installProjectsService } from './service/projects'\nimport * as web from './web'\n\nconst projects: ModuleDef = {\n  name: 'projects',\n  requires: ['team'],\n  web: { routes: web.routes },\n  jobs: [],\n  agent: projectsAgent,\n  init(ctx) {\n    installProjectsService(createProjectsService({ db: ctx.db }))\n    ctx.cli.registerAll([projectListVerb])\n  },\n}\n\nexport default projects\n```\n\n```\nmodules/projects/\n  module.ts            ← thin aggregator (above)\n  schema.ts            ← Drizzle table definitions\n  state.ts             ← status transitions, state machine\n  service/             ← transactional write-path (sole writer of this module's tables)\n  handlers/            ← Hono routes (HTTP API)\n  web.ts               ← route barrel mounted under /api/projects\n  pages/               ← React pages — list, detail, create\n  components/          ← React components owned by this module\n  hooks/               ← TanStack Query hooks\n  jobs.ts              ← pg-boss handlers\n  agent.ts             ← agent slot: tools, materializers, roHints, AGENTS.md fragments\n  tools/               ← defineAgentTool — colocated with the service\n  verbs/               ← defineCliVerb — runs in agent bash and the CLI binary\n  cli.ts               ← barrel exporting \u003cmodule\u003eVerbs\n  seed.ts              ← demo data\n  defaults/            ← *.agent.yaml, *.schedule.yaml — opt-in starter content\n  skills/              ← inline skill bodies the agent reads via drive overlay\n  *.test.ts            ← colocated bun test\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eschema example\u003c/b\u003e — Drizzle + PostgreSQL with typed columns, timestamps, status enums\u003c/summary\u003e\n\n```typescript\n// modules/projects/schema.ts\nimport { pgTable, text, integer, timestamp, check } from 'drizzle-orm/pg-core'\nimport { sql } from 'drizzle-orm'\nimport { nanoidPrimaryKey } from '@vobase/core'\n\nexport const projects = pgTable('projects', {\n  id: nanoidPrimaryKey(),\n  name: text('name').notNull(),\n  description: text('description'),\n  status: text('status').notNull().default('active'),\n  ownerId: text('owner_id').notNull(),\n  createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\n}, (t) =\u003e [\n  check('projects_status_chk', sql`${t.status} in ('active','archived','deleted')`),\n])\n\nexport const tasks = pgTable('tasks', {\n  id: nanoidPrimaryKey(),\n  projectId: text('project_id').references(() =\u003e projects.id),\n  title: text('title').notNull(),\n  status: text('status').notNull().default('todo'),\n  assigneeId: text('assignee_id'),\n  priority: integer('priority').notNull().default(0),\n}, (t) =\u003e [\n  check('tasks_status_chk', sql`${t.status} in ('todo','in_progress','done')`),\n])\n```\n\n`check:shape` enforces that only `service/projects.ts` writes to `projects` — handlers and jobs go through the service.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003ehandler example\u003c/b\u003e — Hono routes with Zod validation, typed RPC client\u003c/summary\u003e\n\n```typescript\n// modules/projects/handlers/list.ts\nimport { Hono } from 'hono'\nimport { zValidator } from '@hono/zod-validator'\nimport { z } from 'zod'\nimport { getCtx } from '~/runtime'\nimport { projectsService } from '../service/projects'\n\nexport const listRoute = new Hono().get(\n  '/',\n  zValidator('query', z.object({ status: z.enum(['active','archived']).optional() })),\n  async (c) =\u003e {\n    const ctx = getCtx(c)\n    const { status } = c.req.valid('query')\n    const items = await projectsService().list({ ownerId: ctx.user.id, status })\n    return c.json(items)\n  },\n)\n```\n\nThe frontend gets fully typed API calls via the Hono RPC client (`src/lib/api-client.ts`):\n\n```typescript\nimport { useQuery } from '@tanstack/react-query'\nimport { api } from '@/lib/api-client'\n\nexport function useProjects() {\n  return useQuery({\n    queryKey: ['projects'],\n    queryFn: async () =\u003e {\n      const res = await api.projects.$get({ query: {} })\n      return await res.json() // fully typed\n    },\n  })\n}\n```\n\n`use-realtime-invalidation.ts` maps `pg_notify` `table` payloads onto the first element of TanStack `queryKey` — services emit notifies after commit and the UI re-fetches automatically.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003ejob example\u003c/b\u003e — background tasks via pg-boss, no Redis\u003c/summary\u003e\n\n```typescript\n// modules/projects/jobs.ts\nimport { defineJob } from '@vobase/core'\nimport { projectsService } from './service/projects'\n\nexport const sendReminder = defineJob('projects:send-reminder',\n  async (data: { taskId: string }) =\u003e {\n    await projectsService().notifyAssignee(data.taskId)\n  },\n)\n```\n\nSchedule from handlers or services: `ctx.scheduler.add('projects:send-reminder', { taskId }, { delay: '1d' })`. Retries, cron scheduling, and priority queues — all Postgres-backed via pg-boss.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eagent slot example\u003c/b\u003e — tools, materializers, AGENTS.md fragments\u003c/summary\u003e\n\n```typescript\n// modules/projects/agent.ts\nimport { defineAgentTool, defineIndexContributor } from '@vobase/core'\nimport { projectsService } from './service/projects'\n\nconst createTask = defineAgentTool({\n  name: 'create_task',\n  audience: 'internal',\n  lane: 'standalone',\n  // schema: zod input/output…\n  async handler({ input, ctx }) {\n    return await projectsService().createTask(input)\n  },\n})\n\nexport const projectsAgent = {\n  agentsMd: [defineIndexContributor({\n    file: 'AGENTS.md',\n    priority: 50,\n    name: 'projects.overview',\n    render: () =\u003e '## Projects\\n\\n- `create_task` to add a task to a project.',\n  })],\n  materializers: [/* WorkspaceMaterializerFactory\u003cWakeContext\u003e[] */],\n  roHints: [/* explain why /projects/\u003cid\u003e/* paths are read-only */],\n  tools: [createTask],\n}\n```\n\nThe wake builder filters tools by `lane` and `audience`, runs each materializer factory against the wake context, chains roHints, and feeds the AGENTS.md contributors into the harness. One agent slot per module — no central registry to update.\n\n\u003c/details\u003e\n\n---\n\n### the ctx object\n\nEvery HTTP handler gets a context object with runtime capabilities. Current surface:\n\n| Property | What it does |\n|---|---|\n| `ctx.db` | Drizzle instance. Full PostgreSQL — reads, writes, transactions. |\n| `ctx.user` | `{ id, email, name, role, activeOrganizationId? }`. From better-auth session. RBAC middlewares: `requireRole()`, `requirePermission()`, `requireOrg()`. |\n| `ctx.scheduler` | Job queue. `add(jobName, data, options)` to schedule background work. |\n| `ctx.storage` | `StorageService` — virtual buckets with local/S3/R2 backends. |\n| `ctx.channels` | `ChannelsService` — email and WhatsApp sends. All messages logged. |\n| `ctx.integrations` | Encrypted credential vault. `ctx.integrations.getActive(provider)` returns decrypted config or null. |\n| `ctx.http` | Typed HTTP client with retries, timeouts, and circuit breakers. |\n| `ctx.realtime` | `RealtimeService` — `notify({ table, id?, action? }, tx?)` after mutations. SSE subscribers receive the event; the frontend hook invalidates matching TanStack queries. |\n\nModules can declare an `init(ctx: ModuleInitCtx)` hook that runs at boot with `{ db, realtime, jobs, scheduler, auth, cli }`. Cross-module callers `import` from `@modules/\u003cname\u003e/service/*` directly — no port shim, no plugin system. Unconfigured services use throw-proxies that produce descriptive errors if accessed.\n\nApp-level config:\n\n```typescript\n// vobase.config.ts\nexport default defineConfig({\n  database: process.env.DATABASE_URL,\n  integrations: { enabled: true },      // opt-in: encrypted credential store\n  storage: {                            // opt-in: file storage\n    provider: { type: 'local', basePath: './data/files' },\n    buckets: { avatars: { maxSize: 5_000_000 }, documents: {} },\n  },\n  channels: {                           // opt-in: email + WhatsApp\n    email: { provider: 'resend', from: 'noreply@example.com', resend: { apiKey: '...' } },\n  },\n  http: {\n    timeout: 10_000,\n    retries: 3,\n    circuitBreaker: { threshold: 5, resetTimeout: 30_000 },\n  },\n  webhooks: {\n    'stripe-events': {\n      path: '/webhooks/stripe',\n      secret: process.env.STRIPE_WEBHOOK_SECRET,\n      handler: 'system:processWebhook',\n      signatureHeader: 'stripe-signature',\n      dedup: true,\n    },\n  },\n})\n```\n\nCredentials stay in `.env`. Config declares the shape.\n\n---\n\n### agent harness\n\nThe harness is the AI runtime in core. It runs on top of `@mariozechner/pi-agent-core` + `@mariozechner/pi-ai` and ships as `createHarness({...})` from `@vobase/core`. Each \"wake\" is one bounded run of an agent over a frozen system prompt.\n\n**Lanes** — the template ships two:\n\n- **Conversation** — bound to `(contactId, channelInstanceId, conversationId)`. Triggered by `inbound_message`, `supervisor`, `approval_resumed`, `scheduled_followup`, `manual`.\n- **Standalone** — operator threads + heartbeats. Triggered by `operator_thread`, `heartbeat`. Customer-facing tools are filtered out.\n\n**Invariants** baked into core:\n\n- *Frozen snapshot.* System prompt computed once at `agent_start`; the `systemHash` is identical every turn so the provider's prefix cache stays warm. Mid-wake writes surface in the next turn's side-load.\n- *Steer/abort between turns.* Customer messages append to the steer queue and drain after `tool_execution_end`. Supervisor and approval-resumed events hard-abort and re-wake.\n- *Tool stdout budget.* 4KB inline → 100KB spill (`/tmp/tool-\u003ccallId\u003e.txt`) → 200KB turn ceiling.\n- *Idle resumption + restart recovery.* The harness recovers orphaned dispatches on boot and resumes idle wakes via journaled events.\n- *Cost cap.* Daily-spend tracking + per-org evaluation gate.\n\n**Workspace** — every wake runs against a virtual filesystem materialized from your modules. AGENTS.md is composed from each module's `agentsMd` contributor (plus per-tool guidance). Read-only paths are enforced at the FS boundary via `ScopedFs`. Memory writes (`/contacts/\u003cid\u003e/MEMORY.md`, `/agents/\u003cid\u003e/MEMORY.md`, `/staff/\u003cid\u003e/MEMORY.md`) flush at turn end.\n\n**LLM provider** — one seam. Bifrost when `BIFROST_API_KEY` + `BIFROST_URL` are set, otherwise direct OpenAI / Anthropic / Google. Use `createModel(alias)` from the template's `~/wake`; never hardcode a provider-prefixed id.\n\n**Testing** — pass `streamFn: stubStreamFn([...])` (inline `AssistantMessageEvent[]` per LLM call) to `bootWake` to keep tests off real providers. Live smoke tests under `tests/smoke/` exercise real keys.\n\n---\n\n### vs the alternatives\n\n| | **Vobase** | **Supabase** | **Pocketbase** | **Rails / Laravel** |\n|---|---|---|---|---|\n| What you get | Full-stack scaffold (backend + frontend + agent harness + skills) | Backend-as-a-service (db + auth + storage + functions) | Backend binary (db + auth + storage + API) | Full-stack framework |\n| Language | TypeScript end-to-end | TypeScript (client) + PostgreSQL | Go (closed binary) | Ruby / PHP |\n| Database | PostgreSQL (Docker Compose local, managed prod) | PostgreSQL (managed) | SQLite (embedded) | PostgreSQL / MySQL |\n| Self-hosted | One process, one container | [10+ Docker containers](https://supabase.com/docs/guides/self-hosting/docker) | One binary | Multi-process |\n| You own the code | Yes — all source in your project | No — managed service | No — compiled binary | Yes — but no AI conventions |\n| AI agent runtime | First-class harness (frozen prompts, tool budget, steer/abort) | Edge functions only | None | None |\n| AI integration | Skills + MCP + canonical module shape | None | None | None |\n| How you customize | Edit the code. AI reads it. | Dashboard + RLS policies | Admin UI + hooks | Edit the code |\n| Hosting cost | As low as $15/mo | $25/mo+ (or complex self-host) | Free (self-host) | Varies |\n| Data isolation | Physical (one db per app) | Logical (RLS) | Physical | Varies |\n| License | MIT | Apache 2.0 | MIT | MIT |\n\n**vs Supabase:** Self-hosted Supabase is [10+ Docker containers](https://supabase.com/docs/guides/self-hosting/docker). RLS policies are hard to reason about. You don't own the backend code. Vobase is one process, you own every line — AI agents can read and modify everything.\n\n**vs Pocketbase:** Pocketbase is a Go binary. You can see the admin UI, but you can't read or modify the internals. When you need custom business logic, you're writing Go plugins or calling external services. Vobase is TypeScript you own — AI agents understand and extend it natively.\n\n**vs Rails / Laravel:** Great frameworks, but they weren't designed for AI coding agents. Vobase's canonical module shape and skill packs mean AI-generated code follows your patterns consistently. Plus: simpler stack (no Redis, single process, TypeScript end-to-end).\n\n---\n\n### runtime architecture\n\nOne Bun process. One Docker container. One app.\n\n```\nDocker container (--restart=always)\n  └── Bun process (PID 1)\n        ├── Hono server\n        │     ├── /api/auth/*    → better-auth (sessions, OTP, CSRF)\n        │     ├── /api/\u003cmod\u003e/*   → module web routes (session-validated)\n        │     ├── /api/cli/*     → CLI catalog + dispatch (HTTP-RPC)\n        │     ├── /mcp           → MCP server (same process, shared port)\n        │     ├── /webhooks/*    → inbound channel webhooks (signature verified, dedup)\n        │     ├── /api/realtime  → SSE stream (LISTEN/NOTIFY → client)\n        │     └── /*             → frontend (static, from dist/)\n        ├── Drizzle (postgres-js → PostgreSQL)\n        ├── Built-in modules (in @vobase/core)\n        │     ├── _auth          → better-auth behind AuthAdapter contract\n        │     ├── _audit         → audit log, record tracking, auth hooks\n        │     ├── _sequences     → gap-free business number counters\n        │     ├── _integrations  → encrypted credential vault, platform OAuth handoff (opt-in)\n        │     ├── _storage       → virtual buckets, local/S3/R2 (opt-in)\n        │     └── _channels      → unified messaging, adapter pattern (opt-in)\n        ├── Template modules (in @vobase/template)\n        │     ├── settings → contacts → team → drive → messaging\n        │     ├── agents → schedules → channels → changes → system\n        │     └── wake/  → agent harness seam (conversation + standalone lanes)\n        ├── pg-boss (Postgres-backed job queue, pg-boss own schema)\n        ├── Outbound HTTP (typed fetch, retries, circuit breakers)\n        └── Audit middleware (all mutations → audit_log)\n```\n\n---\n\n### mcp server\n\nRuns in the same Bun process on the same port. Authenticated via API keys (better-auth apiKey plugin). When you connect Claude Code, Codex, Cursor, or any MCP-compatible tool, it sees your app:\n\n| Tool | What it does |\n|---|---|\n| `list_modules` | List all registered modules (built-in + user) |\n| `read_module` | Read table names from a specific module schema |\n| `get_schema` | List all table names across every module |\n| `view_logs` | Return recent audit log entries |\n\nThe AI sees your exact data model, your existing modules, and the conventions before it writes a single line of code.\n\n---\n\n### deployment\n\nShip a Docker image. Railway, Fly.io, or any Docker host. Set `DATABASE_URL` for a managed Postgres connection.\n\n**Railway (quickest):**\n\n```bash\nrailway up\n```\n\nThe template ships with `Dockerfile` and `railway.json` pre-configured. Add a Postgres plugin and Railway sets `DATABASE_URL` automatically.\n\n**Docker Compose:**\n\n```yaml\n# docker-compose.yml\nservices:\n  vobase:\n    image: your-registry/my-vobase:latest\n    restart: always\n    environment:\n      DATABASE_URL: postgres://user:pass@db:5432/vobase\n    ports:\n      - \"3000:3000\"\n  db:\n    image: pgvector/pgvector:pg17\n    volumes:\n      - pgdata:/var/lib/postgresql/data\n    environment:\n      POSTGRES_DB: vobase\n      POSTGRES_USER: user\n      POSTGRES_PASSWORD: pass\nvolumes:\n  pgdata:\n```\n\n---\n\n### project commands\n\nAfter scaffolding, your project uses standard tools directly — no wrapper CLI:\n\n| Command | What it does |\n|---|---|\n| `docker compose up -d` | Start local Postgres (pgvector/pg17, port 5432). |\n| `bun run dev` | Bun backend with `--hot` and Vite frontend, both via `concurrently`. |\n| `bun run db:push` | Push schema to database (dev). |\n| `bun run db:generate` | Generate migration files for production. |\n| `bun run db:migrate` | Run migrations against the database. |\n| `bun run db:seed` | Seed default admin user and sample data. |\n| `bun run db:reset` | Nuke + push + seed. |\n| `bun run db:studio` | Drizzle Studio for visual database browsing. |\n| `bun run check` | Run every `check:*` (`shape`, `bundle`, `no-auto-nav-tabs`, `shadcn-overrides`). |\n| `bun run test` | Full test suite. `test:e2e` / `test:smoke` for live integration. |\n\n---\n\n### project structure\n\n```\nmy-app/\n  .env\n  .env.example\n  package.json            ← depends on @vobase/core\n  docker-compose.yml      ← local Postgres (pgvector/pg17)\n  drizzle.config.ts\n  vite.config.ts\n  index.html\n  main.ts                 ← ~10-line Bun.serve entry\n  CLAUDE.md               ← project context and guardrails\n  AGENTS.md               ← agent guardrails (mirrors CLAUDE.md)\n  .claude/\n    skills/               ← skill packs the AI reads when generating code\n  auth/                   ← better-auth + plugins\n  runtime/\n    index.ts              ← cross-module primitives, ModuleDef/ModuleInitCtx\n    bootstrap.ts          ← createApp(), worker registration\n    modules.ts            ← static list of modules\n  wake/                   ← agent harness seam (top-level)\n    conversation.ts       ← conversation lane builder\n    standalone.ts         ← standalone lane builder\n    inbound.ts            ← channels:inbound-to-wake handler\n    supervisor.ts         ← messaging:supervisor-to-wake handler\n    operator-thread.ts    ← agents:operator-thread-to-wake handler\n    heartbeat.ts          ← schedules cron-tick callback\n    llm.ts                ← Bifrost / direct provider seam\n    trigger.ts            ← WakeTriggerKind registry\n    workspace/            ← per-wake virtual FS materializers\n    observers/            ← workspace-sync, journal, etc.\n  modules/\n    settings/             ← notification prefs, per-user UI state\n    contacts/             ← customer records + /contacts/\u003cid\u003e/MEMORY.md\n    team/                 ← staff directory + attributes\n    drive/                ← virtual filesystem; modules register overlays\n    messaging/            ← conversations, messages, notes, supervisor fan-out\n    agents/               ← definitions, learned skills, staff memory, scores\n    schedules/            ← agent_schedules + cron heartbeats\n    channels/             ← umbrella for adapters/{web,whatsapp,...}\n    changes/              ← generic propose/decide/apply/history\n    system/               ← ops dashboard, dev helpers\n    \u003ceach module\u003e/\n      module.ts           ← thin aggregator\n      schema.ts\n      state.ts\n      service/            ← sole writer of this module's tables\n      handlers/\n      web.ts\n      pages/              ← React pages (TanStack file-based routes)\n      components/\n      hooks/\n      jobs.ts\n      agent.ts            ← tools, materializers, roHints, AGENTS.md fragments\n      tools/              ← defineAgentTool\n      verbs/              ← defineCliVerb\n      cli.ts              ← \u003cmodule\u003eVerbs barrel\n      seed.ts\n      defaults/           ← *.agent.yaml, *.schedule.yaml\n      skills/             ← inline skill bodies\n  src/                    ← frontend shell only\n    main.tsx\n    routeTree.gen.ts      ← generated TanStack route tree\n    lib/\n      api-client.ts       ← Hono RPC client\n    components/\n      ui/                 ← shadcn/ui (owned by you)\n      ai-elements/        ← AI chat UI components (owned by you)\n      data-table/         ← DiceUI data-table components\n    shell/\n      app-layout.tsx      ← main app shell with sidebar\n      command-palette.tsx\n      auth/\n      settings/\n    hooks/\n    styles/\n    stores/\n  tests/\n    e2e/                  ← real Postgres\n    smoke/                ← live server, real LLM key\n  data/\n    files/                ← optional, created on first upload\n```\n\n---\n\n## Star History\n\n\u003ca href=\"https://www.star-history.com/?repos=vobase%2Fvobase\u0026type=timeline\u0026logscale=\u0026legend=top-left\"\u003e\n \u003cpicture\u003e\n   \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://api.star-history.com/image?repos=vobase/vobase\u0026type=timeline\u0026theme=dark\u0026legend=top-left\" /\u003e\n   \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://api.star-history.com/image?repos=vobase/vobase\u0026type=timeline\u0026legend=top-left\" /\u003e\n   \u003cimg alt=\"Star History Chart\" src=\"https://api.star-history.com/image?repos=vobase/vobase\u0026type=timeline\u0026legend=top-left\" /\u003e\n \u003c/picture\u003e\n\u003c/a\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://i.imgur.com/I5EeSBh.png\"\u003e\nStar if the repo has helped you\n\u003c/p\u003e\n\n---\n\n### license\n\nMIT. Own everything.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvobase%2Fvobase","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvobase%2Fvobase","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvobase%2Fvobase/lists"}