metatron-open-seo/docs/SELF_HOSTING_TEAM_MODE.md
metatroncubeswdev acc35c055c
Some checks failed
CI / ci (push) Has been cancelled
CI / docker-build (push) Has been cancelled
Publish Docker image / docker (push) Has been cancelled
Upload sourcemaps / upload (push) Has been cancelled
Phase 3: team-mode owner setup, user management, shared workspace
Makes AUTH_MODE=team usable end to end.

- resolveTeamContext (middleware/ensure-user/team.ts): a session resolves to a
  membership in the single shared workspace. No per-user fallback org — a
  signed-in user with no membership is treated as signed out, so the owner can
  actually remove people.
- teamProvisioning.ts: one path that writes user + credential account + member
  together (hashPassword from better-auth/crypto). Shared by both entry points.
- /api/team-setup (raw route, outside auth middleware): GET reports whether an
  owner is needed; POST creates the first owner + the shared org, then
  self-disables once any user exists.
- /setup route + sign-in redirect: first run sends you to create the owner.
- teamUsers server functions (owner/admin-gated): list / create (with temp
  password) / reset password / remove. Removal drops membership + sessions,
  keeps the user row for historical attribution.
- Settings gains a "Users" tab in team mode (TeamUsers.tsx).
- docs/SELF_HOSTING_TEAM_MODE.md: activation runbook (.env, build, first owner).

No DB migration — all rows are existing better-auth tables. tsc / oxlint / knip
clean. New teamProvisioning.test.ts (4 cases) passes; suite otherwise unchanged
(pre-existing samSkills.test.ts CRLF failure only).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 08:13:01 -04:00

80 lines
3.0 KiB
Markdown

# Self-hosting with team logins (`AUTH_MODE=team`)
`team` mode turns the app into a single shared workspace with individual
email/password logins. There is no billing, no Google login, no email
verification, and no self-serve signup — the owner provisions every account.
Use it when you want your team on one internal instance and need to see who did
what. For a public multi-tenant product, use `hosted` instead.
## What you get
- Email/password sign-in for each teammate.
- One shared workspace (organization). Everyone works in the same projects.
- Roles: **owner** (you), **admin** (full access except billing/owner actions),
**member** (research + view).
- Two ways to add people:
- **Settings → Users** — create an account with a temporary password directly.
- **Settings → Organization** — send an email invite link (needs no email
provider config to create the invite; the link is shown in the UI).
## 1. Set environment variables
Edit `.env` in the deployment directory (for the pm2 setup that is
`/home/dev/DOCKER/OPEN-SEO/open-seo/.env`):
```sh
AUTH_MODE=team
BETTER_AUTH_URL=https://seo.thedomainnest.com # the exact public origin, https
BETTER_AUTH_SECRET=<64 hex chars> # openssl rand -hex 32
```
`AUTH_MODE` is compiled into the browser bundle, so it must be present when
`vite build` runs — not only at runtime. Vite reads this same `.env` at build
time, so one edit covers both as long as the build runs in this directory.
Keep the existing `DATABASE_URL`, `DATAFORSEO_API_KEY`, etc.
## 2. Rebuild and restart
If your pipeline builds on push, commit/push and let it run. To do it by hand:
```sh
cd /home/dev/DOCKER/OPEN-SEO/open-seo
pnpm install --frozen-lockfile
pnpm build
pm2 restart OPEN-SEO --update-env
```
Confirm config: `curl -s http://127.0.0.1:3001/api/health | jq .checks.auth`
should report `team`.
## 3. Create the owner account
Open the site. With `team` mode on and no users yet, `/sign-in` redirects to
`/setup`. Fill in name, email, password → **Create workspace**. You are signed
in as the owner.
`/setup` disables itself the moment the owner exists. If you ever need to reset,
delete all rows from `user` (and `member`, `account`, `session`) in Postgres and
reload.
## 4. Add your team
**Settings → Users → Add user.** Enter their email, name, a temporary password,
and a role. Share the password out-of-band; they change it after signing in
(Settings → Personal, once that lands) or you reset it from the same screen.
Removing a user drops their membership and signs them out everywhere. Their
`user` row is kept so past activity still attributes correctly; re-adding them
issues a fresh password.
## Notes
- Password reset by email is not available in `team` mode. The owner/admins
reset passwords from Settings → Users.
- The MCP server and its OAuth flow are hosted-only for now; `team` deployments
serve the app UI only.
- Rolling back: set `AUTH_MODE=local_noauth`, rebuild, restart. Existing users
and data stay in the database, just unused.