metatron-open-seo/docs/SELF_HOSTING_TEAM_MODE.md
metatroncubeswdev ce75d45141
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 4: activity log — who did what in the workspace
- activity_log table (sqlite + pg, structurally identical; schema-parity
  covers it). Plain-text columns, no FKs — an append-only trail that must
  outlive the projects/users it references, so target_label snapshots a
  human-readable name at write time.
- ActivityRepository: record() (fire-and-forget, never breaks the caller) +
  list() (org-scoped, actor/action filters, keyset pagination) + listActors().
- Recording wired into the mutations worth tracking: project
  create/archive/restore/domain, audit start, team user create/remove/
  password-reset, invitation sent.
- getActivityLog / getActivityActors server functions (owner/admin gated) +
  Settings → Activity tab (ActivityLogView: filter by user & action, load
  more).
- Migration: drizzle/0045_*, drizzle-pg/0023_*. The pipeline does not run
  migrations — see docs/SELF_HOSTING_TEAM_MODE.md step 5 for the one-time
  `drizzle-kit migrate` on the server. Writes fail silently until the table
  exists.

tsc / oxlint / knip clean. New ActivityRepository.test.ts (4) + schema-parity
picks up the new table; suite otherwise unchanged (pre-existing samSkills
CRLF failure only).

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

99 lines
3.6 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.
## 5. Activity log
**Settings → Activity** (owner/admin only) shows who did what — projects
created/archived, site audits started, users added/removed, invitations sent —
filterable by user and action.
It writes to a new `activity_log` table, so **run the migration once** after
deploying:
```sh
cd /home/dev/DOCKER/OPEN-SEO/open-seo
export $(grep -E '^DATABASE_URL=' .env | xargs)
pnpm exec drizzle-kit migrate --config drizzle-pg.config.ts
pm2 restart OPEN-SEO
```
Until the table exists, the writes fail silently (logged to the console) and the
app keeps working; the Activity tab just shows nothing.
## 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.