Part of the AgentSwarms docs.
A complete local setup on macOS, Linux, and Windows — installing dependencies, standing up your own Supabase project (database, auth, storage), configuring environment variables, and running the app.
| Requirement | Version | Why |
|---|---|---|
| Node.js | 20.19+ or 22.12+ |
Required by Vite 7. Older Node 18 will fail to start the dev server. |
npm (bundled with Node) or Bun 1.1+ |
— | Either works — both package-lock.json and bun.lock are committed. Use one consistently. |
| Git | any recent | to clone the repo |
| A Supabase account | free tier is enough | supabase.com — this is your database, auth, and file storage |
| Supabase CLI | 2.x |
(recommended, not strictly required) — the fastest way to apply the project's SQL migrations (over two hundred) in one command |
Optional, but needed for a fully working app:
| Optional | Why |
|---|---|
| OpenRouter API key (openrouter.ai/keys) | Without it, nobody can chat with an agent or embed a document until they connect their own provider under /integrations. With it, chat and Knowledge Base embeddings work zero-config for every user. |
# Node (via nvm — recommended over the system/Homebrew Node so you can pin versions)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.zshrc # or ~/.bashrc / ~/.bash_profile
nvm install 22
nvm use 22
# Git (skip if `git --version` already works — ships with Xcode Command Line Tools)
xcode-select --install
# Supabase CLI
brew install supabase/tap/supabase
# Bun (optional, only if you prefer it over npm)
curl -fsSL https://bun.sh/install | bash# Node (via nvm)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install 22
nvm use 22
# Git
sudo apt update && sudo apt install -y git
# Supabase CLI — a global `npm install -g supabase` is explicitly unsupported
# by Supabase, so use one of these instead:
# Option A — Homebrew (works on Linux too, not just macOS):
brew install supabase/tap/supabase
# Option B — no install needed, invoke on demand via npx:
# npx supabase login
# npx supabase link --project-ref <your-project-id>
# npx supabase db push
# Option C — grab the current Linux binary from the releases page and put
# it on your PATH: https://github.com/supabase/cli/releases (look for the
# linux_amd64 or linux_arm64 asset matching your CPU architecture).
# Bun (optional)
curl -fsSL https://bun.sh/install | bashUse WSL2 (Windows Subsystem for Linux) — recommended. Everything works
on native Windows: the build scripts go through cross-env, so
npm run build runs the same in cmd.exe, PowerShell and Git Bash. WSL2 is
still the smoother path for Node tooling generally — file watching, native
modules and shell scripts all behave the way the rest of the project assumes.
# In an elevated PowerShell:
wsl --install -d UbuntuReboot if prompted, open the Ubuntu app from the Start menu, create your
Linux user, then follow the Linux instructions above verbatim inside
that WSL shell. Clone the repo and run everything (npm install, npm run dev, etc.) from within WSL, not from Windows PowerShell.
If you'd rather stay on native Windows (no WSL): install Node from
nodejs.org (LTS ≥20.19) and Git from
git-scm.com, and install the Supabase CLI via
scoop install supabase (scoop.sh) or by downloading
the Windows binary from the
Supabase CLI releases page. Any
terminal will do — cmd.exe, PowerShell or Git Bash — because the npm
scripts set their environment through cross-env rather than POSIX shell
syntax.
git clone https://github.com/AgentSwarms-fyi/agentswarms.git
cd agentswarms
npm install # or: bun installIf you plan to contribute, fork it on GitHub first and clone your fork instead — the rest of this guide is identical either way.
There are two ways to get a Supabase backend. Option A (a free hosted project on supabase.com) is the fastest and what the rest of this section walks through. Option B runs Supabase on your own machine with Docker — no account, nothing leaves your infrastructure, and a script does every step for you.
Deploying to Kubernetes instead? There is a third path that skips this whole section:
bash scripts/setup-k8s.shinstalls Supabase into your cluster from the community Helm chart, applies the schema, creates your admin user and starts the app and every service alongside it. See DEPLOYMENT.md § D1.
One command deploys the entire solution: it downloads and starts the
official Supabase Docker stack (Postgres, Auth, Storage, Realtime, the API
gateway and Studio), generates every secret and key, applies the schema,
creates your admin user, writes everything into the app's .env
automatically, and then installs and starts the app itself:
git clone https://github.com/AgentSwarms-fyi/agentswarms.git
cd agentswarms
bash scripts/setup-selfhosted.sh # → app on :8080, Supabase on :8000Prompts for your admin email and password (or pass them non-interactively):
ADMIN_EMAIL=you@corp.com ADMIN_PASSWORD='a-strong-one' bash scripts/setup-selfhosted.shWhat the script does, in order — each step is the automated version of the manual walkthrough in DEPLOYMENT.md § Self-hosted Supabase:
| Step | What happens |
|---|---|
| Download & run | Clones the official supabase/docker stack into supabase-docker/ (git-ignored) and starts it. First run pulls ~2 GB of images. |
| Generate secrets | Postgres password, JWT_SECRET, and the ANON / SERVICE_ROLE API keys signed from that secret (HS256, locally — nothing leaves your machine), plus the Studio dashboard login and vault keys. |
| Wait properly | Waits for the auth service to answer and for the storage service to finish its own boot migrations — pushing the schema too early fails three migrations (see DEPLOYMENT.md § "Apply the schema"). |
| Extensions | Ensures the five required Postgres extensions exist (vector, pg_net, pg_cron, pgmq, supabase_vault). |
| Schema | Applies all migrations with npx supabase db push --db-url ... pointed at your own database. |
| Admin user | Creates your account via the auth admin API, email pre-confirmed, and sets it as ADMIN_EMAIL — the instance's bootstrap superadmin. |
| Wire the app | Writes SUPABASE_URL, both keys, the VITE_ copies, ADMIN_EMAIL/VITE_ADMIN_EMAIL and SITE_URL into .env — nothing to copy by hand. |
| Install & start | Hands over to scripts/setup.sh (same flags: --all, --dev, --docgen, ...) which installs dependencies, generates the remaining app secrets, and brings the stack up. |
Notes worth knowing before you run it:
- Where things end up. App:
http://localhost:8080. Supabase API (Kong):http://localhost:8000— that URL and the generated keys are what landed in your.env. Supabase Studio:http://localhost:8000(usersupabase, password insupabase-docker/docker/.env). - No "organisation/project" step. Self-hosted Supabase has no dashboard org/project concept — the whole stack is one project. Where the hosted flow says "create a project and copy its keys", the script generates those keys and wires them in.
- Re-running is safe. An existing
supabase-docker/docker/.envis reused, never regenerated — regeneratingJWT_SECRETwould invalidate every issued key. To start truly fresh:cd supabase-docker/docker && docker compose down -v, then delete the directory. - Windows: run the script in WSL or Git Bash with Docker Desktop running.
- Sizing: budget roughly +2 vCPU / +4 GB RAM / +20 GB disk for the Supabase stack on top of the app's own requirements.
- Before production: TLS in front of both origins, keep Studio and
Postgres off the public network, and back up Postgres and
PROVIDER_CREDS_SECRET(a restored database cannot decrypt stored credentials without it). The full checklist is in DEPLOYMENT.md § "Before you call it production".
If you used Option B, the script has already done everything in §3.1–§3.2 and §4's Supabase values — skip ahead to §4 only if you want to add optional keys (model providers, email, search), then continue at §5.
-
Go to supabase.com/dashboard → New project.
-
Pick an organization, name, database password (save it — you'll need it when linking the CLI), and region. Wait ~2 minutes for provisioning.
-
Once it's ready, go to Project Settings → API Keys (older projects: Settings → API) and note down four values — you'll need them in step 4:
- Project URL (e.g.
https://xxxxx.supabase.co) - Project ID (the
xxxxxpart of the URL — also shown as "Reference ID" under Project Settings → General) - Publishable key — starts with
sb_publishable_...(on older projects this is theanonkey, a longeyJ...JWT). Safe to expose to browsers. - Secret key — either the legacy
service_roleJWT (a longeyJ...string under the "Legacy API keys" tab — the most reliable choice) or a new-stylesb_secret_...key. Click "Reveal" and copy it in full. Keep it secret — it bypasses row-level security.
⚠️ Don't mix the last two up when filling.envin step 4. The publishable key goes inSUPABASE_PUBLISHABLE_KEYand theVITE_copies; the secret key goes only inSUPABASE_SERVICE_ROLE_KEY. Swapping them is the #1 cause of an "Invalid API key" error at signup. - Project URL (e.g.
The repo ships over two hundred SQL migrations under supabase/migrations/ that create
every table, RLS policy, Postgres function/trigger, index, and the
avatars storage bucket. They also enable the Postgres extensions the app
needs: vector (pgvector, for Knowledge Base embeddings), pg_cron,
pg_net, pgmq, and supabase_vault — all available on Supabase's hosted
free tier, no manual extension setup required.
Recommended: Supabase CLI
npx supabase login
npx supabase link --project-ref <your-project-id> # the Project ID from 3.1
npx supabase db push # applies all migrations, in ordersupabase link records which remote project you're targeting (under the
git-ignored supabase/.temp/ directory) — don't skip it, or db push
won't know where to push. The project_id in supabase/config.toml is
just a local name for the CLI; it ships pre-filled ("agentswarms") and
you don't need to change it.
Alternative: manual, via the SQL Editor (works, but there are over two
hundred files) — in the Supabase Dashboard, open SQL Editor, and run each file
under supabase/migrations/ in filename order (the leading timestamp
is the sort key — oldest first). Paste each file's contents and run it
before moving to the next.
In the Dashboard, go to Authentication → URL Configuration:
- Site URL:
http://localhost:8080(the defaultvite devport — check your terminal output in step 5, it'll tell you if a different port got picked because 8080 was busy) - Redirect URLs: add
http://localhost:8080/**
This makes email confirmation links and password-reset links redirect back to your local dev server instead of failing or 404ing.
Email delivery: leave Supabase's default built-in email sending enabled (Authentication → Emails) — it works out of the box for confirmation and password-reset emails with no extra config, just rate-limited for low volume, which is fine for development. If you want production-grade delivery later, configure custom SMTP under Authentication → Emails → SMTP Settings.
Social login. The "Continue with Google/Apple" buttons on
/loginuse Supabase Auth's nativesignInWithOAuth. They work as soon as you enable the matching provider (with its client ID/secret) in your Supabase project under Authentication → Providers; until then they return a "provider is not enabled" error. Email/password signup and login work fully out of the box.
Copy the example file and fill it in:
cp .env.example .envOpen .env and fill in the values you collected in step 3.1 — every field
is documented inline in the file. In short:
-
SUPABASE_URL,SUPABASE_PUBLISHABLE_KEY,SUPABASE_SERVICE_ROLE_KEY— from step 3.1. These are read by server-side code (API routes, server functions). -
VITE_SUPABASE_URL,VITE_SUPABASE_PUBLISHABLE_KEY— the same two public values again, justVITE_-prefixed. Vite only inlinesVITE_-prefixed vars into the browser bundle, so the client-side Supabase client needs its own copy. (Never put the service role key behind aVITE_prefix — that would ship a database-bypassing secret to every visitor's browser.)The Project ID from step 3.1 is not an environment variable. It is passed straight to the CLI as
supabase link --project-ref <id>in step 3.2, which records it undersupabase/.temp/. Nothing at runtime reads it. -
OPENROUTER_API_KEY— optional but recommended; makes the app usable with zero per-user setup. Get one at openrouter.ai/keys. -
OPENROUTER_DEFAULT_MODEL,OPENROUTER_BASE_URL— optional overrides, sensible defaults are pre-filled. -
No separate embeddings key. Knowledge Base vector search runs on whichever model provider is connected — see Which provider embeds.
-
FIRECRAWL_API_KEY— optional; powers the agentweb_search/web_browsetools workspace-wide. See Web search & browsing below for exactly what works with and without it. -
ADMIN_EMAIL+VITE_ADMIN_EMAIL— the one account email allowed to access the instance admin dashboard. Set both to the address you'll sign up with.
.env is git-ignored — never commit it.
Outbound transactional email (optional). Welcome emails, budget alerts,
BI alerts, scheduled reports, approval requests and the contact form send
through whichever transport you configure. With neither configured, sends
are skipped and logged (see the email_send_log table) — the app works fine
without email, so it's safe to skip this entirely for local dev. Auth emails
(confirmation, password reset) are unaffected — Supabase sends those itself
(step 3.3).
No third-party account is required. SMTP is built in — nodemailer is a
dependency of the app, not something to install — so any relay you already
have works with nothing else signed up for. Resend exists for deployments
that have no relay to point at.
Four variables, no account anywhere:
SMTP_HOST="smtp.your-company.com"
SMTP_PORT="587"
SMTP_USER="apikey-or-username"
SMTP_PASS="..."
EMAIL_FROM="AgentSwarms <noreply@your-company.com>"
SITE_URL="https://your-domain.com"Port 587 with STARTTLS is the usual choice and the default. Port 465
turns on implicit TLS automatically; set SMTP_SECURE="true" to force it on
any other port. Leave SMTP_USER empty for a relay that authenticates by IP
rather than by credential, which is common for an internal Postfix or an SES
endpoint restricted to your VPC.
Anything that speaks SMTP works: a corporate Exchange or Google Workspace relay, Amazon SES, a provider's free tier, or a mail server you run. Whatever you pick, the sending domain still needs SPF and DKIM records or large providers will treat the mail as spam — that requirement belongs to email, not to any particular vendor.
RESEND_API_KEY on its own is not enough. Resend will only send from an
address on a domain you have verified, so both halves matter:
- Create the API key — Resend dashboard → API Keys → Create. Copy it
into
RESEND_API_KEY. It startsre_. - Add your domain — Domains → Add Domain, e.g.
your-company.com(a subdomain likemail.your-company.comis fine and keeps your main domain's reputation separate). - Add the DNS records Resend shows you — an MX record and TXT records for SPF and DKIM — at your DNS host, then press Verify. Propagation is usually minutes.
- Set
EMAIL_FROMto an address on that domain, in the formAgentSwarms <noreply@your-company.com>. The display name is optional but worth setting; the address must be on the verified domain.
RESEND_API_KEY="re_..."
EMAIL_FROM="AgentSwarms <noreply@your-company.com>"
SITE_URL="https://your-domain.com"Important
Two failure modes that look like nothing is wrong.
Leaving EMAIL_FROM empty falls back to AgentSwarms <noreply@example.com>, which Resend rejects — every app email fails while the
app carries on normally. The rejection is recorded in email_send_log, which
is the first place to look when nobody is receiving mail.
Before a domain is verified, Resend allows only onboarding@resend.dev as
the sender, and delivers only to the email address that owns the Resend
account. That is enough to smoke-test the templates and useless for real
users — mail to anyone else is accepted by the API and never arrives.
SITE_URL matters more than it looks: every link in every email is built from
it. Leave it at http://localhost:8080 in production and your users get emails
pointing at their own machine.
Agents can be given two web tools in the agent editor (Build → Agent Builder
→ edit → Tools): web_search (search the web) and web_browse (fetch one page as
clean markdown). How well they work depends on whether a key is configured —
no key is required to start, but the free fallback is limited:
| Setup | web_search |
web_browse |
|---|---|---|
| No key (default) | Works, but falls back to DuckDuckGo's free Instant Answer API — only entity summaries + a few related topics, so thin/often empty for ordinary queries | Works via the built-in fetcher — server-rendered pages only, no JavaScript |
| Firecrawl connector (Integrations page → Web Search) | Real web search via Firecrawl for every agent — key stored encrypted, no restart | Full-page reads via Firecrawl |
FIRECRAWL_API_KEY in .env (workspace-wide) |
Same as above, set via env instead of the UI | Full-page reads via Firecrawl |
Per-agent key (agent editor, no .env change) |
Bring your own Brave / SerpAPI / Tavily key | Bring your own ScrapingBee or custom Firecrawl key |
web_browse and adding a URL to a knowledge base both work without any key.
The built-in fetcher requests the page through the same SSRF guard described
below, strips navigation, headers, footers and cookie banners, and converts what
is left to markdown — including tables, which stay tables rather than being
flattened into prose.
It does not run JavaScript. That is the whole difference between it and
Firecrawl. A server-rendered page — documentation, a blog post, a licence, a
GitHub README, an RFC — converts cleanly. A client-rendered single-page app
returns its empty shell, because the text was never in the HTML the server sent.
When that happens the fetcher does not pretend: the result is marked thin and
carries a note saying the page is probably JavaScript-rendered and that Firecrawl
can read it. Knowledge-base ingestion refuses such a page outright rather than
saving a document that answers nothing.
Other limits worth knowing: 8 MiB per page, a 20-second timeout, HTML and plain text only (a PDF or Office URL is refused rather than mangled), and no crawling — one URL per call, no link-following.
So if web search "isn't really searching," that's the DuckDuckGo fallback — and that is a category difference, not a quality one. The Instant Answer API returns encyclopedia-style entries for recognised entities and has no ranked web results at all, which is why ordinary queries come back empty however they are phrased.
Easiest fix: open Integrations → Web Search, paste a Firecrawl key (from
firecrawl.dev) and click Validate & Save — it takes
effect immediately, no restart. You can also set FIRECRAWL_API_KEY in .env,
or give one agent its own key in the agent editor. Resolution order for the
built-in Firecrawl path is: per-agent key → FIRECRAWL_API_KEY → the
Integrations connector.
Every URL fetched by web_browse is chosen by the model — including by the
built-in fetcher — so it's routed through
the same SSRF guard as swarm HTTP nodes and connector tests: cloud instance
metadata / link-local addresses are always refused, and you can block all
private/internal targets with BLOCK_PRIVATE_NETWORK_FETCH=true (recommended
for public embeds / untrusted multi-tenant installs).
npm run dev
# or: bun run devVite will print the local URL (default http://localhost:8080, or the
next free port if that one's taken — match this to the Auth Site
URL/Redirect URLs you set in step 3.3 if it differs). Open it in a
browser.
- Sign up with an email/password at
/login. You should either land straight in the app (if email confirmation is off) or see a "check your email" prompt — the confirmation email comes from Supabase's default mailer per step 3.3. - Go to Agents and create one (or pick one of the seeded sample
agents), then open it in Build → Agent Chat and send a message. If
OPENROUTER_API_KEYis set, this should work immediately with no further configuration. - Open Knowledge Base, create one, and upload a document. If any
embedding-capable provider is connected (or
OPENROUTER_API_KEYis set), it gets embedded for vector search; otherwise it still works via keyword search. - Open Swarms and load one of the built-in templates to confirm the visual canvas and multi-agent execution work end-to-end.
Product documentation for every feature ships inside the app at /docs.
Every install starts all nine services — there are no profiles and nothing to opt into. The table says what each one does, so you know what a stopped container costs you.
| Service | Container | What you lose without it |
|---|---|---|
| Document renderer | docgen |
Deep-mode exports fall back to the in-browser builder (no native charts/tables) |
| JS sandbox | js-sandbox |
Function and custom-component nodes work on the canvas but fail in deployed / scheduled swarm runs |
| Developer-workspace runtime | notebook-* |
Notebooks cannot run at all — the editor shows a panel asking an admin to enable the runtime. ETL runs, ML training and MCP servers need it too |
| Lakehouse catalog | lakehouse-catalog |
A Postgres for the lakehouse's catalog; without one (this, or your own in LAKEHOUSE_CATALOG_URL) the lakehouse, SQL models and ML stay off |
| Object store (MinIO) | minio |
Where lakehouse Parquet files live; without one (this, or your own in LAKEHOUSE_S3_*) a table has nowhere to write |
| Spark cluster | spark-connect |
A Spark Connect endpoint for the ETL Spark engine and lakehouse queries on Spark; idle until SPARK_CONNECT_URL names it, ~1 GB image, jars on first use |
| Vector store (Qdrant) | qdrant |
Somewhere other than Postgres to search knowledge-base embeddings; idle until VECTOR_STORE=qdrant names it, and retrieval stays on pgvector until it does |
| Online feature store | valkey |
Millisecond feature lookups for serving; idle until FEATURE_STORE_URL names it, and every lookup reads the lakehouse until it does — correct, ~60x slower |
Start it:
bash scripts/setup.shpowershell -ExecutionPolicy Bypass -File scripts\setup.ps1Or with Compose directly:
docker compose up -d --buildWhy they are opt-in rather than always on: the renderer image carries LibreOffice (large), and the notebook runtime mounts the Docker socket into a least-privilege proxy so it can start kernel containers. Neither is wrong to run — both ship hardened — but they should be a decision, not a surprise.
Confirm what is actually running: sign in as the admin and open Observability → Monitoring. It lists every service with its status and response time. Optional services you chose not to start appear as "Not running" in grey rather than as failures.
The Developer workspace (/notebooks) runs notebooks on secure server
kernels — real CPython that can pip install and run the real frameworks
(LangChain, LlamaIndex, LangGraph). It's off by default; a notebook shows a
short "runtime required" panel until an admin turns it on. To enable it:
- Start the runtime services (one command, no env editing):
docker compose up -d --build
- Sign in as the admin and open Admin → Developer runtime (in the sidebar). Flip Enable server runtime on — that's it. The app generates its own signing secret and defaults every internal URL to the compose service names. Optionally tune limits/egress or restrict access to specific users/groups, and hit Run preflight to confirm everything is reachable.
- Open a notebook and run
import langchain. Cells execute on a sandboxed server kernel; there is no in-browser fallback, so until step 2 is done the editor shows a panel asking an admin to turn the runtime on.
The first --build is slow (it installs the frameworks into the kernel image).
Everything is optional and off by default: instances that never run that command
are completely unaffected.
Verify the install. Rather than clicking around to find out whether it works, run the end-to-end check — it validates the whole chain (socket-proxy, kernel image, network, hardened kernel boot, Jupyter serving, the gateway executing a real cell, and a kernel calling the platform back) and prints a pass/fail report:
bash deploy/notebooks/test/verify-runtime.shSecurity model, hardening, scaling, and the full test procedure are in DEVELOPER_WORKSPACE_RUNTIME.md. The runtime is off by default; instances that don't enable it are unaffected.
If any of these fail, check your terminal's vite dev output and the
browser console first — most first-run issues trace back to a missing/typo'd
env var or a migration that didn't apply (re-run supabase db push if you
suspect the latter; it's safe to re-run, already-applied migrations are
skipped).
npm run build runs out of memory. The script already raises Node's heap
to 6 GB through cross-env, so this means the machine itself is short. Close
what you can, or raise it further:
npx cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --sourcemap falseDocker builds are unaffected — the image builds on Linux.
"Invalid API key" when signing up or logging in. The publishable and
secret keys are swapped (or one was truncated when copying) in .env.
SUPABASE_PUBLISHABLE_KEY and both VITE_SUPABASE_*_KEY vars must hold
the sb_publishable_... (or legacy anon) key — the secret key belongs
only in SUPABASE_SERVICE_ROLE_KEY. You can verify a key without the
app:
curl -H "apikey: YOUR_PUBLISHABLE_KEY" https://YOUR_PROJECT_ID.supabase.co/auth/v1/health
# HTTP 200 → key is valid for this projectRemember to restart npm run dev after editing .env.
Server-side features fail with 401 / "Invalid API key" even though the
publishable key works. Your SUPABASE_SERVICE_ROLE_KEY is bad — commonly
a truncated copy (they're easy to cut off), a key copied from a
different Supabase project's dashboard, or a new-style sb_secret_...
key that has been rolled. The reliable fix: in Project Settings → API
Keys → Legacy API keys, copy the service_role JWT (a ~200+
character eyJ... string) and use that as SUPABASE_SERVICE_ROLE_KEY —
and double-check the dashboard URL contains your project ref before
copying.
Missing required field in config: project_id from supabase db push.
supabase/config.toml must contain a non-empty project_id. It ships
pre-filled with "agentswarms" (the value is just a local label — your
real project is selected by supabase link); restore it if it got blanked.
failed to parse environment file: .env (unexpected character ...).
Your editor saved .env with a UTF-8 BOM (byte-order mark), which the
Supabase CLI can't parse. Re-save it as plain UTF-8 without BOM — in
VS Code: click the encoding in the status bar → "Save with Encoding" →
"UTF-8". (On Windows, Set-Content -Encoding utf8 in Windows PowerShell
5.x writes a BOM — use an editor or PowerShell 7+ instead.)
"PROVIDER_CREDS_SECRET is not configured" when saving a warehouse
connection, secret, or Data Catalog source. Stored credentials are
AES-256-GCM encrypted with a key derived from the PROVIDER_CREDS_SECRET
env var, and it has no default. Add any long random string to .env
(e.g. openssl rand -hex 32) and restart npm run dev. Rotating the
value later invalidates previously saved credentials — re-enter them.
"Unsupported provider: lovable_ai" when messaging an agent. Your
database schema predates the 20260719000000_fix_new_user_seed_provider
migration (the signup trigger used to seed sample agents on a provider
that only exists on the hosted platform). Run npx supabase db push —
it updates the trigger and repairs already-created agents.
Changes to .env not taking effect. Vite bakes VITE_* values into
the bundle when the dev server starts. Stop it (Ctrl+C), run
npm run dev again, and hard-refresh the browser (Ctrl+Shift+R).