@nebulr-group/bridge-cli 0.6.5 → 1.0.0-alpha.0

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 (293) hide show
  1. package/README.md +93 -460
  2. package/dist/api/accounts.d.ts +69 -0
  3. package/dist/api/accounts.d.ts.map +1 -0
  4. package/dist/api/accounts.js +42 -0
  5. package/dist/api/accounts.js.map +1 -0
  6. package/dist/api/apps.d.ts +50 -0
  7. package/dist/api/apps.d.ts.map +1 -0
  8. package/dist/api/apps.js +62 -0
  9. package/dist/api/apps.js.map +1 -0
  10. package/dist/api/billing.d.ts +131 -0
  11. package/dist/api/billing.d.ts.map +1 -0
  12. package/dist/api/billing.js +42 -0
  13. package/dist/api/billing.js.map +1 -0
  14. package/dist/api/flags.d.ts +73 -0
  15. package/dist/api/flags.d.ts.map +1 -0
  16. package/dist/api/flags.js +108 -0
  17. package/dist/api/flags.js.map +1 -0
  18. package/dist/api/keys.d.ts +48 -0
  19. package/dist/api/keys.d.ts.map +1 -0
  20. package/dist/api/keys.js +87 -0
  21. package/dist/api/keys.js.map +1 -0
  22. package/dist/auth/browser-open.d.ts +21 -4
  23. package/dist/auth/browser-open.d.ts.map +1 -1
  24. package/dist/auth/browser-open.js +45 -65
  25. package/dist/auth/browser-open.js.map +1 -1
  26. package/dist/auth/lock.d.ts +8 -0
  27. package/dist/auth/lock.d.ts.map +1 -0
  28. package/dist/auth/lock.js +43 -0
  29. package/dist/auth/lock.js.map +1 -0
  30. package/dist/auth/loopback.d.ts +8 -12
  31. package/dist/auth/loopback.d.ts.map +1 -1
  32. package/dist/auth/loopback.js +40 -45
  33. package/dist/auth/loopback.js.map +1 -1
  34. package/dist/auth/session.d.ts +61 -0
  35. package/dist/auth/session.d.ts.map +1 -0
  36. package/dist/auth/session.js +142 -0
  37. package/dist/auth/session.js.map +1 -0
  38. package/dist/auth/sign-in.d.ts +67 -0
  39. package/dist/auth/sign-in.d.ts.map +1 -0
  40. package/dist/auth/sign-in.js +113 -0
  41. package/dist/auth/sign-in.js.map +1 -0
  42. package/dist/auth/store.d.ts +89 -0
  43. package/dist/auth/store.d.ts.map +1 -0
  44. package/dist/auth/store.js +214 -0
  45. package/dist/auth/store.js.map +1 -0
  46. package/dist/bin.js +0 -2
  47. package/dist/bin.js.map +1 -1
  48. package/dist/cli.d.ts +1 -2
  49. package/dist/cli.d.ts.map +1 -1
  50. package/dist/cli.js +13 -17
  51. package/dist/cli.js.map +1 -1
  52. package/dist/client.d.ts +31 -0
  53. package/dist/client.d.ts.map +1 -0
  54. package/dist/client.js +67 -0
  55. package/dist/client.js.map +1 -0
  56. package/dist/commands/accounts.command.d.ts +4 -0
  57. package/dist/commands/accounts.command.d.ts.map +1 -0
  58. package/dist/commands/accounts.command.js +116 -0
  59. package/dist/commands/accounts.command.js.map +1 -0
  60. package/dist/commands/app.command.d.ts +3 -2
  61. package/dist/commands/app.command.d.ts.map +1 -1
  62. package/dist/commands/app.command.js +132 -233
  63. package/dist/commands/app.command.js.map +1 -1
  64. package/dist/commands/auth.command.d.ts +2 -12
  65. package/dist/commands/auth.command.d.ts.map +1 -1
  66. package/dist/commands/auth.command.js +147 -20
  67. package/dist/commands/auth.command.js.map +1 -1
  68. package/dist/commands/billing.command.d.ts +4 -0
  69. package/dist/commands/billing.command.d.ts.map +1 -0
  70. package/dist/commands/billing.command.js +185 -0
  71. package/dist/commands/billing.command.js.map +1 -0
  72. package/dist/commands/check.command.d.ts +2 -1
  73. package/dist/commands/check.command.d.ts.map +1 -1
  74. package/dist/commands/check.command.js +11 -14
  75. package/dist/commands/check.command.js.map +1 -1
  76. package/dist/commands/context.d.ts +58 -0
  77. package/dist/commands/context.d.ts.map +1 -0
  78. package/dist/commands/context.js +78 -0
  79. package/dist/commands/context.js.map +1 -0
  80. package/dist/commands/flag.command.d.ts +3 -62
  81. package/dist/commands/flag.command.d.ts.map +1 -1
  82. package/dist/commands/flag.command.js +181 -864
  83. package/dist/commands/flag.command.js.map +1 -1
  84. package/dist/commands/key.command.d.ts +4 -0
  85. package/dist/commands/key.command.d.ts.map +1 -0
  86. package/dist/commands/key.command.js +44 -0
  87. package/dist/commands/key.command.js.map +1 -0
  88. package/dist/commands/plan.command.d.ts +3 -203
  89. package/dist/commands/plan.command.d.ts.map +1 -1
  90. package/dist/commands/plan.command.js +200 -759
  91. package/dist/commands/plan.command.js.map +1 -1
  92. package/dist/config.d.ts +19 -101
  93. package/dist/config.d.ts.map +1 -1
  94. package/dist/config.js +51 -364
  95. package/dist/config.js.map +1 -1
  96. package/dist/core/discovery.d.ts +27 -0
  97. package/dist/core/discovery.d.ts.map +1 -0
  98. package/dist/core/discovery.js +97 -0
  99. package/dist/core/discovery.js.map +1 -0
  100. package/dist/core/errors.d.ts +26 -0
  101. package/dist/core/errors.d.ts.map +1 -0
  102. package/dist/core/errors.js +33 -0
  103. package/dist/core/errors.js.map +1 -0
  104. package/dist/core/http.d.ts +23 -0
  105. package/dist/core/http.d.ts.map +1 -0
  106. package/dist/core/http.js +153 -0
  107. package/dist/core/http.js.map +1 -0
  108. package/dist/core/private-files.d.ts +6 -0
  109. package/dist/core/private-files.d.ts.map +1 -0
  110. package/dist/core/private-files.js +96 -0
  111. package/dist/core/private-files.js.map +1 -0
  112. package/dist/core/routes.d.ts +94 -0
  113. package/dist/core/routes.d.ts.map +1 -0
  114. package/dist/core/routes.js +91 -0
  115. package/dist/core/routes.js.map +1 -0
  116. package/dist/core/urls.d.ts +15 -0
  117. package/dist/core/urls.d.ts.map +1 -0
  118. package/dist/core/urls.js +55 -0
  119. package/dist/core/urls.js.map +1 -0
  120. package/dist/gate-rules.d.ts +1 -2
  121. package/dist/gate-rules.d.ts.map +1 -1
  122. package/dist/gate-rules.js +4 -5
  123. package/dist/gate-rules.js.map +1 -1
  124. package/dist/index.d.ts +23 -3
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +18 -3
  127. package/dist/index.js.map +1 -1
  128. package/dist/io.d.ts +21 -0
  129. package/dist/io.d.ts.map +1 -0
  130. package/dist/io.js +59 -0
  131. package/dist/io.js.map +1 -0
  132. package/dist/money.d.ts +42 -0
  133. package/dist/money.d.ts.map +1 -0
  134. package/dist/money.js +112 -0
  135. package/dist/money.js.map +1 -0
  136. package/dist/program.d.ts +5 -24
  137. package/dist/program.d.ts.map +1 -1
  138. package/dist/program.js +25 -113
  139. package/dist/program.js.map +1 -1
  140. package/package.json +7 -8
  141. package/dist/auth/api-client.d.ts +0 -50
  142. package/dist/auth/api-client.d.ts.map +0 -1
  143. package/dist/auth/api-client.js +0 -109
  144. package/dist/auth/api-client.js.map +0 -1
  145. package/dist/auth/relative-time.d.ts +0 -2
  146. package/dist/auth/relative-time.d.ts.map +0 -1
  147. package/dist/auth/relative-time.js +0 -27
  148. package/dist/auth/relative-time.js.map +0 -1
  149. package/dist/auth/scopes.d.ts +0 -30
  150. package/dist/auth/scopes.d.ts.map +0 -1
  151. package/dist/auth/scopes.js +0 -39
  152. package/dist/auth/scopes.js.map +0 -1
  153. package/dist/auth/workspace-client.d.ts +0 -70
  154. package/dist/auth/workspace-client.d.ts.map +0 -1
  155. package/dist/auth/workspace-client.js +0 -105
  156. package/dist/auth/workspace-client.js.map +0 -1
  157. package/dist/commands/auth/config.command.d.ts +0 -8
  158. package/dist/commands/auth/config.command.d.ts.map +0 -1
  159. package/dist/commands/auth/config.command.js +0 -60
  160. package/dist/commands/auth/config.command.js.map +0 -1
  161. package/dist/commands/auth/login.command.d.ts +0 -46
  162. package/dist/commands/auth/login.command.d.ts.map +0 -1
  163. package/dist/commands/auth/login.command.js +0 -166
  164. package/dist/commands/auth/login.command.js.map +0 -1
  165. package/dist/commands/auth/logout.command.d.ts +0 -21
  166. package/dist/commands/auth/logout.command.d.ts.map +0 -1
  167. package/dist/commands/auth/logout.command.js +0 -97
  168. package/dist/commands/auth/logout.command.js.map +0 -1
  169. package/dist/commands/auth/methods.command.d.ts +0 -68
  170. package/dist/commands/auth/methods.command.d.ts.map +0 -1
  171. package/dist/commands/auth/methods.command.js +0 -91
  172. package/dist/commands/auth/methods.command.js.map +0 -1
  173. package/dist/commands/auth/status.command.d.ts +0 -18
  174. package/dist/commands/auth/status.command.d.ts.map +0 -1
  175. package/dist/commands/auth/status.command.js +0 -104
  176. package/dist/commands/auth/status.command.js.map +0 -1
  177. package/dist/commands/auth/use.command.d.ts +0 -17
  178. package/dist/commands/auth/use.command.d.ts.map +0 -1
  179. package/dist/commands/auth/use.command.js +0 -47
  180. package/dist/commands/auth/use.command.js.map +0 -1
  181. package/dist/commands/branding.command.d.ts +0 -3
  182. package/dist/commands/branding.command.d.ts.map +0 -1
  183. package/dist/commands/branding.command.js +0 -42
  184. package/dist/commands/branding.command.js.map +0 -1
  185. package/dist/commands/event.command.d.ts +0 -3
  186. package/dist/commands/event.command.d.ts.map +0 -1
  187. package/dist/commands/event.command.js +0 -27
  188. package/dist/commands/event.command.js.map +0 -1
  189. package/dist/commands/flag-init.command.d.ts +0 -13
  190. package/dist/commands/flag-init.command.d.ts.map +0 -1
  191. package/dist/commands/flag-init.command.js +0 -463
  192. package/dist/commands/flag-init.command.js.map +0 -1
  193. package/dist/commands/guide.command.d.ts +0 -61
  194. package/dist/commands/guide.command.d.ts.map +0 -1
  195. package/dist/commands/guide.command.js +0 -655
  196. package/dist/commands/guide.command.js.map +0 -1
  197. package/dist/commands/info.command.d.ts +0 -3
  198. package/dist/commands/info.command.d.ts.map +0 -1
  199. package/dist/commands/info.command.js +0 -70
  200. package/dist/commands/info.command.js.map +0 -1
  201. package/dist/commands/integration.command.d.ts +0 -35
  202. package/dist/commands/integration.command.d.ts.map +0 -1
  203. package/dist/commands/integration.command.js +0 -290
  204. package/dist/commands/integration.command.js.map +0 -1
  205. package/dist/commands/journeys.d.ts +0 -43
  206. package/dist/commands/journeys.d.ts.map +0 -1
  207. package/dist/commands/journeys.js +0 -221
  208. package/dist/commands/journeys.js.map +0 -1
  209. package/dist/commands/ops.command.d.ts +0 -17
  210. package/dist/commands/ops.command.d.ts.map +0 -1
  211. package/dist/commands/ops.command.js +0 -129
  212. package/dist/commands/ops.command.js.map +0 -1
  213. package/dist/commands/privilege.command.d.ts +0 -16
  214. package/dist/commands/privilege.command.d.ts.map +0 -1
  215. package/dist/commands/privilege.command.js +0 -86
  216. package/dist/commands/privilege.command.js.map +0 -1
  217. package/dist/commands/role.command.d.ts +0 -3
  218. package/dist/commands/role.command.d.ts.map +0 -1
  219. package/dist/commands/role.command.js +0 -101
  220. package/dist/commands/role.command.js.map +0 -1
  221. package/dist/commands/runtime-dir.d.ts +0 -16
  222. package/dist/commands/runtime-dir.d.ts.map +0 -1
  223. package/dist/commands/runtime-dir.js +0 -18
  224. package/dist/commands/runtime-dir.js.map +0 -1
  225. package/dist/commands/setup.command.d.ts +0 -63
  226. package/dist/commands/setup.command.d.ts.map +0 -1
  227. package/dist/commands/setup.command.js +0 -148
  228. package/dist/commands/setup.command.js.map +0 -1
  229. package/dist/commands/stripe.command.d.ts +0 -29
  230. package/dist/commands/stripe.command.d.ts.map +0 -1
  231. package/dist/commands/stripe.command.js +0 -99
  232. package/dist/commands/stripe.command.js.map +0 -1
  233. package/dist/commands/tenant.command.d.ts +0 -3
  234. package/dist/commands/tenant.command.d.ts.map +0 -1
  235. package/dist/commands/tenant.command.js +0 -135
  236. package/dist/commands/tenant.command.js.map +0 -1
  237. package/dist/commands/token.command.d.ts +0 -3
  238. package/dist/commands/token.command.d.ts.map +0 -1
  239. package/dist/commands/token.command.js +0 -54
  240. package/dist/commands/token.command.js.map +0 -1
  241. package/dist/commands/user.command.d.ts +0 -3
  242. package/dist/commands/user.command.d.ts.map +0 -1
  243. package/dist/commands/user.command.js +0 -117
  244. package/dist/commands/user.command.js.map +0 -1
  245. package/dist/credentials.d.ts +0 -141
  246. package/dist/credentials.d.ts.map +0 -1
  247. package/dist/credentials.js +0 -348
  248. package/dist/credentials.js.map +0 -1
  249. package/dist/flag-coherence.d.ts +0 -42
  250. package/dist/flag-coherence.d.ts.map +0 -1
  251. package/dist/flag-coherence.js +0 -103
  252. package/dist/flag-coherence.js.map +0 -1
  253. package/dist/output.d.ts +0 -17
  254. package/dist/output.d.ts.map +0 -1
  255. package/dist/output.js +0 -107
  256. package/dist/output.js.map +0 -1
  257. package/dist/preview.d.ts +0 -9
  258. package/dist/preview.d.ts.map +0 -1
  259. package/dist/preview.js +0 -63
  260. package/dist/preview.js.map +0 -1
  261. package/dist/product-decisions.d.ts +0 -40
  262. package/dist/product-decisions.d.ts.map +0 -1
  263. package/dist/product-decisions.js +0 -52
  264. package/dist/product-decisions.js.map +0 -1
  265. package/dist/prompt-urls.d.ts +0 -31
  266. package/dist/prompt-urls.d.ts.map +0 -1
  267. package/dist/prompt-urls.js +0 -35
  268. package/dist/prompt-urls.js.map +0 -1
  269. package/dist/prompts/auth-master-integration-prompt.md +0 -255
  270. package/dist/prompts/billing/master.md +0 -332
  271. package/dist/prompts/decisions/feature-control.md +0 -131
  272. package/dist/prompts/decisions/going-live.md +0 -60
  273. package/dist/prompts/decisions/login.md +0 -71
  274. package/dist/prompts/decisions/look-and-feel.md +0 -68
  275. package/dist/prompts/decisions/payments.md +0 -97
  276. package/dist/prompts/decisions/roles.md +0 -78
  277. package/dist/prompts/decisions/teams.md +0 -61
  278. package/dist/prompts/fit-together.md +0 -97
  279. package/dist/prompts/flags/master.md +0 -220
  280. package/dist/prompts/guide-coverage.json +0 -17
  281. package/dist/prompts/integration-success.md +0 -106
  282. package/dist/prompts/mcp-tools.json +0 -90
  283. package/dist/prompts/mechanisms.md +0 -298
  284. package/dist/prompts/orientation.md +0 -42
  285. package/dist/prompts/product-decisions.json +0 -338
  286. package/dist/resolve.d.ts +0 -110
  287. package/dist/resolve.d.ts.map +0 -1
  288. package/dist/resolve.js +0 -260
  289. package/dist/resolve.js.map +0 -1
  290. package/dist/shape.d.ts +0 -25
  291. package/dist/shape.d.ts.map +0 -1
  292. package/dist/shape.js +0 -74
  293. package/dist/shape.js.map +0 -1
@@ -1,255 +0,0 @@
1
- # Bridge Auth — Master Integration Prompt
2
-
3
- You are integrating **Bridge Authentication** (auth, tenant management, RBAC) into a user's application. Your job is to detect the project structure, identify the technologies, and apply the right per-framework integration for each.
4
-
5
- > **The one rule for app code: every gate is a flag.** A link, a page, a button, an endpoint: if some people get it and others do not, the code asks a flag, and the flag's rule says why (a privilege, a plan feature, a rollout). App code never reads a role, a privilege list, the plan or a plan feature to decide what someone may see or do. Numbers are plan limits, and permission on one specific record stays in app code. `bridge guide fit-together` has the whole rule; the last verification step runs `npx @nebulr-group/bridge-cli check gates`.
6
-
7
- This prompt is framework-agnostic. It orchestrates discovery, confirmation, and verification — the actual install commands, file shapes, and code snippets live in the per-framework guides fetched in Step 4.
8
-
9
- > **Related master prompts** — if the user asked for flags or billing specifically (not auth), stop here and route them:
10
- > - Feature Flags 2.0 only → `bridge guide flags`
11
- > - Billing 2.0 only → `bridge guide billing`
12
- >
13
- > If they want auth + flags + billing wired together, run this prompt first, then chain `bridge guide flags` and `bridge guide billing` at the end (or whichever subset they want).
14
-
15
- ## Step 0 — Authenticate
16
-
17
- Run `bridge auth login` and wait for it to print "Logged in as <email>". Once it exits, proceed to Step 1.
18
-
19
- **Connected through the Bridge MCP server instead?** Skip the login: the connection is already signed in to one app. Every CLI step below has a tool; use the tool and do not translate by hand.
20
-
21
- | Operation | MCP | CLI |
22
- |---|---|---|
23
- | What Bridge does, to relay to the developer | `get_started` | `bridge guide orientation` |
24
- | Read the app: id, URLs, redirect URIs (Step 3) | `get_app` | `bridge app get` |
25
- | Read the login methods (Step 3) | `get_auth_config` | `bridge info auth-config` |
26
- | Set the frontend URL, callback and allowed origins (Step 3b) | `update_app` (`uiUrl`, `defaultCallbackUri`, `allowedOrigins`); `allowedOrigins` replaces the whole list, so send every origin | `bridge app update --ui-url … --default-callback-uri … --allowed-origins …` |
27
- | Add one redirect URI, keeping the others (Step 3b) | `add_redirect_uri` | `bridge app redirect-uris add <url>` |
28
- | This prompt | `get_integration_guide` (topic `master`) | `bridge guide` |
29
- | A per-framework guide (Step 4) | `get_integration_guide` (topic `integration`, then `auth`) with the framework | `bridge guide <framework>`, then `bridge guide <framework> sdk-auth` |
30
- | Check the project's env vars against the app (Step 6) | `diagnose_integration` | `bridge diagnose` |
31
- | Why the developer's own sign-in failed (Step 6) | `debug_auth_issue` with their email | `bridge event auth-attempts --user <email>` |
32
- | Roles and flags for route protection (Step 5) | `list_roles`, `list_feature_flags` | `bridge role list`, `bridge flag list` |
33
-
34
- ---
35
-
36
- **At the end of this integration you will output a success message that looks like this:**
37
-
38
- ```
39
- ██████╗ ██████╗ ██╗██████╗ ██████╗ ███████╗
40
- ██╔══██╗██╔══██╗██║██╔══██╗██╔════╝ ██╔════╝
41
- ██████╔╝██████╔╝██║██║ ██║██║ ███╗█████╗
42
- ██╔══██╗██╔══██╗██║██║ ██║██║ ██║██╔══╝
43
- ██████╔╝██║ ██║██║██████╔╝╚██████╔╝███████╗
44
- ╚═════╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═════╝ ╚══════╝
45
-
46
- ──────────────────────────────────────────────
47
- Congratulations — [project-name] now has superpowers.
48
- ...
49
- ✅ You know who's calling
50
- [token-description]
51
- ...
52
- ──────────────────────────────────────────────
53
- Here is what I actually did:
54
- [what-i-actually-did]
55
-
56
- And here is what I changed:
57
- [what-i-changed]
58
- ──────────────────────────────────────────────
59
- ```
60
-
61
- As you work through the steps below, track the following so you have it ready:
62
- - **`[project-name]`** — folder name and/or `package.json` name for each project integrated
63
- - **`[token-description]`** — the identifiers the developer uses to access the authenticated user in their framework (e.g. stores for Svelte, hooks for React, decorators for NestJS)
64
- - **`[what-i-actually-did]`** — high-level outcomes: what works now that didn't before
65
- - **`[what-i-changed]`** — every package installed, file created or modified, and URL configured, with exact values
66
-
67
- ## Step 1 — Discover projects
68
-
69
- Scan the current directory and its immediate subdirectories for `package.json` files. For each one:
70
-
71
- 1. **Detect the package manager** — check for lock files in this order:
72
- - `bun.lock` or `bun.lockb` → use `bun`
73
- - `pnpm-lock.yaml` → use `pnpm`
74
- - `yarn.lock` → use `yarn`
75
- - `package-lock.json` or none → use `npm`
76
-
77
- 2. **Detect frontend framework** (from `dependencies` + `devDependencies`):
78
- - `svelte` or `@sveltejs/kit` → **SvelteKit**
79
- - `react` + `next` → **Next.js**
80
- - `react` (without next) → **React**
81
- - `@angular/core` → **Angular**
82
-
83
- 3. **Detect backend framework** (from `dependencies`):
84
- - `@nestjs/core` → **NestJS**
85
- - `express` (without @nestjs/core) → **Express**
86
-
87
- 4. **Detect existing auth** (from `dependencies` + `devDependencies`):
88
- - `@nebulr/nblocks-svelte`, `@nebulr/nblocks-react`, etc. → **nblocks (predecessor to Bridge — migration needed)**
89
- - `@nebulr-group/bridge-svelte`, `@nebulr-group/bridge-react`, `@nebulr-group/bridge-nestjs`, etc. → **Bridge already installed — check if wiring is complete (see Step 1b)**
90
- - `next-auth`, `@auth0/*`, `@clerk/*`, `lucia`, `passport` → **third-party auth present — warn user**
91
-
92
- 5. **Record for each project:**
93
- - Path (relative to working directory)
94
- - Type: `frontend` or `backend`
95
- - Framework + version
96
- - Package manager
97
- - Existing auth (if any)
98
-
99
- ## Step 1b — Check if Bridge is already integrated
100
-
101
- If Step 1 detected a Bridge plugin (`@nebulr-group/bridge-<framework>`) in `dependencies`, the integration may already be partially or fully complete. Don't guess from the dependency alone — audit the wiring.
102
-
103
- **Delegate the audit to the per-framework guide.** Each plugin's guide ships an "Integration audit" section that lists the exact files / providers / route shapes the framework requires. Run:
104
-
105
- ```
106
- bridge guide <framework> # integration audit checklist
107
- bridge guide <framework> sdk-auth # the sign-in pages and their design
108
- ```
109
-
110
- The framework guide tells you precisely what must exist (bootstrap call, provider wrapping, the auth pages, environment variables). Apply that checklist to the project on disk.
111
-
112
- **General decision matrix** (independent of framework):
113
-
114
- - **Wiring complete AND the auth pages are served** → Bridge is fully integrated. Skip to **Step 6b** and output the success message.
115
- - **Bootstrap wiring complete BUT the auth pages are missing** → Bridge is partially integrated. Without them signup verification, password reset and the SSO callback land on a 404. On SvelteKit the fix is one file, `src/routes/auth/[...bridge]/+page.svelte` rendering `<BridgeAuthRoutes />`; hand-written pages that only render a Bridge component can then be deleted. React: one `/auth/*` route rendering `<BridgeAuthRoutes />`. Next.js: `app/auth/[...bridge]/page.tsx` rendering `<BridgeAuthRoutes />`. Angular: `...bridgeAuthRoutes()` spread into `app.routes.ts`. Add only what's missing.
116
- - **Bootstrap wiring incomplete** (regardless of route state) → Offer to complete the initial setup (proceed to Step 4).
117
- - **Bridge is NOT installed** → Continue with Steps 2–6 for fresh setup.
118
-
119
- When you find missing pieces, list each one explicitly to the user — they need to know what's broken and why (e.g., "the signup verification email route is missing — every new signup is currently hitting a 404 after clicking the verification link").
120
-
121
- ## Step 2 — Tell the developer the plan, ask only what you cannot detect
122
-
123
- **Talk to the developer in plain words, once, briefly.** Say what you are about to do. Ask only questions whose answer you could not find in the project. Do not recite what you detected (framework versions, package manager, the Bridge app's settings, enabled sign-in methods), show commands, or name configuration fields. That detail is for you; the developer did not ask for it.
124
-
125
- - **One project:** integrate it. Do not ask which.
126
- - **Several projects:** ask once which to integrate ("I found a SvelteKit app in `./web` and a NestJS API in `./api` — set up both?").
127
- - **Existing auth** (nblocks or a third-party library): say so in one line and what will happen to it. This is the one thing about the current state worth mentioning.
128
-
129
- **Sign-in (frontend projects only).** Sign-in happens inside the app (SDK mode): the plugin serves the login, signup and password pages on the app's own routes, inside its own layout, styled to match the app's existing design (Step 4). There is nothing to choose. (If the developer explicitly asks for Bridge's hosted sign-in page, or the app already uses it, respect that.)
130
-
131
- Example — the whole message:
132
-
133
- ```
134
- I'll add sign-in to QR Studio: sign-in and sign-up pages inside the app, styled to
135
- match it. Every page will need sign-in except those. I'll connect your Bridge app to
136
- http://localhost:5180.
137
-
138
- One question: should sign-in also work when you open the app over Tailscale? If so,
139
- paste the full https://….ts.net:8445 address.
140
- ```
141
-
142
- With no question left to ask, say what you are doing and carry on; do not wait for a "yes".
143
-
144
- ## Step 3 — Read the Bridge app (for you, not the developer)
145
-
146
- ```bash
147
- bridge app get
148
- bridge info auth-config
149
- ```
150
-
151
- Note the `appId` (frontend and backend both need it), the enabled sign-in methods and the app's URLs. Use them to decide what to change; do not report them. If the CLI is not available or not configured, ask the developer for the `appId`; it is in the Bridge dashboard.
152
-
153
- ## Step 3b — Point the Bridge app at the frontend
154
-
155
- Take the frontend URL from the project: a port fixed in `vite.config.*`, `--port` in a dev script, the framework default otherwise. Ask only when it is genuinely unclear, or when the project also serves the app on another address (a tunnel, a LAN or Tailscale host, a second dev script) — then ask whether sign-in should work there too, and add each address they confirm.
156
-
157
- Then update the app. Change only what is missing; read the current values first, and pass the full list back for `--allowed-origins`, which replaces it:
158
-
159
- ```bash
160
- bridge app update \
161
- --ui-url <frontend-url> \
162
- --default-callback-uri <frontend-url>/auth/oauth-callback \
163
- --redirect-uris <frontend-url>/auth/oauth-callback \
164
- --allowed-origins <frontend-url>
165
- ```
166
-
167
- Why each matters (for you): without the callback in the redirect URIs Bridge rejects the sign-in redirect; without the origin it blocks the browser's requests. `--ui-url` is where links in Bridge's emails point when your backend sends them; unset, people land on a Bridge page instead of your app, and it must also be one of the allowed origins.
168
-
169
- Tell the developer at most one line about this ("Connected your Bridge app to http://localhost:5180"), never the field names.
170
-
171
- ## Step 4 — Fetch and apply per-framework guides
172
-
173
- For each confirmed project, fetch the framework-specific integration prompt.
174
-
175
- **Frontend projects** — the setup, then the sign-in pages:
176
-
177
- | Framework | Setup | Sign-in pages and their design |
178
- |-----------|-------|--------------------------------|
179
- | SvelteKit | `bridge guide svelte` | `bridge guide svelte sdk-auth` |
180
- | React | `bridge guide react` | `bridge guide react sdk-auth` |
181
- | Next.js | `bridge guide nextjs` | `bridge guide nextjs sdk-auth` |
182
- | Angular | `bridge guide angular` | `bridge guide angular sdk-auth` |
183
-
184
- **Match the app's design.** The sign-in pages render inside the app, so they must look like the rest of it, not like Bridge's defaults. Before you finish, read the app's existing styles (global CSS, CSS variables, Tailwind or theme config, the font it loads, an existing button and input) and set the `--bridge-*` design tokens from them; if the app wraps its pages in a card or shell, give the sign-in pages the same frame. The per-framework guide's *Match the app's design* section has the mapping. Ask for colours only when the project has no styles of its own.
185
-
186
- **Backend projects:**
187
-
188
- | Framework | Command |
189
- |-----------|---------|
190
- | NestJS | `bridge guide nestjs` |
191
- | Express | `bridge guide express` |
192
-
193
- Follow the per-framework guide instructions verbatim. Pass these values from Step 3:
194
- - `appId` — same for all projects; it goes in the project's `.env` (`VITE_BRIDGE_APP_ID` on SvelteKit and React, `NEXT_PUBLIC_BRIDGE_APP_ID` on Next.js, `BRIDGE_APP_ID` on NestJS and Express), which the plugin reads itself. Angular has no env convention: `appId` goes in `provideBridge({ appId })` from `environment.ts`. On every plugin an option passed explicitly wins over the environment
195
- - `packageManager` — detected in Step 1 (use it for all install commands)
196
-
197
- **What the handover looks like.** The guides are short because the plugins serve their own pages. On SvelteKit the whole frontend integration is the `.env` line, `src/routes/+layout.ts` (`export const load = bridgeBootstrap({ rules })`), `src/routes/+layout.svelte` (`<BridgeBootstrap>`) and one auth file, `src/routes/auth/[...bridge]/+page.svelte` — about fifteen lines. React, Next.js and Angular follow the same shape: the provider (`<BridgeProvider>` / `provideBridge()`), one auth route (`<BridgeAuthRoutes>` / `...bridgeAuthRoutes()`) and, with plans, one subscription route on `/subscription` (`<BridgeBillingRoutes>` / `...bridgeBillingRoutes()`). On NestJS it is `BridgeModule.forRoot({ guard: { global: true } })` in `AppModule` and the `.env` line; on Express, `createBridge()` and `app.use(bridge.auth())`. Do not write login, signup, callback or password pages by hand, and no fetch helper for tokens. `bridge guide mechanisms` explains the levels and customisation rungs if the developer asks for more.
198
-
199
- **Order:** Frontend first, then backend. This lets you verify login works before adding backend guards.
200
-
201
- ## Step 5 — Route protection defaults
202
-
203
- When setting up route protection, apply these sensible defaults:
204
-
205
- **Frontend:**
206
- - `defaultAccess: 'protected'` — everything requires login by default
207
- - Mark as public: auth routes only (`/auth/*`) — the OAuth callback must be accessible
208
- - Do NOT make other routes public by default. The user can relax this later for specific pages
209
-
210
- **Backend:**
211
- - `guard.global: true` with `defaultAccess: 'protected'`
212
- - Mark as public: health check endpoints, public read-only APIs
213
- - Use the framework's current-user accessor on endpoints that need user identity (the per-framework guide names it: `@CurrentUser()` for NestJS, `req.user` for Express, etc.)
214
-
215
- Tell the user: "I've set up default route protection. You can refine which routes are public or protected using the Bridge CLI (`bridge role list`, `bridge flag list`) or by editing the route config directly."
216
-
217
- ## Step 6 — Verify
218
-
219
- For each integrated project:
220
-
221
- 1. **Build check** — run the project's build command (from `package.json` scripts)
222
- 2. **Config check** — confirm the `appId` environment variable is set
223
- 3. **Route check** — confirm protected routes have guards and public routes are accessible
224
- 4. **Sign-in check** — ask the developer to sign up and sign in themselves in the running app: sign up at `/auth/signup`, open the verification email, set a password, sign in, sign out. Do not create test users for them. If a step fails, `bridge event auth-attempts --user <their email>` (MCP: `debug_auth_issue`) names the reason and the fix.
225
- 5. **Gate check** — run `npx @nebulr-group/bridge-cli check gates` in each project. Every gate in app code is a flag: it lists each direct role, privilege, plan or plan-feature check with the flag to use instead. Fix every one and run it again until it is clean. Only a check the developer explicitly asked to keep stays, marked `// bridge-gate-exception: <reason>`
226
-
227
- If anything fails, including the gate check, diagnose and fix before moving on. Once the checks pass, move on to the summary in Step 6b — do NOT just dump a list of changed files at the user.
228
-
229
- ## Step 6b — Tell the developer what they just got
230
-
231
- Fetch the success message template (MCP: `get_integration_guide` topic `integration-success`; CLI: `bridge guide integration-success`), then output it personalised for this project.
232
-
233
- **Substitutions (always apply):**
234
-
235
- - `[project-name]` → the `name` field from the project's `package.json`
236
- - `[bridge-plugin]` → the installed Bridge plugin (e.g. `bridge-svelte`, `bridge-react`, `bridge-nestjs`)
237
- - `[dev-url]` → the frontend dev server URL detected in Step 3b (e.g. `http://localhost:5173`)
238
-
239
- **Personalisation rules:**
240
-
241
- - Fill in every `[placeholder]` using the substitution guide at the bottom of the template. For `[what-i-actually-did]` and `[what-i-changed]`, draw from what you actually observed and changed during Steps 1–6 — the examples in the guide are illustrative only. Strip the `AGENT SUBSTITUTION GUIDE` section before outputting — it must never be shown to the developer.
242
- - If both a frontend and a backend were integrated, group the bottom two sections by project rather than merging them into a flat list.
243
- - **DO NOT write any text before the banner.** The very first character of your response must be the first character of the ASCII art. No "Perfect!", no "Here's your success message:", no transition sentence, no acknowledgement — nothing. Start with the `█` character. If you find yourself typing an intro, stop and delete it.
244
- - **If the build is broken** or auth doesn't actually work end-to-end, prepend a single "Heads up:" line before the banner. Never bury bad news under it.
245
-
246
- After delivering the message, the integration is complete.
247
-
248
- ## Step 7 — Offer follow-on tracks
249
-
250
- After auth is wired and the success banner is printed, the developer commonly wants flags and billing next. Mention these one-liners so they know how to continue:
251
-
252
- - **Feature Flags 2.0** → `bridge guide flags` (auto-detects framework, sets up flag evaluation, telemetry, realtime)
253
- - **Billing 2.0** → `bridge guide billing` (plans, the subscription pages, the paywall, plan limits enforced by the backend)
254
-
255
- Do not run them automatically — the developer decides when they want each track.
@@ -1,332 +0,0 @@
1
- # Billing 2.0 — Master Integration Prompt
2
-
3
- You are integrating **Bridge Billing** into a user's application — plan selection, Stripe Checkout, subscription state, lifecycle notices (trial / dunning / cancel), and usage quota counters.
4
-
5
- > **The one rule for app code: every gate is a flag.** A link, a page, a button, an endpoint: if some people get it and others do not, the code asks a flag, and the flag's rule says why (a privilege, a plan feature, a rollout). App code never reads a role, a privilege list, the plan or a plan feature to decide what someone may see or do. Numbers are plan limits, and permission on one specific record stays in app code. `bridge guide fit-together` has the whole rule; the last verification step runs `npx @nebulr-group/bridge-cli check gates`.
6
-
7
- This prompt is framework-agnostic. It orchestrates discovery, pricing model setup, and verification — the actual install commands and code snippets live in the per-framework guides fetched in Step 4.
8
-
9
- > **Related master prompts** — if the user hasn't set up auth yet, stop here and route them:
10
- > - Auth first → `bridge guide`
11
- >
12
- > If they want auth + billing wired together, run `bridge guide` first, then come back here.
13
-
14
- ## Step 0 — Authenticate
15
-
16
- Run `bridge auth login` and wait for it to print "Logged in as <email>". Once it exits, proceed to Step 1.
17
-
18
- **Connected through the Bridge MCP server instead?** Skip the login: the connection is already signed in to one app. Every CLI step below has a tool; use the tool and do not translate by hand.
19
-
20
- | Operation | MCP | CLI |
21
- |---|---|---|
22
- | List plans with prices and quotas | `list_plans` | `bridge plan list` |
23
- | Create a plan, or reshape one in one call | `create_plan`, or `apply_plan` (prices, quotas and trial together) | `bridge plan create` |
24
- | Is Stripe connected and receiving webhooks? | `get_stripe_status` | `bridge stripe status` |
25
- | Connect Stripe and switch payments on | `setup_payments` | `bridge setup payments` |
26
- | Add or change a price | `set_plan_price` | `bridge plan price set` |
27
- | Add or change a quota | `set_plan_quota` (`policy` hard or metered, `kind` counter or gauge) | `bridge plan quota set` |
28
- | List every metric with its kind | `list_plan_quotas` | `bridge plan quota list` |
29
- | Keep plan-less users out of the paywall | `update_app` (`paymentsAutoRedirect: false`) | `bridge app update --payments-auto-redirect false` |
30
- | This prompt | `get_integration_guide` (topic `billing-master`) | `bridge guide billing` |
31
- | A per-framework billing guide (Step 4) | `get_integration_guide` (topic `billing`) with the framework | `bridge guide billing --framework <name>` |
32
- | How limits are enforced end to end | `get_integration_guide` (topic `mechanisms`) | `bridge guide mechanisms` |
33
-
34
- ## Step 1 — Discover projects
35
-
36
- Scan the current directory and its immediate subdirectories for `package.json` files. For each one:
37
-
38
- 1. **Package manager** — from lock file (`bun.lock` → `bun`, `pnpm-lock.yaml` → `pnpm`, `yarn.lock` → `yarn`, `package-lock.json` → `npm`)
39
-
40
- 2. **Framework** — from `dependencies` + `devDependencies`:
41
- - `svelte` or `@sveltejs/kit` → **SvelteKit**
42
- - `react` + `next` → **Next.js**
43
- - `react` (without next) → **React**
44
- - `@angular/core` → **Angular**
45
- - `@nestjs/core` → **NestJS**
46
- - `express` (without `@nestjs/core`) → **Express**
47
-
48
- 3. **Bridge Auth installed?** — look for `@nebulr-group/bridge-<framework>` in dependencies. Billing is scoped to a workspace/tenant and requires Bridge Auth. If not present, stop and run `bridge guide` first.
49
-
50
- 4. **Billing already wired?** — check whether the subscription pages are served (SvelteKit: `src/routes/subscription/[...bridge]/+page.svelte` rendering `<BridgeBillingRoutes />`; React: `<BridgeBillingRoutes />` on `/subscription/*`; Next.js: `app/subscription/[[...bridge]]/page.tsx`; Angular: `...bridgeBillingRoutes()` in `app.routes.ts`) and whether `bridge plan list` returns at least one plan. If billing looks complete, jump to **Step 1b**.
51
-
52
- 5. **Existing billing system?** — look for `stripe`, `@stripe/*`, `paddle-*`, `lemon-squeezy`, `chargebee`. Flag any found.
53
-
54
- ## Step 1b — Audit existing billing wiring
55
-
56
- If Step 1 found billing already partially or fully set up, audit it before doing anything:
57
-
58
- - Run `bridge plan list` — are plans defined?
59
- - Check the subscription pages are served (SvelteKit: the `subscription/[...bridge]` file above; other frameworks: the route the per-framework guide names)
60
- - Check the root layout for `<BridgeBillingNotice />`
61
- - Check every backend handler that creates a limited thing carries the plan-limit check (NestJS: `@RequireQuota`; Express: `bridge.requireQuota`)
62
-
63
- **Decision:**
64
- - **Fully wired** (plans exist, subscription pages served, notice in layout, limits on the backend) → skip to **Step 6** and output the success message
65
- - **Partially wired** → tell the user exactly what's missing and only add what's absent
66
- - **Plans missing** → continue from Step 3 (pricing model)
67
-
68
- ## Step 2 — Present findings and confirm
69
-
70
- Show the user what you found:
71
-
72
- ```
73
- I detected the following projects:
74
-
75
- 1. ./my-app — SvelteKit 5 (frontend, bun)
76
- Bridge Auth: ✅ installed
77
- Billing: not yet wired
78
- Existing billing system: none
79
-
80
- Which projects should I wire billing into? (all / select by number)
81
- ```
82
-
83
- Wait for confirmation before proceeding.
84
-
85
- ## Step 3 — Understand the pricing model
86
-
87
- Ask the developer to describe their pricing in plain language:
88
-
89
- > "Describe your plans — names, prices, and what each plan includes or limits. For example: 'Free plan with 100 AI completions per month. Pro at $29/month with 1000 completions and advanced analytics. 14-day free trial on Pro.'"
90
-
91
- From their answer, map everything to confirmation tables before creating anything.
92
-
93
- **Plans:**
94
-
95
- | key | name | trial |
96
- |-----|------|-------|
97
- | `free` | Free | — |
98
- | `pro` | Pro | 14d |
99
-
100
- **Prices** (a plan can have several — one per currency + interval):
101
-
102
- | plan | amount | currency | interval |
103
- |------|--------|----------|----------|
104
- | `free` | 0 | USD | month |
105
- | `pro` | 29 | USD | month |
106
- | `pro` | 290 | USD | year |
107
-
108
- > **A single plan can carry both a monthly and a yearly price.** When the
109
- > developer gives two intervals for one tier (e.g. "$29/month or $290/year"),
110
- > that is **ONE plan with two prices** — never separate `pro-monthly` /
111
- > `pro-yearly` plans. A free plan gets one price of 0: a plan with no price cannot
112
- > be assigned to a workspace, and Bridge refuses to create one.
113
-
114
- **Quotas** (per-resource limits that differ between plans):
115
-
116
- | plan | metric | limit | policy | kind |
117
- |------|--------|-------|--------|------|
118
- | `free` | `ai_completions` | 100 | hard | counter |
119
- | `pro` | `ai_completions` | 1000 | hard | counter |
120
- | `free` | `projects` | 3 | hard | gauge |
121
-
122
- Use `hard` when overage should be blocked. Use `metered` when overage should bill via Stripe.
123
-
124
- **Counter or gauge?** If deleting it frees room, it's a gauge and your app counts it; if it happened, it's a counter and Bridge counts it. AI completions, exports and API calls are counters (reset each period); projects, tickets and seats are gauges (never reset). Seats are a gauge the app names (e.g. `seats`) with source `membership`: Bridge counts the workspace's active members itself, pending invites included, and the app never reports them. The developer decides the metric list; you decide the kind from that sentence and say why in one line.
125
-
126
- **Entitlements** (features on for some plans, off for others):
127
-
128
- | plan | key | value |
129
- |------|-----|-------|
130
- | `free` | `advanced_analytics` | off |
131
- | `pro` | `advanced_analytics` | on |
132
-
133
- A feature that is on for `pro` goes in `pro`'s features list (`bridge plan feature add pro advanced_analytics --name "Advanced analytics"`) and not in `free`'s. It reaches the app as `bridge:billing.entitlement.advanced_analytics`: the flag that controls the feature uses the rule `bridge:billing.entitlement.advanced_analytics eq true`, so no rule names a plan. A direct check without a flag, `@RequireEntitlement('advanced_analytics')` (never `@RequireQuota`), is the exception. A hard quota with limit 1 that nothing counts still works as an entitlement too; it is the older way.
134
-
135
- If there are no per-plan limits or feature differences, the quotas and entitlements tables are empty — skip those commands below.
136
-
137
- **Do not create anything until the developer confirms all tables.**
138
-
139
- ## Step 3b — Create plans, connect Stripe, then set prices
140
-
141
- Once confirmed, create the plans. **Every plan needs at least one price** — a plan with no
142
- prices cannot be assigned to a workspace, so `plan create` takes the first one. A free tier is
143
- a **zero-amount** price, not the absence of a price:
144
-
145
- ```bash
146
- bridge plan create --key free --name "Free" --amount 0 --interval month
147
- bridge plan create --key pro --name "Pro" --amount 29 --interval month --currency usd --trial --trial-days 14
148
- ```
149
-
150
- Verify: `bridge plan list`
151
-
152
- If any plan will have a price, connect Stripe **before** setting prices (so each price syncs to Stripe):
153
-
154
- ```bash
155
- bridge stripe status
156
- ```
157
-
158
- If it reports `webhookHealthy: false`, checkout still works but subscription changes, cancellations
159
- and payment failures will not sync. Tell the developer and follow the repair hint in `message`.
160
-
161
- If not connected, ask the developer for their Stripe keys (from `dashboard.stripe.com/apikeys`):
162
-
163
- ```bash
164
- bridge setup payments --stripe-key sk_test_... --stripe-public-key pk_test_...
165
- ```
166
-
167
- This stores the keys **and** switches payments on in one step. (`bridge stripe connect` only
168
- stores the keys and leaves payments off.)
169
-
170
- Now set any **remaining** prices — one command per Prices-table row not already covered by the
171
- `plan create` call above. `plan price set` is idempotent (keyed on currency + interval), so a
172
- tier with monthly **and** yearly pricing is just one more call against the same plan key:
173
-
174
- ```bash
175
- bridge plan price set pro --amount 29 --currency usd --interval month
176
- bridge plan price set pro --amount 290 --currency usd --interval year
177
- ```
178
-
179
- Then create usage quotas from the confirmed table. A `hard` quota blocks the
180
- feature at the cap (entitlement flips off); a `metered` quota bills overage per
181
- unit and never blocks — it requires a per-unit price:
182
-
183
- ```bash
184
- # Hard cap (block at the limit) — a counter, the default
185
- bridge plan quota set <plan> --metric <key> --limit <n> --policy hard
186
-
187
- # Hard cap on something that exists (a gauge: projects, tickets)
188
- bridge plan quota set <plan> --metric <key> --limit <n> --policy hard --kind gauge
189
-
190
- # Metered: first <limit> units free, then --price-amount per unit (limit 0 = pure
191
- # per-unit, billed from unit 1). Currency defaults to the plan's price currency.
192
- bridge plan quota set <plan> --metric <key> --limit <n> --policy metered --price-amount <perUnit> [--price-currency <cur>]
193
- ```
194
-
195
- Run one command per row. Then add each on/off feature from the entitlements
196
- table to the plans that include it:
197
-
198
- ```bash
199
- bridge plan feature add <plan> <feature_key> --name "<Display name>"
200
- ```
201
-
202
- (There is no `plan entitlement set` command — do not invent one. Features live
203
- on the plan; every `hard` quota is also an entitlement of the same name.)
204
- Check the result with `bridge plan quota list`, which lists every metric with its kind.
205
-
206
- **Where the limit is counted.** Once, where the action happens. Ask the developer whether the action calls their server. If it does, the plan-limit check on the handler that creates the thing (NestJS `@RequireQuota`, Express `bridge.requireQuota`) refuses at the cap and records the use after a successful request — a POST increments the limit, nothing else to wire — and the frontend only shows that decision. If it happens in the browser only, the frontend reports it (`bridge.usage.report` / `bridge.usage.set`) and `<QuotaGate>` stops the button at the cap: a complete, first-class setup. Never both for one metric. `bridge guide mechanisms` has the whole model; `bridge guide fit-together` shows where limits sit next to plans, roles and flags.
207
-
208
- ## Step 4 — Fetch and apply the per-framework guide
209
-
210
- The per-framework prompts are **not** bundled in the CLI — each plugin ships its own at
211
- `mcp/billing-prompt.md`, which the commands below fetch. For each confirmed project, fetch the
212
- framework-specific guide and follow it verbatim:
213
-
214
- | Framework | Command |
215
- |-----------|---------|
216
- | SvelteKit | `bridge guide billing --framework svelte` |
217
- | React | `bridge guide billing --framework react` |
218
- | Next.js | `bridge guide billing --framework nextjs` |
219
- | Angular | `bridge guide billing --framework angular` |
220
- | NestJS | `bridge guide billing --framework nestjs` |
221
- | Express | `bridge guide billing --framework express` |
222
-
223
- If the CLI returns a 404, the plugin hasn't published its billing guide yet — tell the user, don't improvise inline.
224
-
225
- Wire frontend first, then backend.
226
-
227
- **When the per-framework guide is complete, do not stop — return here and continue with Step 4b.**
228
-
229
- ## Step 4b — Plan-selection paywall (on by default)
230
-
231
- By default, a signed-in tenant that hasn't chosen a plan is redirected to a dedicated
232
- welcome page and can't use the app until they pick one. **Set this up unless the developer
233
- opts out** — it's the expected first-run experience.
234
-
235
- - The per-framework guide (Step 4) mounts the plugin's subscription pages, which include a
236
- default paywall page (on SvelteKit: `/subscription/plan`, served by the subscription
237
- catch-all). Confirm that part ran. Do **not** create a separate welcome page on your own.
238
- - The paywall applies once the app has plans, and only while `paymentsAutoRedirect` is on.
239
- - A dedicated onboarding page such as `/welcome` is a product choice: **ask the developer**
240
- whether they want one. Only if they say yes, add it as the per-framework guide shows and
241
- point `billing.paywallRoute` at it.
242
- - Read `paymentsAutoRedirect` (`bridge app get`). **On:** nothing to do. **Off:** tell the
243
- developer in one line that new customers are not asked to pick a plan while it is off,
244
- and turn it on only if they say so. Do not investigate why it is off: the developer may
245
- have switched it off on purpose.
246
-
247
- If the developer does **not** want a forced paywall, disable it with one command:
248
-
249
- ```bash
250
- bridge app update --payments-auto-redirect false
251
- ```
252
-
253
- With the paywall off, users can enter the app without choosing a plan; the subscription pages
254
- still work for self-serve upgrades.
255
-
256
- ## Step 5 — Verify
257
-
258
- For each integrated project, the agent verifies — do not hand this to the developer:
259
-
260
- 1. Run the project's build command — no TypeScript or import errors
261
- 2. Navigate to the subscription route — plan cards render with correct prices, and a tier with monthly + yearly pricing shows both intervals (not two separate plans)
262
- 3. Select the free plan — subscription updates immediately, no redirect
263
- 4. Select a paid plan — Stripe Checkout opens
264
- 5. Complete a test payment — redirected back with the updated plan showing
265
- 6. Cancel a payment — redirected back to the subscription page
266
- 7. Paywall (unless opted out): sign in as a new tenant with no plan — you land on the paywall page (or `/welcome` if the developer chose one) and can't reach the app until a plan is chosen
267
- 8. Plan limit (when a backend was wired): at the cap, the decorated request answers `402 QUOTA_EXCEEDED` — send it with curl, not only through the UI — and in the app the upgrade dialog opens naming the metric
268
- 9. **Gate check** — `npx @nebulr-group/bridge-cli check gates` in the project: every direct role, privilege, plan or plan-feature check it lists becomes a flag. Run it again until it is clean
269
-
270
- If anything fails, diagnose and fix before moving on.
271
-
272
- ## Step 6 — Tell the developer what they just got
273
-
274
- **Do not skip this step.** Output the banner below, personalised. The very first character of your response must be `█` — no intro sentence, no "Here's your summary:", nothing before it. If something is broken, prepend a single "Heads up:" line before the `█`.
275
-
276
- ```
277
- ██████╗ ██████╗ ██╗██████╗ ██████╗ ███████╗
278
- ██╔══██╗██╔══██╗██║██╔══██╗██╔════╝ ██╔════╝
279
- ██████╔╝██████╔╝██║██║ ██║██║ ███╗█████╗
280
- ██╔══██╗██╔══██╗██║██║ ██║██║ ██║██╔══╝
281
- ██████╔╝██║ ██║██║██████╔╝╚██████╔╝███████╗
282
- ╚═════╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═════╝ ╚══════╝
283
-
284
- ──────────────────────────────────────────────
285
- Billing is live in [project-name].
286
-
287
- ✅ Plans — [plan-names]
288
- ✅ Subscription page — [subscription-route]
289
- ✅ Paywall — [paywall-summary]
290
- ✅ Lifecycle notices — auto-render on trial / payment failure / cancel
291
- ✅ Quotas and entitlements — [quota-summary]
292
- ──────────────────────────────────────────────
293
- Here is what I actually did:
294
- [what-i-actually-did]
295
-
296
- And here is what I changed:
297
- [what-i-changed]
298
- ──────────────────────────────────────────────
299
- Plan selection is required by default. To let users into the app without
300
- picking a plan first, disable the paywall:
301
- bridge app update --payments-auto-redirect false
302
- ──────────────────────────────────────────────
303
- ```
304
-
305
- Fill every `[placeholder]` with real values from the integration:
306
- - `[project-name]` — folder name and/or `package.json` name
307
- - `[plan-names]` — the plan keys (e.g. `free`, `premium`)
308
- - `[subscription-route]` — where the subscription page is served (SvelteKit default `/subscription`)
309
- - `[paywall-summary]` — e.g. "new users sent to `/subscription/plan` to pick a plan" (or "off — `paymentsAutoRedirect false`")
310
- - `[quota-summary]` — metric keys, limits and kind, and the backend handlers that enforce them, or "none"
311
- - `[what-i-actually-did]` / `[what-i-changed]` — every file created or modified, every command run
312
-
313
- ## Step 7 — Offer follow-on tracks
314
-
315
- After the success banner, mention what they can add next:
316
-
317
- - **Plan limits on the backend** — if the backend was not wired in this session: `bridge guide billing --framework nestjs` (one decorator per handler)
318
- - **Limits in the UI** — an upgrade dialog opens on its own when the backend refuses; disabling a button before the click or showing usage numbers is in the frontend guide, Step 3
319
- - **Portal** — "Manage billing" is already on the subscription page; the per-framework guide shows how to place it elsewhere
320
-
321
- Do not run these automatically — the developer decides when they want each.
322
-
323
- ---
324
-
325
- ## Reference — enforcement policies
326
-
327
- | Policy | At-cap behavior | When to use |
328
- |--------|-----------------|-------------|
329
- | `hard` | Entitlement flips off at cap; no overage | Seat counts, included features |
330
- | `metered` | Overage bills as a Stripe metered price; usage continues | API calls, storage |
331
-
332
- Bridge's usage ingest always accepts events — it never refuses on the app's behalf. With a backend, the refusal at a hard cap comes from **your** backend: the plan-limit check (`@RequireQuota` in NestJS, `bridge.requireQuota` in Express) reads the quota and answers `402 QUOTA_EXCEEDED` before the handler runs. Without one, `<QuotaGate>` stops the button at the cap. `bridge guide mechanisms` explains the model.