Team memory for AI coding agents. teamem portal is an open, self-hostable service that ingests your team's engineering signals (GitHub commits, PRs, issues), continuously compiles them with LLMs into a structured, interlinked knowledge base (open markdown format, fully exportable), and serves it to every team member's code agent over MCP with progressive disclosure.
Status: M1 server track complete; standalone CLI work continues. The server now implements ingestion, F1 extraction, F2 semantic merge, hybrid vector/FTS retrieval, MCP tools, redaction, persistence, and queue processing, with real PostgreSQL/pgvector integration coverage. Checks that require a live LLM/embedding provider or GitHub App remain conditional until those credentials are supplied. See the scope-corrected M1 acceptance report. The standalone MIT CLI lives in
teamem-ai/cli; its repository skeleton is complete, whileteamem initis still pending. The Web UI remains a later milestone.
| Path | Package | License | Purpose |
|---|---|---|---|
/ (root) |
— | AGPL-3.0-only | Repository default license |
apps/server |
@teamem/server |
AGPL-3.0-only | Ingestion API, compile engine, MCP endpoint, GitHub connector |
apps/web |
@teamem/web |
AGPL-3.0-only | Portal UI (served by the server) |
packages/schema |
@teamem/schema |
MIT | Shared contract types & Zod validators — the open-format carrier, free for any client/tool to import |
The license split is deliberate: the portal (server + web) is AGPL so the
product stays open; the schema package is MIT so the knowledge format stays
freely adoptable by clients, connectors, and third-party tools without
copyleft obligations. The CLI lives in a separate MIT repository
(teamem-ai/cli, npm package teamem).
Requires Node >= 20 and pnpm.
pnpm install
pnpm lint # eslint
pnpm typecheck # tsc --noEmit across packages
pnpm test # vitestContributions use short-lived branches, signed-off commits, protected pull
requests, and squash merges into main. See CONTRIBUTING.md
for the contributor checklist and docs/GITFLOW.md for the
complete branch, review, GitHub governance, and release policy. Report security
issues through the private process in SECURITY.md, not a public
issue.
Releases are versioned Git tags plus GitHub Releases and GHCR images. This repository publishes distributable artifacts; it does not deploy a hosted environment.
Three containers, no Redis — the compile queue is pg-boss, which lives inside Postgres. One fewer service to operate than comparable stacks.
The portal web UI is served directly by the server container on the same port — no separate web deployment unit. Bring the stack up, open the browser, sign in with GitHub, and you're running.
-
Docker and Docker Compose v2 installed on the host.
-
A single GitHub App — created once before
docker compose up. The same App is used for two purposes:- OAuth sign-in — team members sign in with their GitHub accounts.
- Webhook ingestion — GitHub pushes, PRs, and issues are ingested as evidence for the knowledge compiler.
You do not need a second OAuth App, and you do not need to create local user accounts. The GitHub App is the only identity provider.
- Go to GitHub → Settings → Developer settings → GitHub Apps → New GitHub App.
- Fill in the required fields:
- GitHub App name — anything (e.g.
acme-teamem). - Homepage URL —
http://localhost:8080(or your deployment URL). - Callback URL —
http://localhost:8080/auth/github/callback. If you deploy on a different host/port, adjust the origin accordingly and also setTEAMEM_BASE_URLin your.env. - Webhook URL — leave blank for now (or set to a public URL if you have one). Webhook ingestion works later, after initial setup.
- Webhook secret — generate a strong random string (e.g.
openssl rand -hex 32). Save this — you will put it in.env.
- GitHub App name — anything (e.g.
- Under Permissions, set:
- Repository permissions → Contents — Read-only (for commit access).
- Repository permissions → Pull requests — Read-only.
- Repository permissions → Issues — Read-only.
- Organization permissions → Members — Read-only (for team member profile resolution).
- Under User permissions:
- Email addresses — Read-only.
- Under Where can this GitHub App be installed? choose Any account.
- Click Create GitHub App.
- After creation, note the App ID (top of the page).
- Scroll to Private keys → Generate a private key. Download the
.pemfile — you will paste its contents into.env. - Under Client secrets → Generate a new client secret. Copy the value immediately (GitHub shows it only once).
- Go to Install App in the left sidebar and install it on your personal
account or organization. Note the Installation ID from the URL:
https://github.com/settings/installations/NNNNNNNN.
You now have all the values needed for .env:
| .env variable | Where to find it |
|---|---|
TEAMEM_GITHUB_APP_ID |
App ID (numeric, top of GitHub App settings) |
TEAMEM_GITHUB_INSTALLATION_ID |
Installation ID from the URL after install |
TEAMEM_GITHUB_PRIVATE_KEY |
Contents of the downloaded .pem file |
TEAMEM_GITHUB_OAUTH_CLIENT_ID |
Client ID (shown on the App settings page) |
TEAMEM_GITHUB_OAUTH_CLIENT_SECRET |
Client secret (generated in step 9) |
TEAMEM_GITHUB_WEBHOOK_SECRET |
Webhook secret (generated in step 2) |
cp .env.example .envOpen .env and fill in the following required values:
| Variable | Requirement |
|---|---|
POSTGRES_PASSWORD |
Required — no default. Compose refuses to start without it. Pick a strong password. |
All six TEAMEM_GITHUB_* variables |
Required for sign-in. Without them the login page shows a disabled button with a setup notice. |
LLM provider keys (at least one is required for the compile worker to turn events into concept pages):
| Variable | Provider |
|---|---|
TEAMEM_ANTHROPIC_API_KEY |
Anthropic Claude |
TEAMEM_OPENAI_API_KEY |
OpenAI |
TEAMEM_OPENROUTER_API_KEY |
OpenRouter (multi-provider gateway) |
TEAMEM-prefixed on purpose — the ambient ANTHROPIC_API_KEY or OPENAI_API_KEY
from your host shell is never inherited by accident.
See .env.example for all available variables and their defaults.
# Standard topology — postgres + server + worker (3 containers)
docker compose up -d --build
# All-in-one — server embeds the compile worker (2 containers)
TEAMEM_ALL_IN_ONE=true docker compose up -d --build postgres serverThe first build takes a few minutes (installing dependencies, building the server and web SPA). Subsequent starts are faster — the image layers are cached.
# All services should show "healthy"
docker compose ps
# The server health endpoint returns 200 OK
curl -fSs http://127.0.0.1:${TEAMEM_PORT:-8080}/healthz && echo " OK"
# Postgres is bound to loopback only (never exposed to the network)
docker compose port postgres 5432
# Expected: 127.0.0.1:5432 (not 0.0.0.0)Navigate to http://localhost:8080 and click Sign in with GitHub.
The first user to sign in automatically becomes the team owner. The GitHub OAuth callback bootstraps:
- A user record tied to your GitHub identity.
- A team (named after your GitHub login).
- An owner membership in that team.
- A web session cookie (httpOnly, SameSite=Lax).
After sign-in you land on the app landing page. From there you can:
- Create a project in Settings → Project.
- Mint an API key in Settings → API Keys.
- Invite team members in Members.
- Configure the webhook on your GitHub App to start ingesting events.
Go to your GitHub App settings → Webhook and set:
- Payload URL:
http://<your-host>:8080/v1/webhooks/github - Content type:
application/json - Secret: the same value as
TEAMEM_GITHUB_WEBHOOK_SECRETin your.env
Select Pull requests, Pushes, Issues, and Issue comments under "Let me select individual events", then save. GitHub will deliver events to your portal — they appear in Events and are compiled into concept pages.
Note: webhook delivery requires GitHub to reach your server. For local development, use a tunnel like ngrok to expose your local port and update both the GitHub App callback URL and
TEAMEM_BASE_URLaccordingly.
These behaviors are intentional — they are guardrails, not bugs:
| What you do | What happens | Why |
|---|---|---|
Omit POSTGRES_PASSWORD from .env |
docker compose up fails immediately with POSTGRES_PASSWORD is required |
No default password — you must set one. |
| Omit GitHub App OAuth credentials | Login page shows a warning banner: "Sign-in isn't configured yet" and the button is disabled. | The operator must create a GitHub App before sign-in works. |
| Sign in as a user who is not in any team | Landing page says "You're not in a team yet" with guidance to ask for an invite link. | Every user must be a team member — no orphan accounts. |
POSTGRES_PASSWORD set, but not exported for docker compose |
Compose ignores .env if not also exported; fails with the same error. |
POSTGRES_PASSWORD must be in .env (Compose auto-loads .env), or exported in the shell. |
docker compose down # stop containers, keep data volume
docker compose down -v # stop containers and delete the database volumeTypeScript · Postgres (+ pgvector) · pg-boss · Drizzle ORM · Zod · React + Vite + shadcn/ui · LLM via BYO key (Claude / OpenAI / OpenRouter / any OpenAI-compatible endpoint).