@nebulr-group/bridge-cli 0.6.0-beta.3 → 0.6.0-beta.5

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 (55) hide show
  1. package/dist/commands/flag.command.d.ts.map +1 -1
  2. package/dist/commands/flag.command.js +19 -6
  3. package/dist/commands/flag.command.js.map +1 -1
  4. package/dist/commands/guide.command.d.ts +12 -0
  5. package/dist/commands/guide.command.d.ts.map +1 -1
  6. package/dist/commands/guide.command.js +96 -6
  7. package/dist/commands/guide.command.js.map +1 -1
  8. package/dist/commands/integration.command.d.ts +8 -0
  9. package/dist/commands/integration.command.d.ts.map +1 -1
  10. package/dist/commands/integration.command.js +92 -3
  11. package/dist/commands/integration.command.js.map +1 -1
  12. package/dist/commands/journeys.d.ts +2 -1
  13. package/dist/commands/journeys.d.ts.map +1 -1
  14. package/dist/commands/journeys.js +15 -1
  15. package/dist/commands/journeys.js.map +1 -1
  16. package/dist/commands/plan.command.d.ts +39 -0
  17. package/dist/commands/plan.command.d.ts.map +1 -1
  18. package/dist/commands/plan.command.js +134 -10
  19. package/dist/commands/plan.command.js.map +1 -1
  20. package/dist/commands/role.command.d.ts.map +1 -1
  21. package/dist/commands/role.command.js +2 -1
  22. package/dist/commands/role.command.js.map +1 -1
  23. package/dist/commands/tenant.command.d.ts.map +1 -1
  24. package/dist/commands/tenant.command.js +12 -5
  25. package/dist/commands/tenant.command.js.map +1 -1
  26. package/dist/commands/token.command.d.ts.map +1 -1
  27. package/dist/commands/token.command.js +6 -0
  28. package/dist/commands/token.command.js.map +1 -1
  29. package/dist/commands/user.command.d.ts.map +1 -1
  30. package/dist/commands/user.command.js +6 -0
  31. package/dist/commands/user.command.js.map +1 -1
  32. package/dist/flag-coherence.d.ts +42 -0
  33. package/dist/flag-coherence.d.ts.map +1 -0
  34. package/dist/flag-coherence.js +103 -0
  35. package/dist/flag-coherence.js.map +1 -0
  36. package/dist/preview.d.ts +9 -0
  37. package/dist/preview.d.ts.map +1 -0
  38. package/dist/preview.js +63 -0
  39. package/dist/preview.js.map +1 -0
  40. package/dist/prompts/auth-master-integration-prompt.md +15 -0
  41. package/dist/prompts/billing/master.md +20 -1
  42. package/dist/prompts/decisions/feature-control.md +105 -0
  43. package/dist/prompts/decisions/going-live.md +59 -0
  44. package/dist/prompts/decisions/login.md +68 -0
  45. package/dist/prompts/decisions/look-and-feel.md +62 -0
  46. package/dist/prompts/decisions/payments.md +81 -0
  47. package/dist/prompts/decisions/roles.md +72 -0
  48. package/dist/prompts/decisions/teams.md +56 -0
  49. package/dist/prompts/mcp-tools.json +78 -0
  50. package/dist/prompts/orientation.md +40 -0
  51. package/dist/shape.d.ts +25 -0
  52. package/dist/shape.d.ts.map +1 -0
  53. package/dist/shape.js +74 -0
  54. package/dist/shape.js.map +1 -0
  55. package/package.json +1 -1
@@ -0,0 +1,105 @@
1
+ # Feature control — decision guide
2
+
3
+ Turning features on and off for some people and not others, without a deploy. The tool is a **flag**: code asks for it by key, and the flag's rule decides the answer.
4
+
5
+ > Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). How the pieces work underneath: `bridge guide mechanisms`.
6
+
7
+ ## The rule that decides everything else
8
+
9
+ - **Is a feature on for them? A flag.** Always, and for good. The flag is the stable place in code; the *reasons* live in its rule and change as the business does: 10% of users today, "plan is Pro" tomorrow, one named customer as an exception next month. Each of those is a rule edit, not new code.
10
+ - **How much do they get? The plan.** Prices, trials and limits live on the plan: see the **payments** guide.
11
+ - **Who inside a workspace may do it? A role.** See the **roles** guide.
12
+
13
+ A plan can also grant a feature on its own (a *plan feature*, checked with `@RequireEntitlement` on the backend and `<Entitled>` in the UI; the payments guide explains it). Use that only when the feature *is* what the plan sells and nothing else will ever decide it: the backend then refuses it without extra wiring. Anything that could ever need a rollout, a trial cohort or a one-customer exception is a flag.
14
+
15
+ ## Start from what is there
16
+
17
+ | Read | MCP | CLI |
18
+ |---|---|---|
19
+ | Every flag with its rule, and warnings on flags that cannot mean what they look like | `list_feature_flags` | `bridge flag list` |
20
+ | One flag | `get_flag` | `bridge flag get <key>` |
21
+ | Plan keys and role keys, for rules | `list_plans`, `list_roles` | `bridge plan list`, `bridge role list` |
22
+
23
+ ## Ask the developer
24
+
25
+ Product questions. Ask, wait, never guess.
26
+
27
+ 1. **Which feature, in their words, and who should get it?** Everyone, nobody yet, a percentage of users, one plan, some roles, named customers, or a mix.
28
+ 2. **What do people without it see?** Nothing at all, a teaser with an upgrade link, or the old version.
29
+ 3. **Should it switch on or off by itself at a set time?**
30
+ 4. **If it is tied to a plan: is it simply part of that plan for ever,** or might it later go to a trial group or a single customer too? (This picks flag or plan feature, above.)
31
+
32
+ ## Decide yourself
33
+
34
+ - **Flag keys in kebab-case, named after the feature: `new-editor`, `analytics`.** Reason: the key is what code reads; it should still make sense after the rollout is over.
35
+ - **Boolean unless the developer needs variants.** Reason: on/off covers almost every case and reads plainly in code.
36
+ - **Target a plan by its key (`pro`), from `list_plans`, never its display name,** whatever an example elsewhere shows. Reason: the app sends the key.
37
+ - **Always give the rule an `otherwiseValue`, and make it the safe answer (usually `false`).** Reason: anyone the rule does not match gets it.
38
+ - **Test the rule with the exact attributes the app sends before relying on it** (`evaluate_feature_flag` / `bridge flag eval`). Reason: a rule on an attribute the app never sends silently answers `otherwiseValue` for everyone.
39
+ - **Read flags through the SDK the per-framework guide names, never with a hand-written request.** Reason: a wrong address fails into "off", which looks exactly like "not bought".
40
+ - **Treat a warning from `list_feature_flags` as a bug to fix now.**
41
+
42
+ ## The attributes a rule can test
43
+
44
+ These are the exact names and values. A rule on any other name matches nothing unless the app sends that attribute itself.
45
+
46
+ **Sent by the browser SDKs** (SvelteKit, React, Next.js, Angular) for a signed-in person, read from their token:
47
+
48
+ | Attribute | Value | Compared |
49
+ |---|---|---|
50
+ | `user.id` | the person's id | exactly |
51
+ | `user.email` | their email | exactly |
52
+ | `user.role` | the role **key** as created, e.g. `OWNER`, `ADMIN` | exactly: `admin` does not match `ADMIN` |
53
+ | `tenant.id` | the workspace id | exactly |
54
+ | `tenant.plan` | the plan **key**, e.g. `pro` (not the name) | ignoring case: `Pro` matches `pro` |
55
+ | `privileges` | a list of privilege keys | as a list; target a role instead |
56
+
57
+ **Also sent by the browser SDKs, from the workspace's subscription:**
58
+
59
+ | Attribute | Value |
60
+ |---|---|
61
+ | `bridge:billing.plan` | the plan key, compared exactly |
62
+ | `bridge:billing.subscription.status` | `trial`, `active`, `past_due`, `cancel_at_period_end` or `canceled` |
63
+ | `bridge:billing.trial` | `true` or `false` |
64
+ | `bridge:billing.quota.<metric>.used` / `.limit` / `.remaining` | numbers |
65
+ | `bridge:billing.quota.<metric>.percent_used` | a fraction: `0.8` is 80% |
66
+ | `bridge:billing.entitlement.<name>` | `true` or `false`, including `app_active` (the subscription is active, trialing, past due or cancelling) |
67
+
68
+ **On the NestJS backend** only the signed-in person's id is filled in (as the identity). A rule on `tenant.plan` or `user.role` sees nothing there until the app passes those values from the verified user on each call; the NestJS flags guide shows how. Never pass values the client sent.
69
+
70
+ **Percentage rollouts** bucket on the identity: the person's id when signed in, a stored anonymous id in the browser otherwise. A backend call with no signed-in person gets the default.
71
+
72
+ Operators: `eq`, `neq`, `in`, `not_in`, `contains`, `not_contains`, `gt`, `lt`, `between`, `regex`, `exists`, `not_exists`.
73
+
74
+ A condition's values are always a list, even for a single value.
75
+
76
+ ## Do it
77
+
78
+ | Step | MCP | CLI |
79
+ |---|---|---|
80
+ | Create a flag with its rule | `create_feature_flag` | `bridge flag create --key analytics --state on-with-rule --rule '<json>'` |
81
+ | Change a rule or values | `update_feature_flag` | `bridge flag update --key analytics --rule '<json>'` |
82
+ | Switch it on or off for everyone now (the rule is kept, unused while on) | `toggle_feature_flag` | `bridge flag toggle --key analytics --enabled true` |
83
+ | Check who gets what | `evaluate_feature_flag` | `bridge flag eval analytics --identity u1 --attribute tenant.plan=pro` |
84
+ | Switch at a set time | `set_flag_schedule` | `bridge flag schedule set analytics --at … --state on` |
85
+ | Copy flags between apps | `export_feature_flags`, `import_feature_flags` | `bridge flag export`, `bridge flag import <file>` |
86
+ | Code for the framework | `get_integration_guide` (topic `feature-flags`) | `bridge guide flags --framework svelte` |
87
+
88
+ A rule for "Pro only":
89
+
90
+ ```json
91
+ { "branches": [{ "conditions": [{ "attribute": "tenant.plan", "operator": "eq", "values": ["pro"] }], "returnValue": true }],
92
+ "otherwiseValue": false, "rolloutPct": 100 }
93
+ ```
94
+
95
+ ## Prove it
96
+
97
+ 1. `evaluate_feature_flag` / `bridge flag eval` with a context that should get the feature, then one that should not: the answers differ as the developer intended.
98
+ 2. `list_feature_flags` / `bridge flag list` shows no warnings on the flag.
99
+ 3. In the app, as a test user on each side of the rule.
100
+
101
+ ## Where this connects
102
+
103
+ - **Payments:** plan keys, and plan features versus flags.
104
+ - **Roles:** role keys for `user.role`, and why a flag is not a permission.
105
+ - **Going live:** flags are per app; a production app needs its own (`export_feature_flags`, then `import_feature_flags`).
@@ -0,0 +1,59 @@
1
+ # Going live — decision guide
2
+
3
+ Everything between "it works on my machine" and real users signing up and paying. Bridge keeps a readiness checklist for each app; this guide is how to work through it with the developer.
4
+
5
+ > Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). How the pieces work underneath: `bridge guide mechanisms`.
6
+
7
+ ## Start from the checklist
8
+
9
+ `get_setup_status` / `bridge setup status` lists every item (redirect URIs, default callback, allowed origins, UI URL, sign-in methods and provider credentials, email sender, Stripe, plans, first real sign-in) as configured, missing or needing attention, each with its next step. Its `productionNeeds` is what must still change before real users arrive: localhost addresses, test Stripe keys, an unconfirmed sender. Work from that list, not from memory.
10
+
11
+ `list_apps` shows which apps this connection can reach. `diagnose_integration` / `bridge diagnose` compares the project's env files with the app they point at.
12
+
13
+ ## Ask the developer
14
+
15
+ Product questions. Ask, wait, never guess.
16
+
17
+ 1. **Is the app you have been building with already serving real users?** If yes, it is the production app. If no, see "a separate production app" below.
18
+ 2. **What is the production address of the frontend** (and of the backend, if it calls Bridge)?
19
+ 3. **For each social sign-in provider: the production client id and secret,** if they use a different provider app in production.
20
+ 4. **The live Stripe keys** (`sk_live_…`, `pk_live_…`), from the Stripe account that should receive the money. Never invent, guess or reuse a key.
21
+ 5. **Which address and name should emails come from** in production?
22
+
23
+ ## Decide yourself
24
+
25
+ - **A separate Bridge app for production** when the current one has no real users. Reason: test users, test Stripe keys and localhost addresses stay out of production, and nothing in development can touch a real customer. Create it with `create_app`; it does not become the default, so pass its id as `app` or call `use_app`. Copy flags across with `export_feature_flags` then `import_feature_flags`, and recreate each plan with `apply_plan`.
26
+ - **Production env files carry the production app id and no base URL.** Reason: an unset base URL means Bridge's production API; a leftover stage or local base URL sends a production app id to the wrong place, and sign-in fails.
27
+ - **Add production addresses; remove localhost ones only from the production app.** Reason: removing them from the development app breaks local work.
28
+ - **Add redirect URIs one at a time and send allowed origins as a full list** (read first). Reason: the first keeps the list; the second replaces it.
29
+ - **Register the production callback in every social provider's console** (`setup_sso` returns it). Reason: providers refuse callbacks they do not know.
30
+ - **Never put a secret key in a browser variable** (`VITE_*`, `NEXT_PUBLIC_*`). Reason: everything in those ships to every visitor. `diagnose_integration` flags it.
31
+ - **A test user on a production app needs the developer's say-so.** Reason: it creates a workspace in the app real users share; the tool refuses without confirmation.
32
+
33
+ ## Do it
34
+
35
+ | Step | MCP | CLI |
36
+ |---|---|---|
37
+ | Readiness checklist | `get_setup_status` | `bridge setup status` |
38
+ | Check a project's env files | `diagnose_integration` | `bridge diagnose` |
39
+ | Create a production app | `create_app`, then `use_app` | — (create it in the dashboard, then `bridge auth login --app <id>`) |
40
+ | Production URL, default callback, allowed origins | `update_app` | `bridge app update --ui-url … --default-callback-uri … --allowed-origins …` |
41
+ | Production callback URL | `add_redirect_uri` | `bridge app redirect-uris add <url>` |
42
+ | Live Stripe keys | `setup_payments` | `bridge setup payments --stripe-key sk_live_… --stripe-public-key pk_live_…` |
43
+ | Email sender | `setup_communication` | `bridge setup communication --from-address … --from-name …` |
44
+ | Copy flags | `export_feature_flags`, `import_feature_flags` | `bridge flag export --out flags.json`, `bridge flag import flags.json` |
45
+ | Copy a plan | `apply_plan` | `bridge plan apply --spec @plan.json` |
46
+
47
+ ## Prove it
48
+
49
+ 1. `get_setup_status` / `bridge setup status` again: show the developer what is left. Nothing in `productionNeeds` should be a surprise to them.
50
+ 2. With the developer's agreement, `create_test_user` then `verify_login` (`bridge test-user create --confirm "<app name>"`, then `bridge test-user verify`) against the production app: a real sign-in from the production origin.
51
+ 3. `get_stripe_status` / `bridge stripe status`: live keys, webhook working.
52
+ 4. The first real sign-in appears on the checklist once someone signs up.
53
+
54
+ ## Where this connects
55
+
56
+ - **Login:** redirect URIs, origins and provider credentials.
57
+ - **Payments:** live keys, and plans on the production app.
58
+ - **Feature control:** flags are per app.
59
+ - **Look and feel:** a confirmed sender address.
@@ -0,0 +1,68 @@
1
+ # Login — decision guide
2
+
3
+ How people sign up and sign in to the developer's app. This guide says what to ask, what to decide yourself, and which tool or command does each step. The code itself is in the per-framework guide.
4
+
5
+ > Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). How the pieces work underneath: `bridge guide mechanisms`.
6
+
7
+ ## Start from what is there
8
+
9
+ Read before you ask anything, so your questions are about changes, not about the current state.
10
+
11
+ | Read | MCP | CLI |
12
+ |---|---|---|
13
+ | Sign-in methods and token lifetimes | `get_auth_config` | `bridge auth config` |
14
+ | URLs, redirect URIs, allowed origins | `get_app` | `bridge app get` |
15
+ | The exact env variables for this project | `get_environment_info` | — |
16
+
17
+ A new app has password, magic link and passkeys on, two-factor off, and no social providers.
18
+
19
+ ## Ask the developer
20
+
21
+ Product questions. Ask, wait, never guess.
22
+
23
+ 1. **Which ways should people be able to sign in?** Password is always there. Offer magic link, passkeys, Google, GitHub, Microsoft (Azure AD), LinkedIn, Facebook. Company SSO (SAML or OIDC) is set up in the Bridge dashboard, not from here.
24
+ 2. **Should two-factor sign-in be available?**
25
+ 3. **Should sign-in happen on Bridge's hosted page, or inside the app on its own address?** Both use the same one route file; the look-and-feel guide covers what each looks like.
26
+ 4. **Can anyone sign up, or only people who are invited?** This is a teams question: see the **teams** guide before changing it.
27
+ 5. **For each social provider they pick: the client id and client secret** from that provider's console. Never invent, guess or reuse a credential.
28
+
29
+ ## Decide yourself
30
+
31
+ Apply these without asking. Say the reason in one line.
32
+
33
+ - **Callback URL is `<frontend-url>/auth/oauth-callback`,** registered as a redirect URI and as the default callback. Reason: the sign-in route file serves that address, and Bridge refuses any callback it has not seen.
34
+ - **Add redirect URIs one at a time** with `add_redirect_uri` / `bridge app redirect-uris add`. Reason: they keep the rest of the list; nothing is lost.
35
+ - **Before changing allowed origins, read the current list and pass it back with the new origin added.** Reason: `update_app` and `bridge app update --allowed-origins` replace the whole list.
36
+ - **Set the UI URL to the frontend's address.** Reason: links in invitation and password-reset emails are built from it; unset, people land on Bridge's hosted pages.
37
+ - **Leave token lifetimes at their defaults** unless the developer raises it. Reason: the defaults are safe. There are no password-complexity rules to set; if asked, say so.
38
+ - **Write no sign-in pages by hand, and no token fetch helper.** Reason: the plugin serves every sign-in page (login, signup, callback, set password, forgot password, magic link, passkey setup, workspace selection) from one file.
39
+ - **Protect every route by default; make only `/auth/*` public.** Reason: a new page is private until someone decides otherwise.
40
+
41
+ ## Do it
42
+
43
+ | Step | MCP | CLI |
44
+ |---|---|---|
45
+ | Turn magic link, passkeys or two-factor on or off | `update_auth_methods` | `bridge app update --magic-link-enabled true` (also `--passkeys-enabled`, `--mfa-enabled`) |
46
+ | Add a social provider with its credentials | `setup_sso` | `bridge setup sso --provider google --client-id … --client-secret …` |
47
+ | Register the callback URL | `add_redirect_uri` | `bridge app redirect-uris add <url>` |
48
+ | Set UI URL, default callback, allowed origins | `update_app` | `bridge app update --ui-url … --default-callback-uri … --allowed-origins …` |
49
+ | Change token lifetimes (seconds) | `update_password_policy` | `bridge auth password-policy --access-token-ttl 900` |
50
+ | Code for the framework | `get_integration_guide` (topic `auth`) | `bridge guide svelte` (hosted) or `bridge guide svelte sdk-auth` (in-app) |
51
+
52
+ `setup_sso` returns a callback URL. The developer must paste it into the provider's console, or that provider's sign-in fails with a redirect mismatch. Always show it to them with that instruction. `update_auth_methods` can switch a provider on but never saves its credentials.
53
+
54
+ ## Prove it
55
+
56
+ Do not say "it should work". Show it working.
57
+
58
+ 1. `diagnose_integration` / `bridge diagnose`: compares the project's env files with the real app and names every difference with its fix.
59
+ 2. `create_test_user`, then `verify_login` / `bridge test-user create`, then `bridge test-user verify`: a real sign-in, with the token checked.
60
+ 3. If sign-in works there but not in the app, the app code is the difference.
61
+ 4. When a real person cannot sign in: `debug_auth_issue` / `bridge event auth-attempts --user <email>` gives each failed attempt with its reason and fix.
62
+
63
+ ## Where this connects
64
+
65
+ - **Teams:** who can sign up, and whether they create a workspace or join one.
66
+ - **Roles:** what a person can do once signed in.
67
+ - **Look and feel:** hosted or in-app sign-in, and how the pages look.
68
+ - **Going live:** production URLs and redirect URIs replace the localhost ones.
@@ -0,0 +1,62 @@
1
+ # Look and feel — decision guide
2
+
3
+ How Bridge's pages and emails look to the developer's users: the sign-in pages, the subscription pages, and the emails Bridge sends in the app's name.
4
+
5
+ > Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). The customisation levels in full, with every design token: `bridge guide mechanisms`.
6
+
7
+ ## Two places pages can live
8
+
9
+ - **Hosted:** Bridge serves sign-in on its own address. Styled with the app's **branding** (colours, font, corner radius), which Bridge stores.
10
+ - **In the app:** the same pages render inside the developer's own layout, on their address. Styled with CSS design tokens (`--bridge-*`) in the app's own stylesheet.
11
+
12
+ Both use the same single route file; one setting switches sign-in into the app (`loginRoute: '/auth/login'` in SvelteKit's `bridgeBootstrap()`). Subscription pages always render in the app.
13
+
14
+ ## Start from what is there
15
+
16
+ | Read | MCP | CLI |
17
+ |---|---|---|
18
+ | Hosted-page branding | `get_branding` | `bridge branding get` |
19
+ | App name, and the email sender | `get_app` | `bridge app get` |
20
+
21
+ ## Ask the developer
22
+
23
+ Product questions. Ask, wait, never guess.
24
+
25
+ 1. **Should sign-in look like part of the app, on the app's own address, or is Bridge's hosted page fine?**
26
+ 2. **What are the brand colours and font?** Ask for exact values or point at their existing stylesheet; do not pick colours for them.
27
+ 3. **What name should users see** on sign-in pages and in emails?
28
+ 4. **Which address and sender name should emails come from?** (Invitations, password resets, magic links.)
29
+ 5. **Beyond colours, should sign-in pages say anything of their own?** A different heading, a frame around the form, a page they design themselves.
30
+
31
+ ## Decide yourself
32
+
33
+ Climb only as far as the answers require, and say which level you stopped at.
34
+
35
+ - **Level 0, nothing:** in-app pages already sit inside the app's layout, so its header and navigation surround them. Reason: often this alone is enough.
36
+ - **Level 1, tokens:** set `--bridge-primary`, `--bridge-border-radius` and friends on `:root`. Reason: tokens are the supported contract; ordinary CSS already wins over Bridge's defaults, so there is never a need for `!important`.
37
+ - **Level 2, frame and heading:** pass the `frame` and `heading` snippets to the sign-in routes component. Reason: it changes every page at once and keeps each page's own sub-steps.
38
+ - **Level 3, take over one page:** create that page's own route file (e.g. `src/routes/auth/login/+page.svelte`); every other page keeps working.
39
+ - **Level 4, headless:** build everything on `getBridgeAuth()`. Only when the developer asks for it.
40
+ - **Never copy Bridge's components into the app to restyle them.** Reason: the copy stops receiving fixes.
41
+ - **When both hosted pages and in-app pages are used, give them the same colours.** Reason: a person moving between them should not notice.
42
+ - **A new sender address must be confirmed.** Bridge emails that address a link; until it is clicked, mail keeps coming from Bridge's default sender. Tell the developer to look for it.
43
+
44
+ ## Do it
45
+
46
+ | Step | MCP | CLI |
47
+ |---|---|---|
48
+ | Hosted-page colours, font, corners | `update_branding` | `bridge branding update --primary-btn-bg '#1a1a2e' --font-family Inter` |
49
+ | Name users see | `update_app` (`name`) | `bridge app update --name "Acme"` |
50
+ | Email sender | `setup_communication` | `bridge setup communication --from-address no-reply@acme.com --from-name Acme` |
51
+ | In-app pages, tokens, snippets | `get_integration_guide` (topic `branding`, then `auth`) | `bridge guide svelte branding`, `bridge guide mechanisms` |
52
+
53
+ ## Prove it
54
+
55
+ 1. Open the sign-in page and the subscription page in the running app (or the hosted page): colours and font match what the developer gave.
56
+ 2. Trigger a password reset for a test user: the email arrives from the agreed sender, in the app's name, and its link opens the app, not a Bridge page.
57
+
58
+ ## Where this connects
59
+
60
+ - **Login:** hosted or in-app sign-in is the same switch.
61
+ - **Payments:** the subscription and plan-choice pages use the same tokens.
62
+ - **Going live:** a verified sender address before real users arrive.
@@ -0,0 +1,81 @@
1
+ # Payments and plans — decision guide
2
+
3
+ What customers pay, what each plan includes, and what happens when they reach a limit. A **plan** belongs to a workspace, not to a person. It carries the price, the trial and the limits ("3 projects on Free, 50 on Pro"). Payments run through the developer's own Stripe account.
4
+
5
+ > Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). The full model behind limits and upgrade prompts: `bridge guide mechanisms`.
6
+
7
+ ## Start from what is there
8
+
9
+ | Read | MCP | CLI |
10
+ |---|---|---|
11
+ | Plans with prices and limits | `list_plans` | `bridge plan list` |
12
+ | Every metric the plans already limit, with its kind | `list_plan_quotas` | `bridge plan quota list` |
13
+ | Whether Stripe is connected and its webhook works | `get_stripe_status` | `bridge stripe status` |
14
+ | Whether new customers must pick a plan first (`paymentsAutoRedirect`) | `get_app` | `bridge app get` |
15
+
16
+ ## Ask the developer
17
+
18
+ Product questions. Ask, wait, never guess. Then show the answer back as tables (plans, prices, limits) and create nothing until the developer confirms them.
19
+
20
+ 1. **Which plans, and what are they called?** Is there a free one?
21
+ 2. **What does each paid plan cost, in which currency, and monthly, yearly or both?**
22
+ 3. **Is there a free trial, and how many days?**
23
+ 4. **What does each plan limit, and how much?** In their words: "2 tickets on Free, 100 on Pro", "10 seats", "1,000 exports a month".
24
+ 5. **For each limit: at the limit, block, or keep going and charge per extra unit?** If they charge, how much per unit?
25
+ 6. **Which features are only on some plans?** ("Analytics on Pro only.")
26
+ 7. **Do they want a welcome page for new customers** where they choose a plan? Offer it; create it only if they say yes.
27
+
28
+ ## Decide yourself
29
+
30
+ - **Plan keys short and lower-case: `free`, `pro`, `team`.** Reason: the key is permanent and is what code, flag rules and tokens use; the name can change any time.
31
+ - **Monthly and yearly prices of one tier are one plan with two prices,** never `pro-monthly` and `pro-yearly`. Reason: the subscription page then shows one plan with a monthly/yearly switch.
32
+ - **A free plan gets a price of 0.** Reason: a plan with no price cannot be assigned to a workspace.
33
+ - **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. Exports, AI completions and emails are counters (reset each billing period). Tickets, projects and stored files are gauges (never reset). Decide from that sentence and say which you chose.
34
+ - **Seats are the built-in `users` metric, a gauge.** Reason: Bridge counts workspace members itself.
35
+ - **Reuse metric names the app already has** (`list_plan_quotas`) before inventing new ones. Reason: two names for one thing split the count.
36
+ - **Connect Stripe with test keys first; live keys only when going live.** Reason: nobody is charged while you build. Use `setup_payments` / `bridge setup payments`: it stores the keys and switches payments on in one step.
37
+ - **Connect Stripe before adding paid prices.** Reason: each price is created in Stripe as it is saved.
38
+ - **Leave "customers must pick a plan" on** (it is on by default) unless the developer says otherwise. Reason: a new workspace then always has a plan, so every limit and feature check has something to read.
39
+ - **One plan, one call:** `apply_plan` / `bridge plan apply` sets name, trial, prices and limits together. Reason: a plan is never left half set up.
40
+
41
+ ## How a hard limit is enforced, end to end
42
+
43
+ Tell the developer this plainly; it is where integrations go wrong.
44
+
45
+ 1. **The plan holds the number.** A limit is a quota on the plan: metric, limit, `hard`, and `counter` or `gauge`.
46
+ 2. **The backend enforces it, and only the backend.** In NestJS one decorator on the handler that creates the thing: `@RequireQuota('exports')`. Before the handler runs, a workspace at its limit gets `402` with `{ code: 'QUOTA_EXCEEDED', metric, used, limit, fix }` and the handler never runs. After a `2xx` answer, Bridge records one use. Nothing else to wire: no usage call, no counter table. If Bridge cannot answer, the request is refused with `503`, never let through.
47
+ 3. **Gauges send the app's own count.** `@RequireQuota('tickets', { current })` on create and `@SyncQuota('tickets', { current })` on delete, where `current` returns the app's count from its own database. There is no decrement.
48
+ 4. **Seats need no count at all.** `@RequireQuota('users')` on the invite handler checks the seat limit.
49
+ 5. **The frontend only explains the refusal.** With no code at all, the upgrade dialog opens on a `402`, names the limit, and links to the subscription page. A member who cannot manage billing is told to ask the owner. Disabling a button before the click (`<QuotaGate>`) or showing "8 of 10" (`useQuota`) is optional.
50
+ 6. **A frontend alone cannot enforce a limit.** Anyone can call the API directly. An app with no backend can report usage from the browser, but that is self-reported: Bridge counts what the client says.
51
+ 7. **`metered` never blocks.** Past the included amount it bills per unit through Stripe.
52
+ 8. **A plan feature is a hard limit nothing counts.** `analytics` with limit 1 on `pro` makes `@RequireEntitlement('analytics')` and `<Entitled to="analytics">` true on Pro and false elsewhere. Never put `@RequireEntitlement` and `@RequireQuota` on the same name: at the cap the entitlement answers `403` first and the upgrade dialog never opens.
53
+ 9. **The plan-choice page** (`/subscription/plan`) appears only when the app has plans and "customers must pick a plan" is on. Turn it off with `update_app` (`paymentsAutoRedirect: false`) / `bridge app update --payments-auto-redirect false`.
54
+
55
+ Whether a paid feature is a plan feature or a flag with a plan rule is decided in the **feature-control** guide.
56
+
57
+ ## Do it
58
+
59
+ | Step | MCP | CLI |
60
+ |---|---|---|
61
+ | Connect Stripe (keys from the developer) | `setup_payments` | `bridge setup payments --stripe-key sk_test_… --stripe-public-key pk_test_…` |
62
+ | Set up or reshape one plan in one call | `apply_plan` | `bridge plan apply --spec @plan.json` |
63
+ | Or step by step: create a plan with its first price | `create_plan` | `bridge plan create --key pro --name Pro --amount 29 --interval month` |
64
+ | Add another price | `set_plan_price` | `bridge plan price set pro --amount 290 --interval year` |
65
+ | Add a limit | `set_plan_quota` | `bridge plan quota set pro --metric tickets --limit 100 --policy hard --kind gauge` |
66
+ | Turn off the forced plan choice | `update_app` | `bridge app update --payments-auto-redirect false` |
67
+ | Code for the framework | `get_integration_guide` (topic `billing`) | `bridge guide billing --framework svelte`, `bridge guide billing --framework nestjs` |
68
+
69
+ ## Prove it
70
+
71
+ 1. `list_plans` / `bridge plan list` matches the tables the developer confirmed; a plan with monthly and yearly pricing is one plan.
72
+ 2. `get_stripe_status` / `bridge stripe status`: connected, webhook working.
73
+ 3. At the limit, send the decorated request with curl: it answers `402 QUOTA_EXCEEDED`. In the app, the upgrade dialog opens and names the limit.
74
+ 4. A paid plan opens Stripe Checkout and returns to the subscription page with the new plan.
75
+
76
+ ## Where this connects
77
+
78
+ - **Feature control:** features that depend on the plan.
79
+ - **Teams:** seats, and the plan belonging to the workspace.
80
+ - **Roles:** who may change the plan.
81
+ - **Going live:** live Stripe keys.
@@ -0,0 +1,72 @@
1
+ # Roles and permissions — decision guide
2
+
3
+ Who may do what inside a customer's workspace. A **role** (Owner, Admin, Editor…) is a named bundle of **privileges** (`USER_WRITE`, `INVOICE_APPROVE`…). Every signed-in person has one role per workspace, and their token carries its role key and privileges, so the backend can check them on every request.
4
+
5
+ > Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). How the pieces work underneath: `bridge guide mechanisms`.
6
+
7
+ ## Which tool answers which question
8
+
9
+ Three different questions, three different tools. Keep them apart.
10
+
11
+ | The question | Answered by | Guide |
12
+ |---|---|---|
13
+ | Who, inside a workspace, may do this? | a **role** and its privileges | this one |
14
+ | Is this feature on for them at all? | a **flag** | feature-control |
15
+ | How much do they get (projects, seats, exports)? | the **plan** | payments |
16
+
17
+ A role never stands in for a plan ("Pro users are Admins") and a plan never stands in for a role.
18
+
19
+ ## Start from what is there
20
+
21
+ | Read | MCP | CLI |
22
+ |---|---|---|
23
+ | Roles, their privileges, and which is the default | `list_roles` | `bridge role list` |
24
+ | One role in full | `get_role` | — |
25
+ | Privileges that exist | `list_roles` (the `privileges` map) | `bridge privilege list` |
26
+
27
+ A new app has two roles: **Owner** (`OWNER`, everything, including permanent deletes) and **Admin** (`ADMIN`, manage people, read workspace settings). **Owner is the default role**, which means anyone invited without a role becomes an Owner.
28
+
29
+ ## Ask the developer
30
+
31
+ Product questions. Ask, wait, never guess.
32
+
33
+ 1. **What kinds of people use one customer's workspace?** For example: the person who pays, people who manage the team, people who do the work, people who only look.
34
+ 2. **For each kind, what may they do that others may not?** Ask in the app's own verbs: approve an invoice, delete a project, invite a colleague, change billing.
35
+ 3. **Which role should a newly invited person get?** Owner is rarely the right answer.
36
+ 4. **Who may manage the subscription and billing?**
37
+
38
+ ## Decide yourself
39
+
40
+ - **Name privileges after actions, in capitals: `PROJECT_DELETE`, `INVOICE_APPROVE`.** Reason: they read like the existing ones (`USER_READ`, `TENANT_WRITE`) and say exactly what they allow.
41
+ - **Role keys in capitals too: `EDITOR`, `VIEWER`.** Reason: the token carries the key exactly as written, and a flag rule on `user.role` compares it case for case.
42
+ - **In code, check a privilege, not a role name.** Reason: roles get reshaped as the product grows; the privilege on a handler stays true.
43
+ - **Keep Owner as the one role with everything.** Never strip it: it is the only role that can delete, and every workspace needs someone who can.
44
+ - **Make the lowest sensible role the default** by creating it with the default setting on. Reason: a forgotten role on an invitation then gives too little, not too much.
45
+ - **When changing a role's privileges, send the full list.** Reason: the update replaces the list, and anything left out is revoked.
46
+ - **Enforce on the backend; hide buttons in the frontend only as a courtesy.** Reason: anyone can call the API directly.
47
+
48
+ ## Do it
49
+
50
+ | Step | MCP | CLI |
51
+ |---|---|---|
52
+ | Add a privilege | — | `bridge privilege create --key PROJECT_DELETE --description …` |
53
+ | Create a role | `create_role` | `bridge role create --key EDITOR --name Editor --privileges PROJECT_READ,PROJECT_WRITE` |
54
+ | Create it as the new default | `create_role` (`isDefault: true`) | `bridge role create --key MEMBER --name Member --privileges … --is-default` |
55
+ | Change a role's privileges | `update_role` (full list) | `bridge role update --key EDITOR --privileges …` (full list) |
56
+ | Give a person a role | `update_user` | `bridge user update --email … --role EDITOR --tenant-id …` |
57
+ | Code for the framework | `get_integration_guide` (topic `team`) | `bridge guide nestjs` (backend checks), `bridge guide svelte team` |
58
+
59
+ On the NestJS backend the check is a decorator on the handler: `@RequirePrivilege('PROJECT_DELETE')`, or `@RequireRole('ADMIN')` when the developer really means a role.
60
+
61
+ Setting the default on one role clears it from the previous default.
62
+
63
+ ## Prove it
64
+
65
+ 1. `list_roles` / `bridge role list` shows each role with exactly the privileges agreed, and the default where the developer wanted it.
66
+ 2. Call a protected backend route as a user whose role lacks the privilege: it must answer 403. Then with one who has it: it must succeed.
67
+
68
+ ## Where this connects
69
+
70
+ - **Teams:** the role an invited person gets.
71
+ - **Feature control:** a flag can target `user.role`, but "may this person do it" is still a privilege check on the backend.
72
+ - **Payments:** who may change the plan.
@@ -0,0 +1,56 @@
1
+ # Teams and workspaces — decision guide
2
+
3
+ How customers are grouped in the developer's app. In Bridge every signed-in person belongs to a **workspace** (the API and the CLI call it a tenant). A workspace is one customer: it holds the people, their roles, and the plan the customer pays for.
4
+
5
+ > Other decision guides: `bridge guide decision <name>` (MCP: resource `bridge://guides/decisions/<name>`). How the pieces work underneath: `bridge guide mechanisms`.
6
+
7
+ ## Start from what is there
8
+
9
+ | Read | MCP | CLI |
10
+ |---|---|---|
11
+ | Whether people can create their own workspace (`tenantSelfSignup`) | `get_app` | `bridge app get` |
12
+ | Existing workspaces | `list_tenants` | `bridge tenant list` |
13
+ | The people in one workspace | `list_users` | `bridge user list --tenant-id <id>` |
14
+ | Roles an invited person can get | `list_roles` | `bridge role list` |
15
+
16
+ ## Ask the developer
17
+
18
+ Product questions. Ask, wait, never guess.
19
+
20
+ 1. **Is the app for single people or for teams?** With teams, a customer invites colleagues into a shared workspace. For single people, each person simply has a workspace of their own and never sees the word.
21
+ 2. **How does someone get in the first time?** Either they sign up and get a new workspace of their own, or they can only join by invitation from someone already inside.
22
+ 3. **When someone is invited, which role should they get?** See the **roles** guide: on a new app the default role is Owner, so an invitee without a role becomes an Owner.
23
+ 4. **Does the number of people per workspace depend on the plan?** ("1 user on Free, 10 on Pro.") If yes, that is a seat limit: see the **payments** guide.
24
+ 5. **Can one person belong to several workspaces?** Bridge supports it and shows a workspace picker at sign-in; the question is whether the developer wants to offer it in their product.
25
+
26
+ ## Decide yourself
27
+
28
+ - **Self-signup on for a product people try on their own; off for invitation-only products.** Take it from the answer to question 2 and set `tenantSelfSignup` to match. Reason: with it off, a stranger who signs up has nowhere to land.
29
+ - **Use Bridge's team panel for inviting and managing people** (`TeamManagementPanel` in SvelteKit) rather than a page of your own. Reason: it already handles invitations, role changes and removal against the right permissions.
30
+ - **Seats are the built-in `users` limit.** Reason: Bridge counts workspace members itself, so there is nothing for the app to count or report.
31
+ - **Never create workspaces from the app's own code for sign-ups.** Reason: Bridge creates one at signup when self-signup is on; creating them elsewhere makes duplicates.
32
+ - **Create workspaces by hand only for setup and support** (an existing customer you are migrating, or a demo).
33
+
34
+ ## Do it
35
+
36
+ | Step | MCP | CLI |
37
+ |---|---|---|
38
+ | Allow or stop self-signup | `update_app` (`tenantSelfSignup`) | `bridge app update --tenant-self-signup true` |
39
+ | Create a workspace by hand | `create_tenant` | `bridge tenant create --name … --owner-email …` |
40
+ | Invite someone into a workspace | `invite_user` | `bridge user invite --email … --role … --tenant-id …` |
41
+ | Change someone's role | `update_user` | `bridge user update --email … --role … --tenant-id …` |
42
+ | Limit seats per plan | `set_plan_quota` (metric `users`, kind `gauge`) | `bridge plan quota set pro --metric users --limit 10 --policy hard --kind gauge` |
43
+ | Code for the framework | `get_integration_guide` (topic `team`) | `bridge guide svelte team` |
44
+
45
+ Pass a role key on every invitation you make on the developer's behalf; leaving it out gives the app's default role.
46
+
47
+ ## Prove it
48
+
49
+ 1. Create a test user (`create_test_user` / `bridge test-user create`) and sign in (`verify_login` / `bridge test-user verify`): the response names the workspace.
50
+ 2. Invite a second address into that workspace and check it appears in `list_users` / `bridge user list` with the role you meant.
51
+
52
+ ## Where this connects
53
+
54
+ - **Roles:** what each person in a workspace may do.
55
+ - **Payments:** a plan belongs to the workspace, not to a person; seats are a plan limit.
56
+ - **Login:** whether sign-up is open at all.
@@ -0,0 +1,78 @@
1
+ {
2
+ "_comment": [
3
+ "The names of the Bridge MCP server's tools, and nothing else. The bundled guides are tested against this list: a guide that names a tool not in it fails src/__tests__/guide-decisions.test.ts.",
4
+ "Source: the `bridgeTools` registry in bridge-api, microservices/account/nebulr-api/mcp/tools/register.ts.",
5
+ "Generated, never edited by hand: in bridge-api run `bun scripts/generate-mcp-tools-json.ts > <bridge-cli>/bridge-cli/prompts/mcp-tools.json` (in the bridge-api container).",
6
+ "The count must equal the number of entries in `bridgeTools` (67 at the source below). bridge-api's guide-currency.spec.ts fails when this file and the registry disagree."
7
+ ],
8
+ "source": "bridge-api feature/mcp-journey 26792b4",
9
+ "tools": [
10
+ "add_redirect_uri",
11
+ "apply_plan",
12
+ "clear_flag_schedule",
13
+ "connect_stripe",
14
+ "create_api_token",
15
+ "create_app",
16
+ "create_feature_flag",
17
+ "create_plan",
18
+ "create_role",
19
+ "create_tenant",
20
+ "create_test_user",
21
+ "debug_auth_issue",
22
+ "delete_feature_flag",
23
+ "delete_tenant",
24
+ "diagnose_integration",
25
+ "evaluate_feature_flag",
26
+ "export_feature_flags",
27
+ "get_app",
28
+ "get_auth_config",
29
+ "get_branding",
30
+ "get_environment_info",
31
+ "get_flag",
32
+ "get_integration_guide",
33
+ "get_plan",
34
+ "get_role",
35
+ "get_server_info",
36
+ "get_setup_status",
37
+ "get_started",
38
+ "get_stripe_status",
39
+ "get_tenant",
40
+ "get_user",
41
+ "import_feature_flags",
42
+ "invite_user",
43
+ "list_api_tokens",
44
+ "list_apps",
45
+ "list_events",
46
+ "list_feature_flags",
47
+ "list_plan_quotas",
48
+ "list_plans",
49
+ "list_redirect_uris",
50
+ "list_roles",
51
+ "list_tenants",
52
+ "list_users",
53
+ "remove_plan_price",
54
+ "remove_plan_quota",
55
+ "remove_redirect_uri",
56
+ "remove_user",
57
+ "revoke_api_token",
58
+ "scaffold_flags_integration",
59
+ "set_flag_schedule",
60
+ "set_plan_price",
61
+ "set_plan_quota",
62
+ "setup_communication",
63
+ "setup_payments",
64
+ "setup_sso",
65
+ "toggle_feature_flag",
66
+ "update_app",
67
+ "update_auth_methods",
68
+ "update_branding",
69
+ "update_feature_flag",
70
+ "update_password_policy",
71
+ "update_plan",
72
+ "update_role",
73
+ "update_tenant",
74
+ "update_user",
75
+ "use_app",
76
+ "verify_login"
77
+ ]
78
+ }
@@ -0,0 +1,40 @@
1
+ # What Bridge does
2
+
3
+ Relay this to the developer as it stands, then add one sentence about what their app has today (read it first: `get_app` and `get_setup_status`, or `bridge app get` and `bridge setup status`).
4
+
5
+ ---
6
+
7
+ Bridge runs everything between signup and invoice for your app, so you don't build it yourself.
8
+
9
+ - **Login.** People sign up and sign in with a password, a magic link, a passkey, Google, GitHub and other providers, or their company's SSO, with optional two-factor. Bridge serves the pages; your app adds about fifteen lines. *Want to choose how people sign in?*
10
+ - **Teams and workspaces.** Each customer gets a workspace and can invite teammates into it. You decide whether people create their own workspace or join by invitation. *Is your app for single users or for teams?*
11
+ - **Roles and permissions.** Inside a workspace, roles decide who may do what, and your backend checks them on every request. *Who are the different kinds of people in a customer's workspace?*
12
+ - **Payments and plans.** Plans with prices, trials and limits ("3 projects on Free, 50 on Pro"), checkout through your own Stripe account, and an upgrade prompt when someone hits a limit. *Want to set up a free tier and a paid plan?*
13
+ - **Feature control.** Turn any feature on or off for everyone, a percentage of users, one plan or one customer, without a deploy. *Is there a feature you want to roll out carefully or sell on a higher plan?*
14
+ - **Look and feel.** Sign-in and subscription pages in your colours and fonts, inside your own layout, and emails sent in your app's name. *Want the sign-in pages to look like the rest of your app?*
15
+ - **Going live.** A readiness checklist that says exactly what still points at localhost or test keys, and a test sign-in that proves login works end to end. *Ready to check what stands between you and real users?*
16
+
17
+ ---
18
+
19
+ ## Where to go next
20
+
21
+ Each area has a decision guide: the questions to ask the developer, and the defaults to apply without asking.
22
+
23
+ | Area | Decision guide | Journey |
24
+ |---|---|---|
25
+ | Login | `bridge guide decision login` | `bridge guide add-login` |
26
+ | Teams and workspaces | `bridge guide decision teams` | `bridge guide add-teams` |
27
+ | Roles and permissions | `bridge guide decision roles` | — |
28
+ | Payments and plans | `bridge guide decision payments` | `bridge guide add-paid-plan` |
29
+ | Feature control | `bridge guide decision feature-control` | `bridge guide add-feature-flag` |
30
+ | Look and feel | `bridge guide decision look-and-feel` | — |
31
+ | Going live | `bridge guide decision going-live` | `bridge guide go-live` |
32
+
33
+ In an MCP client the same guides are the resources `bridge://guides/decisions/<area>` (areas: `login`, `teams`, `roles`, `payments`, `feature-control`, `look-and-feel`, `going-live`), and the journeys are the server's prompts of the same names.
34
+
35
+ How limits, upgrade prompts and page customisation actually work, for every area: `bridge guide mechanisms`.
36
+
37
+ Two rules hold in every guide:
38
+
39
+ 1. **Product questions go to the developer.** What to charge, who may do what, what a plan includes, what users see: ask, and wait for the answer. Never guess them.
40
+ 2. **Mechanics you decide.** Where a guide gives a default, apply it and say why in one line. The developer can overrule it.