92ab41404801334c86b03d8fcfba0cb1c8965a8d
GrowQR — backend + frontend
A multi-agent platform where every user gets a private Grow Agent (Rivet Kit actor) that orchestrates sub-agents and owns a per-user OpenCode + Gitea Docker stack. See docs/PRD.md for the product spec.
What's wired up
- Auth: Clerk (frontend + backend JWT verification).
- DB: Postgres + Drizzle (users, actor registry, container mappings, repos, OpenCode sessions, events).
- Actors: Rivet Kit —
growAgentper user (master) andsubAgent(worker), with a real OpenCode Zen / OpenAI-compatible tool-use loop. - Per-user containers: Gitea (memory repo) + OpenCode (workflow execution), spawned via
dockerode, with admin user + access token bootstrap, ports allocated from a managed pool, lifecycle reconciled on backend boot. - Frontend: Next.js 16 with
@clerk/nextjsfor auth andrivetkit/clientfor direct actor connections + event streaming. - Tool surface available to the Grow Agent:
spawn_sub_agent,commit_memory,read_memory,list_memory.
One-time setup
You need three external accounts before running:
- Clerk — create an app at https://dashboard.clerk.com → copy the publishable + secret keys.
- OpenCode Zen — create an API key at https://opencode.ai/auth.
- Docker — Docker Desktop (or any daemon
dockerodecan reach via/var/run/docker.sock).
Then:
# Backend env
cp .env.example .env
# fill in CLERK_SECRET_KEY, CLERK_PUBLISHABLE_KEY, OPENCODE_API_KEY
# Frontend env
cd growqr-frontend
cp .env.example .env.local
# fill in NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY
cd ..
Running
# 1. Start Postgres + Rivet engine (per-user OpenCode/Gitea containers are
# spawned dynamically by the backend when a user signs in).
docker compose up -d postgres rivet-engine
# 2. Install + migrate
npm install
npm run db:migrate
# 3. Backend
npm run dev # http://localhost:4000
# 4. Frontend (separate terminal)
cd growqr-frontend
npm install
npm run dev # http://localhost:3000
Open http://localhost:3000, sign up, verify your email, and the home page will:
- Mirror your Clerk user into Postgres.
- Spawn your dedicated Gitea + OpenCode containers (first run pulls images — ~20–40s).
- Connect the Grow Agent chat to your dedicated Rivet actor.
- Stream agent + sub-agent events back to the UI.
Architecture
Browser
│ Clerk JWT
▼
Next.js (3000) ──fetch──▶ /users/bootstrap, /users/me (Hono on 4000)
│
└─Rivet client──▶ /api/rivet/* (Hono → registry.handler)
│
▼
Grow Agent actor (one per user)
│
├─ OpenCode Zen (Kimi K2.6 + tool use)
├─ commit_memory ────▶ Gitea container (per user)
└─ spawn_sub_agent ────▶ OpenCode container (per user)
(multiplexed sessions)
Useful commands
npm run typecheck # backend
npm run db:generate # diff schema → new migration
npm run db:studio # browse Postgres via Drizzle Studio
cd growqr-frontend
npx tsc --noEmit # frontend types
npm run lint
Troubleshooting
- "missing bearer token" from
/users/bootstrap— Clerk session not attached. Sign out and back in. Gitea did not become readyduring provisioning — Gitea takes 10–20s on first pull. Wait, thenPOST /actors/provision(the frontend retries via polling).- OpenCode container exits immediately — check
OPENCODE_IMAGE. The backend starts containers withCmd: ["opencode", "serve", ...]; if you swap images, ensure they expose the OpenCode HTTP surface on:4096. No free ports in USER_PORT_RANGE— bumpUSER_PORT_RANGE_ENDin.envor stop unused user stacks viaPOST /actors/stop.
PRD status
All MVP items in docs/PRD.md §9 are implemented:
- User auth (Clerk)
- Actor registry (Postgres + Rivet)
- One Grow Agent Rivet Kit actor per user
- Sub-agent registration under the Grow Agent
- Per-user Gitea Docker + memory repo
- Memory commits into the user's Gitea
- Per-user OpenCode Docker + session API
- Message endpoint from frontend to Grow Agent (via Rivet)
- Event stream from agent to frontend (via Rivet broadcast)
- Frontend: login, master chat, sub-agent progress, channel-style logs
Payments and the full quest/pathway runner are deferred to v2 (PRD §10).
Description
Languages
TypeScript
95.8%
Dockerfile
2.8%
Shell
1.4%