11---
22name : setup-openleads
33description : >-
4- Sets up a working OpenLeads instance from a fresh clone — installs the api/,
5- web/ and (optional) scraper / workspaces, creates the .env files with freshly
6- generated secrets (SESSION_SECRET, SETTINGS_KEY, SERVICE_TOKEN), seeds a login,
7- optionally wires up local Ollama AI, then starts and verifies the dev servers.
8- Use this whenever someone wants to set up, install, bootstrap, configure, get
9- started with, onboard onto, or "run OpenLeads for the first time" — including
10- vague asks like "how do I run this?", "get this working", "set it up", or
11- "I just cloned this, now what?" while in the OpenLeads repo. Also the entry
12- point for production deployment, which it routes to deploy/DEPLOY.md.
4+ Sets up a working OpenLeads instance from a fresh clone — installs the api/
5+ and web / workspaces, creates api/ .env with a freshly generated SETTINGS_KEY,
6+ seeds a login, optionally wires up local Ollama AI, then starts and verifies
7+ the dev servers. Use this whenever someone wants to set up, install,
8+ bootstrap, configure, get started with, onboard onto, or "run OpenLeads for
9+ the first time" — including vague asks like "how do I run this?", "get this
10+ working", "set it up", or "I just cloned this, now what?" while in the
11+ OpenLeads repo. Also the entry point for production deployment, which it
12+ routes to deploy/DEPLOY.md.
1313---
1414
1515# Set up OpenLeads
1616
1717Drive a first-time OpenLeads setup end to end: dependencies → secrets → login →
18- running, verified servers. OpenLeads is a three -workspace monorepo — ` api/ `
19- (Hono + ` node:sqlite ` ), ` web/ ` (React + Vite), ` scraper/ ` (Anthropic SDK) — so
20- "setup" means getting the pieces installed, configured, and talking to each
21- other, not just one ` npm install ` .
18+ running, verified servers. OpenLeads is a two -workspace monorepo — ` api/ `
19+ (Hono + ` node:sqlite ` ) and ` web/ ` (React + Vite) — so "setup" means getting the
20+ pieces installed, configured, and talking to each other, not just one
21+ ` npm install ` .
2222
2323The canonical instructions live in the repo and may drift ahead of this skill.
2424** Read these first and treat them as the source of truth** if they disagree with
2525anything below:
2626
2727- ` README.md ` → "Quick start (development)" and "Configuration"
28- - ` api/.env.example ` and ` scraper/.env.example ` → the authoritative env var list
28+ - ` api/.env.example ` → the authoritative env var list
2929- ` deploy/DEPLOY.md ` → production (Docker Compose + nginx)
3030
3131## Step 0 — Decide scope and gather inputs
@@ -38,10 +38,7 @@ Ask the user only what you can't infer, then proceed. Don't over-interview.
38382 . ** Login credentials** — you need a username and password to seed the first
3939 user. If they don't offer one, suggest ` admin ` and generate a strong password
4040 (and show it to them so they can save it).
41- 3 . ** Scraper?** Optional. Only needed to auto-discover leads, and it needs an
42- ` ANTHROPIC_API_KEY ` . Skip it if they don't have a key or don't ask — the CRM
43- and invoicing run fine without it.
44- 4 . ** Local AI (chat copilot)?** Optional. The Chat module needs an
41+ 3 . ** Local AI (chat copilot)?** Optional. The Chat module needs an
4542 OpenAI-compatible endpoint; the default targets a ** local Ollama** . Skip if
4643 they don't want it — the rest of the app still runs, only Chat is inert.
4744
@@ -56,7 +53,7 @@ OpenLeads uses Node's built-in SQLite, which needs a recent Node:
5653 ` node --version ` and stop here with a clear message if it's older — nothing
5754 downstream will work.
5855- ` npm ` ships with Node.
59- - Ollama is only needed if they chose local AI (Step 5 ).
56+ - Ollama is only needed if they chose local AI (Step 4 ).
6057
6158## Step 2 — API (` api/ ` )
6259
@@ -72,22 +69,18 @@ already there, read it and only fill missing secrets; otherwise start from the
7269example:
7370
74711 . Read ` api/.env.example ` to get the current, authoritative set of variables.
75- 2 . Generate three independent secrets (see
76- [ Generating secrets] ( #generating-secrets ) ):
77- - ` SESSION_SECRET ` — signs session cookies
72+ 2 . Generate one secret (see [ Generating secrets] ( #generating-secrets ) ):
7873 - ` SETTINGS_KEY ` — AES-256-GCM key for credentials saved via the Settings UI
79- - ` SERVICE_TOKEN ` — bearer token the scraper uses to POST leads
80- 3 . Write ` api/.env ` from the example with those three placeholders replaced.
81- Leave ` NODE_ENV=development ` , ` DB_PATH ` , ports, and the AI/SMTP block at their
74+ 3 . Write ` api/.env ` from the example with that placeholder replaced. Leave
75+ ` NODE_ENV=development ` , ` DB_PATH ` , ports, and the AI/SMTP block at their
8276 example defaults unless the user asked otherwise.
8377
84- > ** Why generate all three even in dev?** In development the app falls back to
85- > insecure built-in defaults (with a warning) if these are unset, so it * would*
86- > boot without them. But it ** fails closed in production** — ` api/src/auth.ts `
87- > throws if ` SESSION_SECRET ` is the dev default under ` NODE_ENV=production ` , and
88- > ` api/src/secrets.ts ` refuses to encrypt credentials without a real
89- > ` SETTINGS_KEY ` . Generating them now means the same ` .env ` works when they later
90- > flip to production, and it's the correct habit to model.
78+ > ** Why generate it even in dev?** In development the app falls back to an
79+ > insecure built-in default (with a warning), so it * would* boot without it. But
80+ > it ** fails closed in production** — ` api/src/secrets.ts ` refuses to encrypt
81+ > credentials without a real ` SETTINGS_KEY ` . Generating it now means the same
82+ > ` .env ` works when they later flip to production. (Sessions are stored
83+ > server-side in the DB — there is no session secret anymore.)
9184
9285** Seed the login** (creates the first user; the app has no signup):
9386
@@ -110,50 +103,23 @@ No env file needed in dev — Vite serves on **http://localhost:5173** and proxi
110103` web/vite.config.ts ` ). (If ` README.md ` quotes a different port, trust the actual
111104` vite.config.ts ` .)
112105
113- ## Step 4 — Scraper (` scraper/ ` , optional)
114-
115- Skip unless the user wants automated lead discovery and has an Anthropic key.
116-
117- ```
118- cd ../scraper
119- npm install
120- ```
121-
122- Create ` scraper/.env ` from ` scraper/.env.example ` , then set:
123-
124- - ` ANTHROPIC_API_KEY ` — their key from https://console.anthropic.com
125- - ` CRM_API_URL ` — ` http://127.0.0.1:8787 ` for local
126- - ` CRM_SERVICE_TOKEN ` — ** must exactly equal** the ` SERVICE_TOKEN ` you put in
127- ` api/.env ` . This is the single most common setup mistake: if they differ, the
128- API rejects every posted lead with 401 and nothing shows up. Copy the value
129- across literally.
130-
131- Verify offline first — this uses fixtures, no Claude calls, no network:
132-
133- ```
134- npm run dry-run
135- ```
136-
137- A live run is ` npm start ` (costs Anthropic tokens) — only run it if asked.
138-
139- ## Step 5 — Local AI / Ollama (optional)
106+ ## Step 4 — Local AI / Ollama (optional)
140107
141108Only if the user wants the Chat copilot. The API defaults
142109(` AI_BASE_URL=http://localhost:11434/v1 ` , ` AI_MODEL=llama3.1:8b ` ) target a local
143110Ollama, so no ` api/.env ` change is needed — just make the models available:
144111
1451121 . Install Ollama (https://ollama.com ) if ` ollama --version ` fails.
146- 2 . Pull the chat + embedding models named in ` api/.env.example ` :
113+ 2 . Pull the chat model named in ` api/.env.example ` :
147114 ```
148115 ollama pull llama3.1:8b
149- ollama pull nomic-embed-text
150116 ```
151117
152118If they'd rather use a hosted OpenAI-compatible endpoint, set ` AI_BASE_URL ` ,
153119` AI_MODEL ` , and ` AI_API_KEY ` in ` api/.env ` instead (or configure it later in the
154120Settings page, which encrypts the key at rest with ` SETTINGS_KEY ` ).
155121
156- ## Step 6 — Start and verify
122+ ## Step 5 — Start and verify
157123
158124Start both dev servers (run them in the background / separate processes so they
159125keep running):
@@ -173,24 +139,25 @@ Then **verify rather than assuming** — don't just report "done":
173139- Confirm Vite is serving on 5173.
174140- Tell the user to open ** http://localhost:5173 ** and log in with the credentials
175141 from Step 2. The UI is in German (it targets the DACH market) — mention that so
176- they're not surprised.
142+ they're not surprised. A fresh database starts with the isarwebsites
143+ Leistungskatalog (website packages, hosting/Pflege, SEO) prefilled.
177144
178145Report exactly what came up and what's still optional/disabled (e.g. "Chat is
179- inactive until you finish Step 5", "lead discovery off — no scraper key ").
146+ inactive until you finish Step 4 ").
180147
181148## Production
182149
183- Don't hand-roll this — OpenLeads ships one Docker image (web + API + scraper)
184- and ` deploy/DEPLOY.md ` is the maintained walkthrough (Docker Compose, nginx +
185- TLS, secrets via ` api.env ` / ` scraper.env ` , seeding the login in-container,
186- scheduling the scraper via cron, backups). Read it and follow it, adapting the
187- domain and secrets to the user's host. The same secret rules apply, plus
188- ` WEB_ORIGIN ` must be the public origin and ` NODE_ENV=production ` (which makes the
189- secret checks fail-closed — so the secrets are now mandatory, not optional).
150+ Don't hand-roll this — OpenLeads ships one Docker image (web + API) and
151+ ` deploy/DEPLOY.md ` is the maintained walkthrough (Docker Compose, nginx + TLS,
152+ secrets via ` api.env ` , seeding the login in-container, backups). Read it and
153+ follow it, adapting the domain and secrets to the user's host. The same secret
154+ rules apply, plus ` WEB_ORIGIN ` must be the public origin and
155+ ` NODE_ENV=production ` (which makes the secret checks fail-closed — so
156+ ` SETTINGS_KEY ` is then mandatory, not optional).
190157
191158## Generating secrets
192159
193- Each secret is an independent long random string. Cross-platform:
160+ A secret is a long random string. Cross-platform:
194161
195162** Node (works everywhere Node is installed — the safe default here):**
196163```
@@ -199,17 +166,15 @@ node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
199166
200167** macOS / Linux:** ` openssl rand -hex 32 `
201168
202- Generate a * fresh* value per secret — never reuse one string for two variables,
203- and never reuse the example's ` change-me-... ` placeholders.
169+ Never reuse the example's ` change-me-... ` placeholder.
204170
205171## Common pitfalls
206172
207173- ** Node too old** → ` node:sqlite ` import fails. Needs 22.5+.
208- - ** ` SERVICE_TOKEN ` ≠ ` CRM_SERVICE_TOKEN ` ** → scraper gets 401, no leads land.
209174- ** Editing ` .env.example ` instead of ` .env ` ** → app reads ` .env ` ; the example is
210175 only a template. Never commit ` .env ` .
211176- ** Treating the ` ExperimentalWarning ` as a failure** → it's normal.
212177- ** Expecting Chat to work without an AI endpoint** → it's inert until Ollama (or
213178 a hosted endpoint) is reachable; the rest of the app is unaffected.
214- - ** Production boot refuses to start ** → that's the fail-closed check; set real
215- ` SESSION_SECRET ` / ` SETTINGS_KEY ` , don't downgrade ` NODE_ENV ` .
179+ - ** Saving AI/SMTP credentials fails in production ** → that's the fail-closed
180+ check; set a real ` SETTINGS_KEY ` , don't downgrade ` NODE_ENV ` .
0 commit comments