Adds logical multi-tenancy on top of Coolify + Gitea so every Vibn
account gets its own isolated tenant boundary, and exposes that
boundary to AI agents (Cursor, Claude Code, scripts) through
per-workspace bearer tokens.
Schema (additive, idempotent — run /api/admin/migrate once after deploy)
- vibn_workspaces: slug, name, owner, coolify_project_uuid,
coolify_team_id (reserved for when Coolify ships POST /teams),
gitea_org, provision_status
- vibn_workspace_members: room for multi-user workspaces later
- vibn_workspace_api_keys: sha256-hashed bearer tokens
- fs_projects.vibn_workspace_id: nullable FK linking projects
to their workspace
Provisioning
- On first sign-in, ensureWorkspaceForUser() inserts the row
(no network calls — keeps signin fast).
- On first project create, ensureWorkspaceProvisioned() lazily
creates a Coolify Project (vibn-ws-{slug}) and a Gitea org
(vibn-{slug}). Failures are recorded on the row, not thrown,
and POST /api/workspaces/{slug}/provision retries.
Auth surface
- lib/auth/workspace-auth.ts: requireWorkspacePrincipal() accepts
either a NextAuth session or "Authorization: Bearer vibn_sk_...".
The bearer key is hard-pinned to one workspace — it cannot
reach any other tenant.
- mintWorkspaceApiKey / listWorkspaceApiKeys / revokeWorkspaceApiKey
Routes
- GET /api/workspaces list
- GET /api/workspaces/[slug] details
- POST /api/workspaces/[slug]/provision retry provisioning
- GET /api/workspaces/[slug]/keys list keys
- POST /api/workspaces/[slug]/keys mint key (token shown once)
- DELETE /api/workspaces/[slug]/keys/[keyId] revoke
UI
- components/workspace/WorkspaceKeysPanel.tsx: identity card,
keys CRUD with one-time secret reveal, and a "Connect Cursor"
block with copy/download for:
.cursor/rules/vibn-workspace.mdc — rule telling the agent
about the API + workspace IDs + house rules
~/.cursor/mcp.json — MCP server registration with key
embedded (server URL is /api/mcp; HTTP MCP route lands next)
.env.local — VIBN_API_KEY + smoke-test curl
- Slotted into existing /[workspace]/settings between Workspace
and Notifications cards (no other layout changes).
projects/create
- Resolves the user's workspace (creating + provisioning lazily).
- Repos go under workspace.gitea_org (falls back to GITEA_ADMIN_USER
for backwards compat).
- Coolify services are created inside workspace.coolify_project_uuid
(renamed {slug}-{appName} to stay unique within the namespace) —
no more per-Vibn-project Coolify Project sprawl.
- Stamps vibn_workspace_id on fs_projects.
lib/gitea
- createOrg, getOrg, addOrgOwner, getUser
- createRepo now routes /orgs/{owner}/repos when owner != admin
Also includes prior-turn auth hardening that was already in
authOptions.ts (CredentialsProvider for dev-local, isLocalNextAuth
cookie config) bundled in to keep the auth layer in one consistent
state.
.env.example
- Documents GITEA_API_URL / GITEA_API_TOKEN / GITEA_ADMIN_USER /
GITEA_WEBHOOK_SECRET and COOLIFY_URL / COOLIFY_API_TOKEN /
COOLIFY_SERVER_UUID, with the canonical hostnames
(git.vibnai.com, coolify.vibnai.com).
Post-deploy
- Run once: curl -X POST https://vibnai.com/api/admin/migrate \\
-H "x-admin-secret: \$ADMIN_MIGRATE_SECRET"
- Existing users get a workspace row on next sign-in.
- Existing fs_projects keep working (legacy gitea owner + their
own per-project Coolify Projects); new projects use the
workspace-scoped path.
Not in this commit (follow-ups)
- Wiring requireWorkspacePrincipal into the rest of /api/projects/*
so API keys can drive existing routes
- HTTP MCP server at /api/mcp (the mcp.json snippet already
points at the right URL — no client re-setup when it lands)
- Backfill script to assign legacy fs_projects to a workspace
Made-with: Cursor
62 lines
2.9 KiB
Plaintext
62 lines
2.9 KiB
Plaintext
# Copy to Coolify environment variables (or .env.local for dev). Do not commit secrets.
|
|
|
|
# --- Postgres: local `next dev` (Coolify internal hostnames do NOT work on your laptop) ---
|
|
# npm run db:local:up then npm run db:local:push with:
|
|
# DATABASE_URL=postgresql://vibn:vibn@localhost:5433/vibn
|
|
# POSTGRES_URL=postgresql://vibn:vibn@localhost:5433/vibn
|
|
|
|
# --- Postgres: production / Coolify (from Coolify UI, reachable from where the app runs) ---
|
|
# Coolify: open the Postgres service → expose/publish a host port → use SERVER_IP:HOST_PORT (not internal UUID host).
|
|
# From repo root, master-ai/.coolify.env with COOLIFY_URL + COOLIFY_API_TOKEN: npm run db:sync:coolify
|
|
# Example shape: postgresql://USER:PASSWORD@34.19.250.135:YOUR_PUBLISHED_PORT/vibn
|
|
# External/cloud: set DB_SSL=true if the DB requires TLS.
|
|
DATABASE_URL=
|
|
POSTGRES_URL=
|
|
|
|
# --- Public URL of this Next app (OAuth callbacks, runner callbacks) ---
|
|
# Local Google OAuth (must match the host/port you open in the browser):
|
|
# NEXTAUTH_URL=http://localhost:3000
|
|
# Google Cloud Console → OAuth client → Authorized redirect URIs (exact):
|
|
# http://localhost:3000/api/auth/callback/google
|
|
# If you use 127.0.0.1 or another port, use that consistently everywhere.
|
|
# Prisma adapter needs Postgres + tables: set DATABASE_URL then run: npx prisma db push
|
|
NEXTAUTH_URL=https://vibnai.com
|
|
NEXTAUTH_SECRET=
|
|
|
|
# --- vibn-agent-runner (same Docker network: http://<service-name>:3333 — or public https://agents.vibnai.com) ---
|
|
AGENT_RUNNER_URL=http://localhost:3333
|
|
|
|
# --- Shared secret: must match runner. Required for PATCH session + POST /events ingest ---
|
|
AGENT_RUNNER_SECRET=
|
|
|
|
# --- Optional: one-shot DDL via POST /api/admin/migrate ---
|
|
# ADMIN_MIGRATE_SECRET=
|
|
|
|
# --- Gitea (git.vibnai.com) — admin token used to create per-workspace orgs/repos ---
|
|
# Token must have admin scope to create orgs. Per-workspace repos are created
|
|
# under "vibn-{workspace-slug}" orgs; legacy projects remain under GITEA_ADMIN_USER.
|
|
GITEA_API_URL=https://git.vibnai.com
|
|
GITEA_API_TOKEN=
|
|
GITEA_ADMIN_USER=mark
|
|
GITEA_WEBHOOK_SECRET=
|
|
|
|
# --- Coolify (coolify.vibnai.com) — admin token used to create per-workspace Projects ---
|
|
# Each Vibn workspace gets one Coolify Project (named "vibn-ws-{slug}") that
|
|
# acts as the tenant boundary. All apps + DBs for that workspace live there.
|
|
COOLIFY_URL=https://coolify.vibnai.com
|
|
COOLIFY_API_TOKEN=
|
|
COOLIFY_SERVER_UUID=jws4g4cgssss4cw48s488woc
|
|
|
|
# --- Google OAuth / Gemini (see .google.env locally) ---
|
|
GOOGLE_CLIENT_ID=
|
|
GOOGLE_CLIENT_SECRET=
|
|
|
|
# --- Local dev: skip Google (next dev only) ---
|
|
# NEXT_PUBLIC_DEV_LOCAL_AUTH_EMAIL=you@example.com
|
|
# Skip NextAuth session for API + project UI (same email must own rows in fs_users)
|
|
# NEXT_PUBLIC_DEV_BYPASS_PROJECT_AUTH=true
|
|
# Optional: require password for dev-local provider (omit to allow localhost Host only)
|
|
# DEV_LOCAL_AUTH_SECRET=
|
|
# Optional display name for the dev user row
|
|
# DEV_LOCAL_AUTH_NAME=Local dev
|