Contributing

This guide gets a developer from a fresh clone to a running local stack in < 10 minutes, then sketches the architecture so you can land a useful PR.

Repository layout

octopuslab/
├─ apps/
│  ├─ web/        ← Next.js 16 (App Router, RSC, next-intl)
│  └─ api/        ← NestJS 11 (CJS), Drizzle ORM, postgres.js
├─ packages/
│  ├─ db/         ← Drizzle schema, shared types
│  ├─ ui/         ← design tokens, primitive components
│  ├─ config/     ← shared TS / ESLint / Prettier
│  └─ db/sql/     ← hand-written SQL (RLS, GRANTs) Drizzle can't emit
└─ pnpm-workspace.yaml

Prerequisites

  • Node 22 LTS. nvm use 22 if you have nvm.
  • pnpm 9+. corepack enable to pick up the version pinned in package.json.
  • A Supabase project (free is fine). You'll need the DB connection string, the project URL, and the service-role key.
  • A Railway account if you want to deploy the API end-to-end; for local dev you can run the API directly with pnpm dev.

First-time setup

git clone git@github.com:jarwiscode/OctopusLab.git
cd OctopusLab
pnpm install

Copy .env.example to .env.local in both apps/web and apps/api and fill in:

  • DATABASE_URL — Supabase pooler URL (not the direct connection; our Drizzle config expects the pooler).
  • SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY.
  • OPENROUTER_API_KEY if you want the AI to actually run; otherwise the chat endpoint returns 503 cleanly.
  • BYOK_ENCRYPTION_KEY — 32 raw bytes base64-encoded (openssl rand -base64 32).

Migrations

Schema lives in packages/db/src/schema/. Drizzle Kit handles the regular flow:

# add / modify a table in packages/db/src/schema/*.ts, then:
pnpm --filter @octopuslab/db drizzle-kit generate
pnpm --filter @octopuslab/db drizzle-kit push

For anything Drizzle can't emit — RLS toggles, GRANT / REVOKE, check constraints, triggers — append to packages/db/sql/0001_enable_rls.sql or a new 000X_…sql and apply via the Supabase MCP or dashboard. See packages/db/sql/README.md for the rationale.

Run the stack

# in two terminals:
pnpm --filter @octopuslab/api dev
pnpm --filter @octopuslab/web dev

Web on localhost:3000, API on localhost:8080. The web is wired to the local API when NEXT_PUBLIC_API_URL=http://localhost:8080.

Architecture

  • Web — Next.js 16 App Router. Server Components for marketing + shells; Client Components everywhere there's state. next-intl drives the EN/RU locale switcher with localePrefix: 'as-needed'. TanStack Query is the only data-fetching layer in the client (no SWR, no axios).
  • API — NestJS 11 with Zod env validation. All persistence goes through Drizzle queries against the Supabase Postgres pooler. Auth: Supabase JWT for dashboard routes, an HMAC-checked API key for /api/v1/public/*.
  • RLS — every table in public has RLS enabled but no policies (deny-all). The API uses SERVICE_ROLE_KEY which bypasses RLS. If you add a table, append it to packages/db/sql/0001_enable_rls.sql and re-apply.
  • AI — OpenRouter streaming SSE with tool-calling loop. Project files live in a per-project Vercel Sandbox microVM.
  • Payments — xRocket Pay + NOWPayments webhooks land at POST /api/v1/billing/webhook / …/nowpayments-webhook with HMAC verification on raw body.

Style + checks

pnpm typecheck   # all workspaces
pnpm lint        # ESLint flat config
pnpm test        # vitest where applicable

TS strict mode is on. We don't accept PRs that disable rules without a comment explaining why. New code follows the existing patterns: server-side Zod for input, Drizzle query builder (no raw SQL except in packages/db/sql/), small focused commits, conventional commit messages.

Deploy targets

  • web → Vercel. main auto-deploys. PR previews via the GitHub integration.
  • api → Railway. Service @octopuslab/api, Dockerfile at apps/api/Dockerfile. Healthcheck on /api/v1/health.
  • db → Supabase. Migrations applied via the Supabase MCP or supabase db push.

Issues

Repo: github.com/jarwiscode/OctopusLab. Open an issue for bugs; for features sketch the change in a discussion first so we can agree on the shape before you write code.