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>
3.9 KiB
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):
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:
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:
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
teammode. 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;
teamdeployments serve the app UI only. - Rolling back: set
AUTH_MODE=local_noauth, rebuild, restart. Existing users and data stay in the database, just unused.