lockin-mcp 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +156 -45
  2. package/dist/blocker.js +2 -2
  3. package/dist/blocker.js.map +1 -1
  4. package/dist/brand.d.ts +2 -1
  5. package/dist/brand.d.ts.map +1 -1
  6. package/dist/brand.js +2 -1
  7. package/dist/brand.js.map +1 -1
  8. package/dist/config.d.ts +1 -1
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +2 -2
  11. package/dist/config.js.map +1 -1
  12. package/dist/hosts-manager.d.ts.map +1 -1
  13. package/dist/hosts-manager.js +24 -11
  14. package/dist/hosts-manager.js.map +1 -1
  15. package/dist/hosts-manager.test.js +22 -3
  16. package/dist/hosts-manager.test.js.map +1 -1
  17. package/dist/index.js +37 -12
  18. package/dist/index.js.map +1 -1
  19. package/dist/install.js +3 -3
  20. package/dist/install.js.map +1 -1
  21. package/dist/installer/agent-instructions.d.ts +15 -1
  22. package/dist/installer/agent-instructions.d.ts.map +1 -1
  23. package/dist/installer/agent-instructions.js +29 -7
  24. package/dist/installer/agent-instructions.js.map +1 -1
  25. package/dist/installer/agents.d.ts.map +1 -1
  26. package/dist/installer/agents.js +70 -79
  27. package/dist/installer/agents.js.map +1 -1
  28. package/dist/installer/claude-desktop-config.d.ts +19 -0
  29. package/dist/installer/claude-desktop-config.d.ts.map +1 -0
  30. package/dist/installer/claude-desktop-config.js +70 -0
  31. package/dist/installer/claude-desktop-config.js.map +1 -0
  32. package/dist/installer/claude-desktop-config.test.d.ts +2 -0
  33. package/dist/installer/claude-desktop-config.test.d.ts.map +1 -0
  34. package/dist/installer/claude-desktop-config.test.js +49 -0
  35. package/dist/installer/claude-desktop-config.test.js.map +1 -0
  36. package/dist/installer/data-dir.d.ts +7 -0
  37. package/dist/installer/data-dir.d.ts.map +1 -0
  38. package/dist/installer/data-dir.js +38 -0
  39. package/dist/installer/data-dir.js.map +1 -0
  40. package/dist/installer/data-dir.test.d.ts +2 -0
  41. package/dist/installer/data-dir.test.d.ts.map +1 -0
  42. package/dist/installer/data-dir.test.js +59 -0
  43. package/dist/installer/data-dir.test.js.map +1 -0
  44. package/dist/installer/install-log.d.ts +14 -0
  45. package/dist/installer/install-log.d.ts.map +1 -0
  46. package/dist/installer/install-log.js +100 -0
  47. package/dist/installer/install-log.js.map +1 -0
  48. package/dist/installer/platform.d.ts.map +1 -1
  49. package/dist/installer/platform.js +2 -2
  50. package/dist/installer/platform.js.map +1 -1
  51. package/dist/installer/poke.d.ts.map +1 -1
  52. package/dist/installer/poke.js +5 -13
  53. package/dist/installer/poke.js.map +1 -1
  54. package/dist/installer/port.d.ts.map +1 -1
  55. package/dist/installer/port.js +5 -4
  56. package/dist/installer/port.js.map +1 -1
  57. package/dist/installer/relay-provision.d.ts.map +1 -1
  58. package/dist/installer/relay-provision.js +62 -25
  59. package/dist/installer/relay-provision.js.map +1 -1
  60. package/dist/installer/run.d.ts.map +1 -1
  61. package/dist/installer/run.js +37 -34
  62. package/dist/installer/run.js.map +1 -1
  63. package/dist/installer/tui.d.ts.map +1 -1
  64. package/dist/installer/tui.js +5 -3
  65. package/dist/installer/tui.js.map +1 -1
  66. package/dist/installer/tunnel.d.ts.map +1 -1
  67. package/dist/installer/tunnel.js +7 -2
  68. package/dist/installer/tunnel.js.map +1 -1
  69. package/dist/installer/user-errors.d.ts +8 -0
  70. package/dist/installer/user-errors.d.ts.map +1 -0
  71. package/dist/installer/user-errors.js +61 -0
  72. package/dist/installer/user-errors.js.map +1 -0
  73. package/dist/installer/user-errors.test.d.ts +2 -0
  74. package/dist/installer/user-errors.test.d.ts.map +1 -0
  75. package/dist/installer/user-errors.test.js +15 -0
  76. package/dist/installer/user-errors.test.js.map +1 -0
  77. package/dist/local-http.d.ts +32 -0
  78. package/dist/local-http.d.ts.map +1 -0
  79. package/dist/local-http.js +140 -0
  80. package/dist/local-http.js.map +1 -0
  81. package/dist/local-http.test.d.ts +2 -0
  82. package/dist/local-http.test.d.ts.map +1 -0
  83. package/dist/local-http.test.js +13 -0
  84. package/dist/local-http.test.js.map +1 -0
  85. package/dist/relay-client.d.ts.map +1 -1
  86. package/dist/relay-client.js +26 -23
  87. package/dist/relay-client.js.map +1 -1
  88. package/dist/state.d.ts.map +1 -1
  89. package/dist/state.js +4 -4
  90. package/dist/state.js.map +1 -1
  91. package/dist/types.d.ts +5 -2
  92. package/dist/types.d.ts.map +1 -1
  93. package/dist/types.js +5 -2
  94. package/dist/types.js.map +1 -1
  95. package/install.ps1 +1 -1
  96. package/install.sh +1 -1
  97. package/package.json +2 -1
package/README.md CHANGED
@@ -9,11 +9,13 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that b
9
9
  | `block_domains` | Block a list of domains (e.g. `youtube.com`, `x.com`, `tiktok.com`) |
10
10
  | `unblock_domains` | Permanently unblock specific domains |
11
11
  | `temporarily_unblock_domains` | Allow access for a limited time, then auto re-block |
12
- | `enter_focus_mode` | Block common distraction sites in one action |
12
+ | `enter_focus_mode` | Block common distraction sites in one action (YouTube, X, TikTok, Instagram, Reddit, Facebook, Netflix, Twitch) |
13
13
  | `get_block_status` | Show what is blocked, temporarily allowed, and system readiness |
14
14
 
15
15
  Blocking redirects domains to `127.0.0.1` and `::1`, including `www.` variants.
16
16
 
17
+ **Pro license required:** `block_domains`, `unblock_domains`, `temporarily_unblock_domains`, and `enter_focus_mode` require an active Pro license. `get_block_status` is read-only and works without Pro. For local development only, set `MDB_SKIP_LICENSE_CHECK=1`.
18
+
17
19
  ## Requirements
18
20
 
19
21
  - **macOS / Linux / Windows** (hosts-based blocking; macOS uses `dscacheutil` for DNS flush)
@@ -49,6 +51,9 @@ npm run install:local
49
51
  | Landing page (`/`) | Features, how-it-works, pricing |
50
52
  | Device auth (`/device`) | Browser login for CLI OAuth device flow |
51
53
  | Dashboard (`/dashboard`) | License keys, Pro status, install instructions |
54
+ | Report (`/report`) | Bug reports and feature requests (rate-limited; not indexed) |
55
+ | MCP OAuth consent (`/oauth/consent`) | Browser approval for ChatGPT / Claude web MCP connectors |
56
+ | Checkout (`/checkout`) | Stripe checkout (requires verified email) |
52
57
  | API (`/api/v1/*`) | License verify, OAuth device flow, token status |
53
58
  | Stripe webhook | Pro upgrade + license key generation |
54
59
 
@@ -60,6 +65,14 @@ npm run install:local
60
65
  4. Installer polls `POST /api/v1/oauth/token` → receives `access_token`
61
66
  5. Daemon verifies token via `POST /api/v1/license/status` on startup and hourly
62
67
 
68
+ ### MCP OAuth (ChatGPT, Claude web)
69
+
70
+ 1. MCP client registers via DCR at `POST /api/v1/oauth/register` (proxied from relay at `POST /register`)
71
+ 2. Client discovers metadata from `/.well-known/oauth-authorization-server` (web app and relay)
72
+ 3. User authorizes at `/api/v1/oauth/authorize` → browser consent at `/oauth/consent`
73
+ 4. Client exchanges code at `POST /api/v1/oauth/token`
74
+ 5. MCP requests to relay use OAuth access tokens or the device `mdb_*` API key
75
+
63
76
  ### Environment variables
64
77
 
65
78
  See [`.env.example`](.env.example) for all configuration options.
@@ -76,14 +89,37 @@ openssl rand -base64 32 # MDB_SERVER_SECRET
76
89
  openssl rand -base64 32 # REGISTRATION_SECRET (relay)
77
90
  ```
78
91
 
79
- Set the same values in **both** your hosting provider (Next.js) and **Convex production** (`./scripts/sync-convex-env.sh` after filling `web/.env.local`).
92
+ Set **Vercel** and **Convex production** env vars separately in each dashboard — do **not** copy `MDB_SERVER_SECRET` from your laptop with a sync script (local dev secrets are different from production).
93
+
94
+ | Secret | Vercel | Convex prod |
95
+ |--------|--------|-------------|
96
+ | `MDB_SERVER_SECRET` | Yes — same value on both | Yes — `npx convex env set MDB_SERVER_SECRET '…' --prod` once |
97
+ | `MDB_JWT_SECRET` | No | Yes — Convex only |
98
+ | `STRIPE_*` | Yes | No |
80
99
 
81
100
  #### 2. Convex production
82
101
 
83
102
  ```bash
84
103
  cd web
85
104
  npx convex deploy # production only — not convex dev
86
- ./scripts/sync-convex-env.sh # from repo root, syncs secrets to Convex
105
+ ```
106
+
107
+ Set env vars in the [Convex dashboard](https://dashboard.convex.dev) → **Production** → Settings → Environment Variables. **Do not** run `./scripts/sync-convex-env.sh` for production — it only targets your dev deployment and `.env.local` often has `localhost` URLs.
108
+
109
+ | Convex prod variable | Value |
110
+ |---------------------|--------|
111
+ | `MDB_APP_URL` | `https://www.lockinmcp.com` |
112
+ | `MDB_SERVER_SECRET` | Same string as Vercel |
113
+ | `MDB_JWT_SECRET` | Production JWT secret (Convex only) |
114
+ | `GOOGLE_*` / `GITHUB_*` | Production OAuth app credentials |
115
+ | `RESEND_API_KEY` / `RESEND_FROM_EMAIL` | Production Resend |
116
+ | `MDB_RELAY_URL` / `MDB_RELAY_PUBLIC_URL` | `https://relay.lockinmcp.com` |
117
+ | `REGISTRATION_SECRET` | Same as relay Worker |
118
+
119
+ One-off CLI (paste production values, not from `.env.local`):
120
+
121
+ ```bash
122
+ cd web && npx convex env set MDB_APP_URL 'https://www.lockinmcp.com' --prod
87
123
  ```
88
124
 
89
125
  #### 3. Stripe live mode
@@ -94,30 +130,52 @@ In [Stripe Dashboard](https://dashboard.stripe.com) (toggle **Live**):
94
130
  |----------|-----------------|
95
131
  | `STRIPE_SECRET_KEY` | Developers → API keys → `sk_live_...` |
96
132
  | `STRIPE_PRICE_ID` | Products → one-time $9.99 price → `price_...` |
97
- | `STRIPE_WEBHOOK_SECRET` | Developers → Webhooks → endpoint `https://lockinmcp.com/api/stripe/webhook` → `whsec_...` |
133
+ | `STRIPE_WEBHOOK_SECRET` | Developers → Webhooks → endpoint `https://www.lockinmcp.com/api/stripe/webhook` → `whsec_...` |
98
134
  | `STRIPE_PROMOTION_CODE_ID` | Optional — locks one promo at checkout; leave unset so customers can enter any code (e.g. `LOCKIN50`) |
99
135
 
100
- Webhook events: **`checkout.session.completed`**
136
+ Webhook events: **`checkout.session.completed`**, **`checkout.session.created`**, **`checkout.session.expired`** (follow-up scheduling backup)
137
+
138
+ #### Abandoned checkout follow-up (24h email)
139
+
140
+ When a signed-in user starts Stripe Checkout but does not pay, a follow-up email is scheduled via Convex (`ctx.scheduler.runAfter`). After deploy:
141
+
142
+ ```bash
143
+ cd web && npx convex deploy
144
+ ./scripts/backfill-oauth-email-verified.sh --prod # one-time OAuth emailVerified backfill
145
+ ```
146
+
147
+ **Verify production:**
148
+
149
+ 1. Convex `MDB_APP_URL` is `https://www.lockinmcp.com` (not `localhost`).
150
+ 2. Vercel `MDB_SERVER_SECRET` === Convex `MDB_SERVER_SECRET`.
151
+ 2. Stripe webhook enables `checkout.session.created` and `checkout.session.expired`.
152
+ 3. Preview email: `./scripts/send-abandoned-checkout-preview.sh you@example.com`
153
+ 4. Dev smoke test (60s delay): `npx convex env set CHECKOUT_FOLLOW_UP_DELAY_MS 60000` then `./scripts/test-abandoned-checkout-schedule.sh <userId>`
154
+ 5. Inspect jobs: `./scripts/list-checkout-follow-ups.sh scheduled`
155
+
156
+ Optional Convex env: `CHECKOUT_FOLLOW_UP_DELAY_MS` (default `86400000` = 24h).
101
157
 
102
158
  #### 4. OAuth (production callbacks)
103
159
 
104
160
  | Provider | Redirect URI |
105
161
  |----------|--------------|
106
- | Google | `https://lockinmcp.com/api/auth/oauth/google/callback` |
107
- | GitHub | `https://lockinmcp.com/api/auth/oauth/github/callback` |
162
+ | Google | `https://www.lockinmcp.com/api/auth/oauth/google/callback` |
163
+ | GitHub | `https://www.lockinmcp.com/api/auth/oauth/github/callback` |
108
164
 
109
- Add `GOOGLE_*` / `GITHUB_*` to `web/.env.local` and sync to Convex.
165
+ Add `GOOGLE_*` / `GITHUB_*` to Vercel and Convex production dashboards (not dev keys from `.env.local`).
110
166
 
111
167
  #### 5. App URLs (hosting + Convex)
112
168
 
113
169
  ```bash
114
- MDB_APP_URL=https://lockinmcp.com
115
- NEXT_PUBLIC_APP_URL=https://lockinmcp.com
116
- MDB_LICENSE_API_URL=https://lockinmcp.com/api/v1
170
+ MDB_APP_URL=https://www.lockinmcp.com
171
+ NEXT_PUBLIC_APP_URL=https://www.lockinmcp.com
172
+ MDB_LICENSE_API_URL=https://www.lockinmcp.com/api/v1
117
173
  MDB_RELAY_PUBLIC_URL=https://relay.lockinmcp.com
118
174
  MDB_RELAY_URL=https://relay.lockinmcp.com
119
175
  ```
120
176
 
177
+ The apex domain (`lockinmcp.com`) redirects to `www` in production.
178
+
121
179
  #### 6. Deploy web app
122
180
 
123
181
  Deploy the `web/` Next.js app to your host (Vercel, etc.) with all env vars from `.env.example`.
@@ -126,7 +184,7 @@ Deploy the `web/` Next.js app to your host (Vercel, etc.) with all env vars from
126
184
 
127
185
  1. Sign up → verify email → checkout with a real card (or Stripe test in test mode first)
128
186
  2. Confirm `/dashboard` shows Pro + license key
129
- 3. Run installer: `curl -fsSL https://lockinmcp.com/install | bash`
187
+ 3. Run installer: `curl -fsSL https://www.lockinmcp.com/install | bash`
130
188
  4. Approve device at `/device` → confirm MCP tunnel provisions
131
189
 
132
190
  ---
@@ -138,23 +196,31 @@ The fastest way to set up blocking, tunneling, and Poke integration:
138
196
  ### macOS / Linux (curl)
139
197
 
140
198
  ```bash
141
- curl -fsSL https://raw.githubusercontent.com/Kiog-Aser/mac-distraction-blocker-mcp/main/install.sh | bash
199
+ curl -fsSL https://raw.githubusercontent.com/Kiog-Aser/LockIn/main/install.sh | bash
200
+ ```
201
+
202
+ Or use the hosted installer (recommended):
203
+
204
+ ```bash
205
+ curl -fsSL https://www.lockinmcp.com/install | bash
142
206
  ```
143
207
 
144
208
  ### Windows (PowerShell)
145
209
 
146
210
  ```powershell
147
- iwr -useb https://raw.githubusercontent.com/Kiog-Aser/mac-distraction-blocker-mcp/main/install.ps1 | iex
211
+ iwr -useb https://raw.githubusercontent.com/Kiog-Aser/LockIn/main/install.ps1 | iex
148
212
  ```
149
213
 
150
214
  ### npm
151
215
 
152
216
  ```bash
153
- npx -y lockin-mcp install
217
+ npx -y lockin-mcp@1.0.4 install
154
218
  # or from a local clone:
155
219
  npm run install:local
156
220
  ```
157
221
 
222
+ The npm package `lockin-mcp` (v1.0.4) replaces the legacy `mac-distraction-blocker-mcp` name. Legacy bin aliases (`mac-distraction-blocker-mcp`, `mdb-install`) still work.
223
+
158
224
  ### What the installer does
159
225
 
160
226
  1. **Detects your platform** (macOS, Linux, or Windows)
@@ -163,7 +229,7 @@ npm run install:local
163
229
  4. **Applies initial blocks** to `/etc/hosts`
164
230
  5. **Starts the MCP HTTP server** locally with an outbound relay connection (background LaunchAgent on macOS)
165
231
  6. **Provisions a stable relay URL** (`https://relay.lockinmcp.com/device/{id}/mcp`) so Poke, Claude, and ChatGPT can reach your Mac
166
- 7. **Connects your AI agent** — Poke, Claude Desktop, or ChatGPT (URL + API key from manifest)
232
+ 7. **Connects your AI agent** — Poke and Claude Desktop use your API key; Claude (web) and ChatGPT use OAuth via lockinmcp.com
167
233
 
168
234
  ### Installer flags (non-interactive)
169
235
 
@@ -183,22 +249,23 @@ npx -y lockin-mcp install --license-key lockin_pro_xxx --sites all --poke-recipe
183
249
  | `--poke-recipe` | Generate distraction-coach recipe URL |
184
250
  | `--mcp-auth-token <token>` | Custom bearer token for tunneled MCP |
185
251
 
186
- Setup details are saved to `~/.mac-distraction-blocker/setup-manifest.json`.
252
+ Setup details are saved to `~/.lockin/setup-manifest.json`.
187
253
 
188
254
  ### Pro licensing ($9.99 lifetime — 50% off $19.99)
189
255
 
190
- - **Purchase**: Sign up (email or Google/GitHub), verify email, pay via Stripe
256
+ - **Purchase**: Create an account at `/login` (email or Google/GitHub; `/signup` redirects here), verify email, pay via Stripe
191
257
  - **License key**: Available on dashboard after purchase; pass `--license-key` to installer
192
258
  - **Browser login**: Default installer flow — OAuth device authorization in your browser
193
259
  - **Auth**: Google and GitHub OAuth supported; email/password requires verification before checkout
260
+ - **Support:** Bug reports and feature requests at `/report` (also linked in the site footer)
194
261
 
195
262
  ---
196
263
 
197
264
  ## Quick Start (manual)
198
265
 
199
266
  ```bash
200
- git clone <this-repo>
201
- cd mac-distraction-blocker-mcp
267
+ git clone https://github.com/Kiog-Aser/LockIn
268
+ cd LockIn
202
269
  npm install
203
270
  npm run build
204
271
  ```
@@ -213,7 +280,7 @@ node dist/index.js --stdio
213
280
 
214
281
  ## MCP Client Configuration (after installer)
215
282
 
216
- Your MCP URL and API key are in `~/.mac-distraction-blocker/setup-manifest.json`:
283
+ Your MCP URL and API key are in `~/.lockin/setup-manifest.json`:
217
284
 
218
285
  - **MCP URL:** from manifest — e.g. `https://relay.lockinmcp.com/device/abc123/mcp` (Poke, Claude, ChatGPT)
219
286
  - **API key:** `mdb_…`
@@ -227,28 +294,59 @@ npx poke@latest mcp add 'https://YOUR_RELAY_HOST/device/YOUR_ID/mcp' -n 'LockIn
227
294
 
228
295
  Or set URL + API key in [poke.com/integrations](https://poke.com/integrations).
229
296
 
297
+ ### Claude (claude.ai)
298
+
299
+ The installer opens a link that **pre-fills the connector name and MCP URL** (`connectorName` + `connectorUrl`). Claude web uses **OAuth** — do not paste your API key in Advanced settings. See the [connector setup guide](https://lockinmcp.com/docs/connectors#claude-web).
300
+
301
+ 1. Choose **Claude (claude.ai)** in the installer (or build the link with `connectorName=LockIn MCP` and your encoded MCP URL)
302
+ 2. Confirm the pre-filled MCP server URL (e.g. `https://relay.lockinmcp.com/device/abc123/mcp`)
303
+ 3. Leave Advanced OAuth settings empty. Click Add, then sign in on lockinmcp.com when Claude prompts (Pro license required)
304
+ 4. Start a new chat with LockIn enabled
305
+
306
+ For Bearer/API-key auth, use **Claude Desktop** instead — the installer can write `claude_desktop_config.json` automatically.
307
+
230
308
  ### Claude Desktop
231
309
 
232
- `~/Library/Application Support/Claude/claude_desktop_config.json`:
310
+ Claude Desktop only supports **stdio** MCP servers in `claude_desktop_config.json` — not direct `url` entries. The installer merges a config that uses [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) to bridge your relay URL (requires Node.js 18+).
311
+
312
+ **Config paths:**
313
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
314
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
315
+ - Linux: `~/.config/Claude/claude_desktop_config.json`
316
+
317
+ Manual example:
233
318
 
234
319
  ```json
235
320
  {
236
321
  "mcpServers": {
237
322
  "lockin-mcp": {
238
- "url": "https://YOUR_TUNNEL_HOST/mcp",
239
- "headers": {
240
- "Authorization": "Bearer mdb_..."
323
+ "command": "npx",
324
+ "args": [
325
+ "-y",
326
+ "mcp-remote@latest",
327
+ "https://relay.lockinmcp.com/device/YOUR_ID/mcp",
328
+ "--header",
329
+ "Authorization:${LOCKIN_MCP_AUTH_HEADER}"
330
+ ],
331
+ "env": {
332
+ "LOCKIN_MCP_AUTH_HEADER": "Bearer mdb_..."
241
333
  }
242
334
  }
243
335
  }
244
336
  }
245
337
  ```
246
338
 
339
+ On Windows, use `"command": "npx.cmd"` instead of `"npx"`. Quit Claude Desktop completely before editing, then reopen.
340
+
247
341
  ### ChatGPT
248
342
 
249
- 1. Open ChatGPT Settings Connectors
250
- 2. Add MCP server URL from your manifest (e.g. `https://….trycloudflare.com/mcp`)
251
- 3. Paste API key when prompted: `mdb_...`
343
+ Custom MCP connectors require **Developer mode** under Advanced settings. ChatGPT cannot accept credentials from an external link — copy from the installer or [setup guide](https://lockinmcp.com/docs/connectors#chatgpt).
344
+
345
+ 1. Open [ChatGPT Settings Connectors → Advanced](https://chatgpt.com/#settings/Connectors/Advanced)
346
+ 2. Enable **Developer mode**
347
+ 3. Add connector → paste MCP server URL from your manifest
348
+ 4. Set Authentication to OAuth and leave DCR enabled — sign in on lockinmcp.com when prompted (do not paste your API key in OAuth fields)
349
+ 5. Start a new chat and enable the LockIn connector
252
350
 
253
351
  ### Relay deployment (operators)
254
352
 
@@ -262,18 +360,20 @@ npx wrangler secret put MDB_JWT_SECRET # optional, same as web app
262
360
  npm run deploy
263
361
  ```
264
362
 
265
- Set `MDB_RELAY_URL`, `MDB_RELAY_PUBLIC_URL`, and `REGISTRATION_SECRET` in `web/.env.local` and sync to Convex via `./scripts/sync-convex-env.sh`.
363
+ Set relay vars on **Convex production** in the dashboard. For local dev only: `web/.env.local` + `./scripts/sync-convex-env.sh` (dev deployment).
266
364
 
267
365
  ## Privileges & `/etc/hosts`
268
366
 
269
367
  The server writes marked entries between:
270
368
 
271
369
  ```
272
- # mac-distraction-blocker-mcp BEGIN
370
+ # lockin-mcp BEGIN
273
371
  ...
274
- # mac-distraction-blocker-mcp END
372
+ # lockin-mcp END
275
373
  ```
276
374
 
375
+ Legacy installs may still show `# mac-distraction-blocker-mcp` markers; those are stripped on the next hosts sync.
376
+
277
377
  Only those lines are touched; the rest of `/etc/hosts` is preserved.
278
378
 
279
379
  ### Option A: Run the server with `sudo` (simplest)
@@ -286,28 +386,26 @@ For stdio MCP clients, configure the client to launch with `sudo` (see client co
286
386
 
287
387
  ### Option B: Passwordless `sudo` for hosts updates (recommended)
288
388
 
289
- The server falls back to `sudo cp` when it cannot write `/etc/hosts` directly. To avoid prompts during tool calls, allow your user to copy a temp file into hosts without a password:
389
+ The installer creates `~/.lockin/update-hosts.sh` and a sudoers entry at `/etc/sudoers.d/lockin-mcp-hosts` that allows your user to run that script without a password. Temp files use the prefix `lockin-mcp-hosts-{pid}`.
390
+
391
+ If you installed manually, re-run the installer or configure sudoers to allow (replace `YOUR_USER` with your macOS username):
290
392
 
291
393
  ```bash
292
- sudo visudo -f /etc/sudoers.d/mac-distraction-blocker
394
+ sudo visudo -f /etc/sudoers.d/lockin-mcp-hosts
293
395
  ```
294
396
 
295
- Add (replace `YOUR_USER` with your macOS username):
397
+ Add:
296
398
 
297
399
  ```
298
- YOUR_USER ALL=(ALL) NOPASSWD: /bin/cp /tmp/mac-distraction-blocker-hosts-* /etc/hosts
299
- YOUR_USER ALL=(ALL) NOPASSWD: /usr/sbin/dscacheutil -flushcache
300
- YOUR_USER ALL=(ALL) NOPASSWD: /usr/bin/killall -HUP mDNSResponder
400
+ YOUR_USER ALL=(ALL) NOPASSWD: /Users/YOUR_USER/.lockin/update-hosts.sh
301
401
  ```
302
402
 
403
+ (Adjust paths for Linux/Windows as needed.)
404
+
303
405
  ### Verify permissions
304
406
 
305
407
  Call `get_block_status` — `hostsFileWritable: true` means the server can update blocking without extra setup.
306
408
 
307
- ## MCP Client Configuration
308
-
309
- See **MCP Client Configuration (after installer)** above.
310
-
311
409
  ### Legacy: local stdio (Claude Desktop)
312
410
 
313
411
  `~/Library/Application Support/Claude/claude_desktop_config.json`:
@@ -317,12 +415,20 @@ See **MCP Client Configuration (after installer)** above.
317
415
  "mcpServers": {
318
416
  "lockin-mcp": {
319
417
  "command": "node",
320
- "args": ["/absolute/path/to/mac-distraction-blocker-mcp/dist/index.js", "--stdio"]
418
+ "args": ["/absolute/path/to/LockIn/dist/index.js", "--stdio"]
321
419
  }
322
420
  }
323
421
  }
324
422
  ```
325
423
 
424
+ ## Security
425
+
426
+ - **MCP bearer auth:** HTTP MCP requires `MDB_MCP_AUTH_TOKEN` or `--mcp-auth-token` (`mdb_*` tokens). Relay-backed HTTP refuses to start without a token.
427
+ - **API rate limiting:** Login, signup, OAuth (account + MCP), license verify, checkout, consent, and user-report routes are rate-limited.
428
+ - **User reports:** `POST /api/report` accepts bug/feature submissions (5 per IP per hour). Requires `RESEND_API_KEY` in production; falls back to Convex logs in dev.
429
+ - **Relay tokens:** Device tokens are hashed at rest on the Cloudflare worker (SHA-256).
430
+ - **Setup manifest:** Written with `0o600` permissions; contains your relay URL and API key — do not commit or share.
431
+
326
432
  ## Hooking up to [poke.com/kitchen](https://poke.com/kitchen)
327
433
 
328
434
  The installer tunnels your local MCP over HTTPS so Poke can connect remotely — no extra terminal tab.
@@ -379,13 +485,13 @@ No arguments. Returns blocked list, temporary allowances with expiry timestamps,
379
485
  flowchart LR
380
486
  A[MCP Client / Poke] --> R[Relay Worker]
381
487
  R --> B[MCP Server on Mac]
382
- B --> C[State JSON\n~/.mac-distraction-blocker/]
488
+ B --> C[State JSON\n~/.lockin/]
383
489
  B --> D[/etc/hosts]
384
490
  B --> E[DNS cache flush]
385
491
  C --> B
386
492
  ```
387
493
 
388
- 1. **State** — Block list and temporary unblocks live in `~/.mac-distraction-blocker/state.json`
494
+ 1. **State** — Block list and temporary unblocks live in `~/.lockin/state.json`
389
495
  2. **Hosts sync** — Active blocks are written to `/etc/hosts` with a managed marker
390
496
  3. **Expiry watcher** — A background timer re-applies blocks when temporary unblocks expire (server process must be running)
391
497
  4. **DNS flush** — Best-effort `dscacheutil` / `mDNSResponder` refresh after changes
@@ -397,8 +503,13 @@ npm install
397
503
  npm run dev # watch TypeScript
398
504
  npm run build
399
505
  npm run lint
506
+ npm test # Vitest (root MCP tests)
507
+ cd web && npm test # web rate-limit tests
508
+ cd relay && npm test # relay token hashing tests
400
509
  ```
401
510
 
511
+ CI (`.github/workflows/ci.yml`) runs lint, test, build, and `npm audit --audit-level=high` for the MCP server, web app, and relay worker.
512
+
402
513
  ## Troubleshooting
403
514
 
404
515
  | Issue | Fix |
@@ -407,7 +518,7 @@ npm run lint
407
518
  | Site still loads | Hard-refresh browser; DNS cache may take a moment. Try private window. |
408
519
  | Temp unblock didn't re-block | Keep the MCP server process running (expiry watcher runs in-process) |
409
520
  | Poke can't connect | Check manifest relay URL + `/health` returns `lockin-mcp`; verify relay device is online |
410
- | Wrong platform | Designed for macOS; Linux may work with `/etc/hosts` but DNS flush differs |
521
+ | Wrong platform | macOS, Linux, and Windows are supported; DNS flush behavior differs by OS |
411
522
 
412
523
  ## License
413
524
 
package/dist/blocker.js CHANGED
@@ -71,12 +71,12 @@ export async function blockDomains(domains) {
71
71
  if (method === "unchanged" && newDomains.length === 0) {
72
72
  return {
73
73
  blocked: validated,
74
- message: `${validated.length} domain(s) already blocked.`,
74
+ message: `${validated.length} site(s) already blocked.`,
75
75
  };
76
76
  }
77
77
  return {
78
78
  blocked: validated,
79
- message: `Blocked ${validated.length} domain(s) via /etc/hosts (${method}).`,
79
+ message: `Blocked ${validated.length} site(s) via /etc/hosts (${method}).`,
80
80
  };
81
81
  }
82
82
  export async function unblockDomains(domains) {
@@ -1 +1 @@
1
- {"version":3,"file":"blocker.js","sourceRoot":"","sources":["../src/blocker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"blocker.js","sourceRoot":"","sources":["../src/blocker.ts"],"names":[],"mappings":"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"}
package/dist/brand.d.ts CHANGED
@@ -2,11 +2,12 @@ export declare const APP_NAME = "LockIn MCP";
2
2
  export declare const APP_SLUG = "lockin-mcp";
3
3
  export declare const APP_TAGLINE = "Block distractions. Lock in focus.";
4
4
  export declare const APP_DOMAIN = "lockinmcp.com";
5
+ export declare const CANONICAL_ORIGIN = "https://www.lockinmcp.com";
5
6
  export declare const RELAY_HOST = "relay.lockinmcp.com";
6
7
  export declare const RELAY_PUBLIC_ORIGIN = "https://relay.lockinmcp.com";
7
8
  export declare const LICENSE_PRICE_ORIGINAL = "$19.99";
8
9
  export declare const LICENSE_PRICE = "$9.99";
9
10
  export declare const LICENSE_DISCOUNT_LABEL = "50% off \u2014 forever";
10
11
  export declare const POKE_LOCKIN_RECIPE_URL = "https://poke.com/r/KQ7myvC_Xpo";
11
- export declare const ICON_URL = "https://lockinmcp.com/icon.png";
12
+ export declare const ICON_URL = "https://www.lockinmcp.com/icon.png";
12
13
  //# sourceMappingURL=brand.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,eAAe,CAAC;AACrC,eAAO,MAAM,QAAQ,eAAe,CAAC;AACrC,eAAO,MAAM,WAAW,uCAAuC,CAAC;AAChE,eAAO,MAAM,UAAU,kBAAkB,CAAC;AAC1C,eAAO,MAAM,UAAU,wBAAwB,CAAC;AAChD,eAAO,MAAM,mBAAmB,gCAA0B,CAAC;AAC3D,eAAO,MAAM,sBAAsB,WAAW,CAAC;AAC/C,eAAO,MAAM,aAAa,UAAU,CAAC;AACrC,eAAO,MAAM,sBAAsB,2BAAsB,CAAC;AAC1D,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AACvE,eAAO,MAAM,QAAQ,mCAAmC,CAAC"}
1
+ {"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,eAAe,CAAC;AACrC,eAAO,MAAM,QAAQ,eAAe,CAAC;AACrC,eAAO,MAAM,WAAW,uCAAuC,CAAC;AAChE,eAAO,MAAM,UAAU,kBAAkB,CAAC;AAC1C,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAC5D,eAAO,MAAM,UAAU,wBAAwB,CAAC;AAChD,eAAO,MAAM,mBAAmB,gCAA0B,CAAC;AAC3D,eAAO,MAAM,sBAAsB,WAAW,CAAC;AAC/C,eAAO,MAAM,aAAa,UAAU,CAAC;AACrC,eAAO,MAAM,sBAAsB,2BAAsB,CAAC;AAC1D,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AACvE,eAAO,MAAM,QAAQ,uCAAiC,CAAC"}
package/dist/brand.js CHANGED
@@ -2,11 +2,12 @@ export const APP_NAME = "LockIn MCP";
2
2
  export const APP_SLUG = "lockin-mcp";
3
3
  export const APP_TAGLINE = "Block distractions. Lock in focus.";
4
4
  export const APP_DOMAIN = "lockinmcp.com";
5
+ export const CANONICAL_ORIGIN = "https://www.lockinmcp.com";
5
6
  export const RELAY_HOST = `relay.${APP_DOMAIN}`;
6
7
  export const RELAY_PUBLIC_ORIGIN = `https://${RELAY_HOST}`;
7
8
  export const LICENSE_PRICE_ORIGINAL = "$19.99";
8
9
  export const LICENSE_PRICE = "$9.99";
9
10
  export const LICENSE_DISCOUNT_LABEL = "50% off — forever";
10
11
  export const POKE_LOCKIN_RECIPE_URL = "https://poke.com/r/KQ7myvC_Xpo";
11
- export const ICON_URL = `https://${APP_DOMAIN}/icon.png`;
12
+ export const ICON_URL = `${CANONICAL_ORIGIN}/icon.png`;
12
13
  //# sourceMappingURL=brand.js.map
package/dist/brand.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"brand.js","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AACrC,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AACrC,MAAM,CAAC,MAAM,WAAW,GAAG,oCAAoC,CAAC;AAChE,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAC1C,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,UAAU,EAAE,CAAC;AAChD,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,UAAU,EAAE,CAAC;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC;AAC/C,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC;AACrC,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAC1D,MAAM,CAAC,MAAM,sBAAsB,GAAG,gCAAgC,CAAC;AACvE,MAAM,CAAC,MAAM,QAAQ,GAAG,WAAW,UAAU,WAAW,CAAC"}
1
+ {"version":3,"file":"brand.js","sourceRoot":"","sources":["../src/brand.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AACrC,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AACrC,MAAM,CAAC,MAAM,WAAW,GAAG,oCAAoC,CAAC;AAChE,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAC1C,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAC5D,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,UAAU,EAAE,CAAC;AAChD,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,UAAU,EAAE,CAAC;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC;AAC/C,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC;AACrC,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAC1D,MAAM,CAAC,MAAM,sBAAsB,GAAG,gCAAgC,CAAC;AACvE,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,gBAAgB,WAAW,CAAC"}
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Production app origin when MDB_APP_URL is unset (e.g. `npx lockin-mcp install`). */
2
- export declare const DEFAULT_APP_URL = "https://lockinmcp.com";
2
+ export declare const DEFAULT_APP_URL = "https://www.lockinmcp.com";
3
3
  export declare function getAppUrl(): string;
4
4
  export declare function getLicenseApiUrl(): string;
5
5
  export declare function getPurchaseUrl(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,uFAAuF;AACvF,eAAO,MAAM,eAAe,0BAA0B,CAAC;AAEvD,wBAAgB,SAAS,IAAI,MAAM,CAMlC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,uFAAuF;AACvF,eAAO,MAAM,eAAe,8BAAmB,CAAC;AAEhD,wBAAgB,SAAS,IAAI,MAAM,CAMlC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
package/dist/config.js CHANGED
@@ -1,6 +1,6 @@
1
- import { APP_DOMAIN } from "./brand.js";
1
+ import { CANONICAL_ORIGIN } from "./brand.js";
2
2
  /** Production app origin when MDB_APP_URL is unset (e.g. `npx lockin-mcp install`). */
3
- export const DEFAULT_APP_URL = `https://${APP_DOMAIN}`;
3
+ export const DEFAULT_APP_URL = CANONICAL_ORIGIN;
4
4
  export function getAppUrl() {
5
5
  return (process.env.MDB_APP_URL ??
6
6
  process.env.NEXT_PUBLIC_APP_URL ??
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,UAAU,EAAE,CAAC;AAEvD,MAAM,UAAU,SAAS;IACvB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,WAAW;QACvB,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,eAAe,CAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IACnD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,GAAG,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,GAAG,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,2BAA2B,CAAC;AACtE,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAEhD,MAAM,UAAU,SAAS;IACvB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,WAAW;QACvB,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,eAAe,CAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IACnD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,GAAG,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,GAAG,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,2BAA2B,CAAC;AACtE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"hosts-manager.d.ts","sourceRoot":"","sources":["../src/hosts-manager.ts"],"names":[],"mappings":"AA4DA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS1D;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAErD;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CA+BhE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAyB7D;AAED,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,CAOR;AA6BD,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,CAehF;AA2CD,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAcnD;AAED,wBAAsB,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACrE,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACxC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CAiBD;AAED,+DAA+D;AAC/D,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAK7D"}
1
+ {"version":3,"file":"hosts-manager.d.ts","sourceRoot":"","sources":["../src/hosts-manager.ts"],"names":[],"mappings":"AA8DA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS1D;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAErD;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CA+BhE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAwB7D;AAkBD,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,CAOR;AA6BD,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,CAehF;AA2CD,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAcnD;AAED,wBAAsB,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACrE,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACxC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CAiBD;AAED,+DAA+D;AAC/D,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAK7D"}
@@ -3,7 +3,7 @@ import { execFile, spawn } from "node:child_process";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { promisify } from "node:util";
6
- import { BLOCK_IP, BLOCK_IP_V6, MARKER, expandDomainVariants, getHostsPath, } from "./types.js";
6
+ import { BLOCK_IP, BLOCK_IP_V6, HOSTS_MARKERS, MARKER, expandDomainVariants, getHostsPath, normalizeDomain, } from "./types.js";
7
7
  import { ensureHostsUpdaterScript, writeHostsViaPasswordlessScript, } from "./installer/hosts-sudo.js";
8
8
  import { commandExists } from "./installer/platform.js";
9
9
  const execFileAsync = promisify(execFile);
@@ -58,7 +58,7 @@ export function stripManagedEntries(hostsContent) {
58
58
  const kept = [];
59
59
  let skipping = false;
60
60
  for (const line of lines) {
61
- if (line.includes(MARKER)) {
61
+ if (HOSTS_MARKERS.some((marker) => line.includes(marker))) {
62
62
  skipping = true;
63
63
  continue;
64
64
  }
@@ -80,27 +80,40 @@ export function stripManagedEntries(hostsContent) {
80
80
  return kept.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
81
81
  }
82
82
  export function buildManagedSection(domains) {
83
- if (domains.length === 0) {
83
+ const logicalSites = dedupeLogicalSites(domains);
84
+ if (logicalSites.length === 0) {
84
85
  return "";
85
86
  }
86
87
  const lines = [
87
88
  "",
88
89
  `${MARKER} BEGIN`,
89
90
  "# Managed by LockIn MCP — do not edit manually",
91
+ "# Each site lists apex + www (IPv4 + IPv6) so redirects cannot bypass the block.",
90
92
  ];
91
- const uniqueHosts = new Set();
92
- for (const domain of domains) {
93
- for (const host of expandDomainVariants(domain)) {
94
- uniqueHosts.add(host);
93
+ for (const site of logicalSites) {
94
+ const hostnames = [...new Set(expandDomainVariants(site))].sort();
95
+ lines.push(`# ${site}`);
96
+ for (const host of hostnames) {
97
+ lines.push(`${BLOCK_IP} ${host}`);
98
+ lines.push(`${BLOCK_IP_V6} ${host}`);
95
99
  }
96
100
  }
97
- for (const host of [...uniqueHosts].sort()) {
98
- lines.push(`${BLOCK_IP} ${host}`);
99
- lines.push(`${BLOCK_IP_V6} ${host}`);
100
- }
101
101
  lines.push(`${MARKER} END`);
102
102
  return lines.join("\n");
103
103
  }
104
+ function dedupeLogicalSites(domains) {
105
+ const seen = new Set();
106
+ const result = [];
107
+ for (const domain of domains) {
108
+ const normalized = normalizeDomain(domain);
109
+ if (!normalized || seen.has(normalized)) {
110
+ continue;
111
+ }
112
+ seen.add(normalized);
113
+ result.push(normalized);
114
+ }
115
+ return result.sort();
116
+ }
104
117
  export function composeHostsFile(existingContent, blockedDomains) {
105
118
  const base = stripManagedEntries(existingContent);
106
119
  const managed = buildManagedSection(blockedDomains);