metatron-open-seo/docs/SELF_HOSTING_TEAM_MODE.md
metatroncubeswdev 17dfca406f
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
Audit crawler: send a browser navigation header set (tier-1 anti-bot)
The crawler identified as `OpenSEO-Audit/1.0` with almost no headers, which
naive bot filters and security plugins block outright.

- New crawl-request.ts: AUDIT_USER_AGENT (a current Chrome string),
  buildAuditHeaders() (Accept, Accept-Language, Sec-Fetch-*, Sec-Ch-Ua,
  Upgrade-Insecure-Requests), and fetchForAudit() — fetch + those headers +
  one retry on a transient 429/503.
- Wired into the page crawl (site-audit-workflow-helpers), robots.txt +
  sitemap discovery, and start-URL redirect probing.

Gets past the naive tier; still reported as "blocked" for JS/TLS challenges
(Cloudflare Managed Challenge, DataDome) — those need a real browser. Doc note
points operators at WAF IP/UA allowlisting for their own sites.

No env dependency (keeps the audit lib importable without a cloudflare:workers
mock). tsc / oxlint / knip clean; new crawl-request.test.ts (5); suite
otherwise unchanged.

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

103 lines
3.9 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.
- Site audit sends a Chrome-like User-Agent and browser headers, which gets
past naive bot filters but not JS/TLS challenges (Cloudflare Managed
Challenge, DataDome). For those, allowlist the server's IP or the crawler's
User-Agent in the target site's WAF.
- 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.