Synaplan
開源 AI 平台,供企業使用:自行託管的 AI 代理、符合 GDPR 的聊天機器人小工具、RAG 知識庫、WhatsApp 與 Outlook AI、MCP、多模型路由。Docker、Kubernetes 或託管於 Germ
AI 翻譯
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Organization-scoped collaboration and idea tracking. Organizations contain users, boards, statuses and ideas; boards arrange ideas by status in swimlanes, and an idea promoted out of a board becomes an issue that moves through sprints.
Stack: TypeScript · Next.js · Nest.js · Prisma · PostgreSQL 16 · Tailwind v4 + shadcn/ui · Vitest + Playwright · pnpm workspaces + Turborepo · deployed on Vercel
corepack enable && corepack prepare [email protected] --activate
pnpm install
You need Node ≥ 24.20 and pnpm ≥ 12.3.4. pnpm install fails on an engine mismatch rather
than installing something that will not run.
packages/infrastructure runs prisma generate on postinstall, so the Prisma client is built for
you. It reads the schema and does not need a reachable database.
| You need | Good for | |
|---|---|---|
| A local container (default) | Docker | Ordinary development. Disposable, offline, and yours alone |
| A PostgreSQL 16 you already run | Nothing more | A machine that already has one |
| Vercel Postgres | A Vercel account | A machine that cannot run a database server, or sharing one database across machines |
All three are the same setting — DATABASE_URL in .env at the repository root — and pnpm start
behaves accordingly. The next section covers each.
pnpm start
That is the whole thing: Next on http://localhost:3000, Nest on http://localhost:3001/api/v1, and a migrated and seeded PostgreSQL behind them. Ctrl+C stops both. If either port is taken, that server moves up to the next free one and the launcher prints where it went.
tools/local/start.ts is what it runs, and it is idempotent — copy .env
from the example if it is missing, start the postgres compose service only if nothing is
already listening at the host and port DATABASE_URL names, build, prisma migrate deploy, seed,
then run both halves. Run it as often as you like. It reloads the web app on save; re-run it after
changing apps/api, which it runs as built output rather than under a watcher.
It refuses to run against anything but your own machine. The script migrates and seeds whatever
DATABASE_URL names, so a host that is not loopback stops it before the first write, quoting the
address it read — see Against Vercel Postgres for the deliberate way
past that.
pnpm start starts the container for you. To run only the database (Docker Desktop must be
running; the service is postgres, the container collega-postgres, from
docker-compose.yml):
docker compose up -d postgres # start, detached; the data lives in the postgres-data volume
docker compose ps # "healthy" once pg_isready passes (a few seconds)
docker compose logs -f postgres # follow the logs
docker compose stop postgres # stop, keeping the data
docker compose down -v # remove it and its data (destroys all local data)
POSTGRES_PASSWORD must be set in .env (copy it from .env.example first); the container reads
it, and POSTGRES_USER / POSTGRES_HOST_PORT (default 5432) too. The container publishes
localhost:${POSTGRES_HOST_PORT}, so stop any other PostgreSQL on that port or change the port.
.env is gitignored and copied from .env.example, whose defaults are placeholders
for a throwaway local container. Two are worth knowing about:
POSTGRES_USER is collega, not postgres. The container creates exactly one login role, so
psql -U postgres fails with role "postgres" does not exist.ANTHROPIC_API_KEY may be left empty, which runs AI-assisted idea drafting dark rather than
broken (SPEC/20-feature-ai-idea-assist.md rule 31).For a machine that cannot run a database server, or to share one database between machines. It is
the same pnpm start; only DATABASE_URL changes, plus one flag that says you meant it.
1. Get the connection string out of Vercel, into a scratch file rather than over your .env:
cd apps/api && vercel link # once, to collega-api
vercel env pull /tmp/collega-preview.env --environment=preview
vercel env pull writes every variable for that environment, SITE_ADMIN_PASSWORD and
ANTHROPIC_API_KEY included. Pulling it straight onto .env would overwrite your local one and put
deployment credentials on disk, so pull it somewhere else and copy across the single line you want.
2. Find the direct connection string. Storage integrations inject several keys and the names
differ by provider. Take the one whose value begins postgresql:// — that is the direct connection.
A prisma+postgres:// value is the Accelerate proxy, and prisma migrate deploy cannot run through
it, so it is not the one you want here.
3. Put it in .env at the repository root, as DATABASE_URL:
DATABASE_URL=postgresql://…
.env is the file this repository reads — both tools/local/start.ts and
the Nest host load it from the repository root, and neither reads .env.local. An exported
DATABASE_URL in your shell wins over the file, if you would rather not write it down at all.
4. Run it:
COLLEGA_ALLOW_REMOTE_DATABASE=1 pnpm start
Docker is never involved: start.ts reads the host out of the connection string, sees it is not this
machine, and does not start a container that the string would not reach anyway.
Why the flag exists, and why it should stay a flag. pnpm start runs prisma migrate deploy and
then the demo seed against whatever DATABASE_URL names. On a staging database that is exactly
right. On a production one it is a catastrophe, and the two differ by a string in a file. So the
script refuses a non-loopback host outright and COLLEGA_ALLOW_REMOTE_DATABASE=1 is the way to say
you meant it — per command, not exported in a shell profile, where it would silently apply to
every run afterwards.
Two consequences worth expecting:
DEMO_PASSWORD from
scenario.ts, which is in this
repository. A database holding that data must never be one that matters.Nearly everything reads the API. Sign-in, boards and board detail, the ideas list and the idea inspector, delivery, and the settings screens are all live against the real database, including the writes: author an idea, move a card between lanes, toggle an upvote, promote an idea to an issue.
Two things are not finished:
| The AI-assist admin screens — prompt editor, probes, usage | Read from the API since slice 132, but their forms are inert: nothing is saved from them yet. |
| Outcomes and the roadmap | Empty, deliberately. They are Slice 2 of Issues-and-Delivery: no table, no entity, no route. SPEC/30-Contracts.md says so outright, and apps/web/lib/data/delivery.ts explains why empty beats invented. |
Which of the two a screen gets is decided in apps/web/lib/data/ and nowhere
else — a reader there is either a fetch or a fixture, and the call sites cannot tell. Wave-by-wave
status lives in SPEC/implementation-agent-tracker.md.
pnpm start # the whole application — API, web, database
pnpm dev # an alias for `pnpm start`; there is no web-only mode
pnpm check # lint + typecheck + test + build — the gate
pnpm build
pnpm test # every package except the Playwright suite
pnpm test:e2e # Playwright; needs a running app
pnpm lint:fix # Biome, with fixes applied
pnpm check is what "green" means — Biome (lint, format, and the layer-boundary rules), tsc
across every package, Vitest, and next build. Run it before calling anything done. A single
package: pnpm --filter @collega/api test.
The build is in the gate because tsc cannot stand in for it: a 'use client' file that reaches a
server-only module through a barrel typechecks cleanly and then fails to build. Turbo caches it, so
a second run costs nothing.
Turborepo caches aggressively, and a cached pass has twice hidden a real regression in this repository. When you need a result you can trust, force it:
pnpm check --force
That reports 0 cached and is the only form worth quoting as evidence.
Prisma owns the schema
(packages/infrastructure/prisma/schema.prisma) —
28 models, 14 enums. Frozen at conversion slice S0.2, and changed since only by the amendments
recorded in SPEC/decisions.md.
pnpm --filter @collega/infrastructure db:generate # regenerate the client from the schema
pnpm --filter @collega/infrastructure db:migrate # prisma migrate deploy
pnpm --filter @collega/infrastructure db:seed # the demo data
pnpm start runs all three for you. You only need them by hand when working against a database it
did not create.
db:seed | The demo data — two organizations, one account per role each, boards, ideas, comments, sprints, issues, and (in the three vertical organizations) Board References, Outcomes and board sprints | Refuses to run when NODE_ENV=production. |
db:bootstrap-admin | One App Admin, so a fresh deployment is reachable at all | Safe in production; it is what the API's deploy build runs |
db:bootstrap-organization | One empty organization with an admin | Safe in production |
The demo seed rebuilds from committed code in about four seconds, which is why nothing in a local
database is worth protecting. To start over (destroys all local data): docker compose down -v,
then pnpm start.
The Nest host composes DATABASE_URL from the POSTGRES_* parts when it is unset, so a password has
one home. The Prisma CLI cannot — migrate, db pull and studio read env("DATABASE_URL")
straight out of the schema — which is why pnpm start writes the composed value back into .env the
first time. In deployment it is set verbatim, which is the shape Prisma Postgres hands over.
Two Vercel projects from this one repository, each rooted at its own app directory, each with its own
vercel.json:
| Project | Root directory | Framework |
|---|---|---|
collega | apps/web | Next.js |
collega-api | apps/api | Nest.js |
Both install and build from the workspace root (cd ../.. && pnpm install --frozen-lockfile), so
Turborepo resolves the package graph exactly as it does locally. The API's build command also runs
db:migrate and db:bootstrap-admin, which is what makes a newly provisioned database usable
without a manual step.
| Variable | Where | Notes |
|---|---|---|
DATABASE_URL | collega-api | Set verbatim; the Prisma CLI cannot compose it from parts |
COLLEGA_API_URL | collega | The API's origin. The web app is HTTP-only and reaches the API through this and nothing else |
SITE_ADMIN_EMAIL, SITE_ADMIN_PASSWORD | collega-api | Consumed by db:bootstrap-admin at build time |
ANTHROPIC_API_KEY | collega-api | Optional. Absent means the AI feature runs dark |
Three environments, and the thing worth getting right is which database each one writes to.
| Environment | Database | Who reaches it |
|---|---|---|
| Production | The production Prisma Postgres | collega-api on main |
| Preview | A separate staging database | Preview deployments, and a developer with no local PostgreSQL |
| Local | A postgres:16 container, via docker compose | pnpm start |
Status: the staging database is not provisioned yet. Until it is, Preview shares Production's database — so every schema change already lands there from a preview build, which is the reason to do this before the next migration rather than after. To provision it:
npm i -g vercel && vercel login
cd apps/api && vercel link # link to collega-api
# No --prefix. That flag is what leaves the app with no DATABASE_URL to find.
vercel install prisma-postgres --name collega-dev --environment preview
vercel env ls # confirm DATABASE_URL is there
Three things will bite otherwise. The first one stops the deploy; the other two are quieter.
One variable name, every environment. DATABASE_URL is the only connection string this
repository reads — schema.prisma reads env("DATABASE_URL"), and the Nest host reads that or the
unprefixed POSTGRES_* parts and nothing else. Vercel scopes variables per environment, so the same
name holds the production database in Production and the staging one in Preview. That is the whole
mechanism; nothing needs to know which environment it is in.
So connect the store without a prefix. --prefix exists to avoid collisions with variables you
already have, and it is the thing that breaks this: a prefixed store injects
<PREFIX>_POSTGRES_URL and friends, which nothing here reads, so db:migrate and
db:bootstrap-admin find no connection string and the build fails at the migrate step. Unprefixed,
the integration writes DATABASE_URL itself — and keeps it current if the store ever rotates its
credentials.
Copying the value into a DATABASE_URL of your own also works and is the fallback when the
integration will not write that name. Know what it costs: it is a snapshot, so a credential rotation
updates the store's variable and leaves your copy pointing at the old one.
Either way, take the postgres:// value, not the prisma+postgres:// one — that is the
Accelerate protocol and needs an extension this workspace does not install.
The staging database gets Production's App Admin. The API's build command ends with
db:bootstrap-admin, so the first preview build after provisioning migrates and bootstraps the new
database on its own — which is what you want. But that script reads SITE_ADMIN_EMAIL and
SITE_ADMIN_PASSWORD, and unless those are scoped per-environment it installs the production
credential into staging, which is the separation you were trying to create. Set Preview-scoped
throwaway values first.
The demo seed will never run on Vercel, by design. prisma/seed/index.ts throws when
NODE_ENV === 'production', and Vercel sets that for every build, preview included. So a freshly
provisioned staging database comes up with the bootstrap admin and nothing else: no organizations,
no boards, no ideas. Demo data goes in from a shell where NODE_ENV is unset — yours:
vercel env pull /tmp/collega-preview.env --environment=preview
DATABASE_URL='<the postgresql:// value from that file>' pnpm --filter @collega/infrastructure db:seed
Pull to a scratch path, not onto .env — the pull carries every variable for that environment,
deployment credentials included. See Against Vercel Postgres for the
same steps in more detail.
Developers point at this same staging database when they cannot run one locally — Against Vercel Postgres under "Run it locally" has those steps.
The API's entrypoint is apps/api/server.js, and the bootstrap is src/bootstrap.ts — not
src/main.ts. Vercel's NestJS preset resolves an entrypoint in a fixed order, and every src/
candidate (main, app, index, server) outranks every root-level one. While src/main.ts
existed, the preset compiled it with its own toolchain and server.js was never consulted.
Reintroducing src/main.ts, or renaming server.js, silently hands the deployment back to a
source-compiled artifact. The file's own header comment says this too; read it before touching
either name.
maxDuration lives in the project's settings, not in vercel.json. A functions block is
rejected before the build starts unless its patterns match source files inside an api/ directory,
which this layout has none of.
Both projects set an ignoreCommand that asks Turborepo whether the app is affected by the commit.
It reasons correctly on source changes — a commit touching only packages/application genuinely
does not affect apps/web, which cannot import it. The gap is environment-variable changes: they
leave no diff, so the ignore step sees an unaffected app and cancels a build you actually wanted.
To force one, push a commit that touches a file under apps/api/ (for the API) or apps/web/ (for
the web app) — any file, a comment or markdown will do. An empty commit or a Redeploy touches no
path and is cancelled the same way; apps/web/AGENTS.md has the detail.
.github/workflows/ci.yml runs pnpm check on every pull request and on
pushes to dev and main. It deliberately does not set DATABASE_URL: packages/infrastructure
guards a suite with skipIf(!DATABASE_URL), so the variable's mere presence stops it skipping and it
then tries to reach a database CI does not have.
Branches flow feature/<NNN>-<short-description> → dev → main.
apps/
web Next.js client — HTTP only, imports @collega/design-system and nothing else
api Nest.js host — the only thing that talks to the database
packages/
domain Entities, enums, value objects, invariants — depends on nothing
application Use cases, authorization, validation — depends on domain
infrastructure Prisma persistence, integrations — implements domain/application ports
design-system Comp P tokens and primitives, on Tailwind v4 + shadcn/ui
e2e/ Playwright suite
tools/
local/start.ts `pnpm start` — database, API and web in one command
golden Capture/replay harness — the conversion's regression detector
boundaries Architecture tests over the layer rules
arch Architecture assertions lint cannot express (the identity chokepoint)
demo-shots Screenshots the demo deck is built from
prompt-eval The AI-assist evaluation corpus, its runner and the v1 baseline
SPEC/ Canonical specs — the source of truth
SPEC/mockups/ UI comps; comp-q-*.html is the locked reference rendering
Dependencies flow inward, and the boundaries are enforced rather than conventional:
biome.json's noRestrictedImports overrides fail the lint run on a cross-layer import, and
tools/boundaries is an architecture test over those rules — it writes probe files and confirms
Biome reports them, because a lint config that looks right and enforces nothing is the failure mode
that actually happened here. apps/web reaching into packages/application is a lint error,
deliberately.
Business rules live in domain and application — never in controllers or React components.
The UI is locked to comp P, rendered as comp Q on Tailwind v4 + shadcn/ui. Structure is
settled; the palette is five themes with a picker, Graphite (dark) by default.
SPEC/mockups/comp-q-*.html are self-contained pages you can open in a browser — they are the
reference, and the theme in
packages/design-system/src/globals.css is carried over
from SPEC/mockups/_build/q.css; apps/web/app/globals.css only imports it. Change the palette in both or the comps stop being a reference.
If a page's layout is not settled, produce a throwaway comp in SPEC/mockups/ for review before
writing production React against an undecided design.
pnpm start seeds these accounts. Sign in at http://localhost:3000/login (or the web port
pnpm start prints, if 3000 was taken).
Every seeded account's password is Abc123!. It is development only and published here on
purpose — DEMO_PASSWORD in
packages/infrastructure/src/demo-seed/modules/scenario.ts —
so a database holding this data must never be one that matters.
| Password | Name | Role | Organization | |
|---|---|---|---|---|
[email protected] | Abc123! | Sam Sitewide | App Admin | (none — every organization) |
[email protected] | Abc123! ¹ | Site Administrator | App Admin | (none — every organization) |
[email protected] | Abc123! | Olivia Administer | Org Admin | Acme Robotics |
[email protected] | Abc123! | Noah Contributor | User | Acme Robotics |
[email protected] | Abc123! | Maya Collaborator | User | Acme Robotics |
[email protected] | Abc123! | Rosa Observer | Read Only | Acme Robotics |
[email protected] | Abc123! | Olivia Administer | Org Admin | Blue Harbor Logistics |
[email protected] | Abc123! | Noah Contributor | User | Blue Harbor Logistics |
[email protected] | Abc123! | Maya Collaborator | User | Blue Harbor Logistics |
[email protected] | Abc123! | Rosa Observer | Read Only | Blue Harbor Logistics |
[email protected] | Abc123! | Priya Raman | Org Admin | Pinecone Labs |
[email protected] | Abc123! | Daniel Okafor | User | Pinecone Labs |
[email protected] |
¹ The configured App Admin, created from SITE_ADMIN_EMAIL / SITE_ADMIN_PASSWORD in .env
(the values above are .env.example's). Unlike the others it must change its password at first
sign-in. A .env copied before 2026-10-04 still says Ch4ngeMe!Now, and an account already
created keeps the password it was created with — the seed never overwrites one.
What each role can do:
SiteAdmin role in code and specs) — every organization; creates organizations and users directly, and changes
organization content (boards, ideas) only through View As.The full roster — every address, display name and organization — is demo.md.
password authentication failed for user "collega" even though .env is correct
POSTGRES_PASSWORD is applied only when the data directory is first initialized, so an old
credential survives in an existing volume. Reset it in place without losing data — the official image
initializes pg_hba.conf with local all all trust, so this needs no password:
docker exec -it collega-postgres \
psql -U collega -d postgres -c "ALTER USER collega WITH PASSWORD '<your-password>';"
-d postgres is not optional: psql defaults to a database named after the connecting user, and no
collega database exists — the application's is Collega. To start over instead
(destroys all local data): docker compose down -v.
pnpm check fails on apps/web/next-env.d.ts
That file is generated, and next dev and next build write different versions of it, so a dev
server left running rewrites it under you. git checkout -- apps/web/next-env.d.ts and re-run.
pnpm check fails with EPIPE or EPERM on a Prisma engine, on Windows
A running app holds packages/infrastructure/dist/generated/prisma open. Stop pnpm start before
running the gate, and do not run two pnpm commands against this workspace at once.
pnpm start moved to other ports than 3000 and 3001
Something else holds them — often a previous run killed hard enough that its servers outlived it. pnpm start puts each server in
its own process group and signals the group, so Ctrl+C leaves nothing behind; kill -9 on the
launcher does not.
prisma migrate deploy fails with P3005 — "the database schema is not empty"
The database has tables Prisma did not create. Drop it and let pnpm start rebuild it; the seed
makes that cheap.
pnpm start says DATABASE_URL points at <host>, which is not this machine
Working as intended. The script migrates and seeds whatever it is given, so it will not write to a
host it cannot see is yours. If that host really is a staging database, prefix the command with
COLLEGA_ALLOW_REMOTE_DATABASE=1 — see
Against Vercel Postgres. If it is not, look at DATABASE_URL in .env
and at your shell, which overrides the file.
pnpm install fails on an engine version — the workspace requires Node ≥ 24.20 and
pnpm ≥ 12.3.4. corepack prepare [email protected] --activate.
SPEC/*.md is the source of truth. If behaviour changes, update the canonical spec first, then
tests, then implementation.feature/<NNN>-<short-description>, merged into dev.CLAUDE.md and AGENTS.md carry the full working rules, coding standards
and the multi-agent worktree workflow.
See LICENSE.
Abc123! |
| Hannah Lindqvist |
| User |
| Pinecone Labs |
[email protected] | Abc123! | Marcus Bell | Read Only | Pinecone Labs |
[email protected] | Abc123! | Camille Durand | Org Admin | Brightline Creative |
[email protected] | Abc123! | Dana Whitfield | User | Brightline Creative |
[email protected] | Abc123! | Theo Lindqvist | User | Brightline Creative |
[email protected] | Abc123! | Julian Ellery | Read Only | Brightline Creative |
[email protected] | Abc123! | Elena Vasquez | Org Admin | Meridian Holdings |
[email protected] | Abc123! | Tomas Reyes | User | Meridian Holdings |
[email protected] | Abc123! | Grace Nakamura | User | Meridian Holdings |
[email protected] | Abc123! | Henry Osei | Read Only | Meridian Holdings |