Skip to content

license: soft-warn cap policy + /usage endpoint + UsageBanner#239

Merged
keysersoft merged 1 commit into
mainfrom
feat/add-team-tier
May 21, 2026
Merged

license: soft-warn cap policy + /usage endpoint + UsageBanner#239
keysersoft merged 1 commit into
mainfrom
feat/add-team-tier

Conversation

@keysersoft
Copy link
Copy Markdown
Contributor

Why

Today connector / MCP-server caps on paid tiers return 403. With the upcoming pricing restructure (Starter 1u / 5 conn, Team 3u / 15 conn, Business 10u / ∞) we need caps to act as upgrade nudges, not hard blocks. User decision: "soft warn — backend mostra un upgrade nudge dopo N connectors ma non blocca."

Trial keeps a hard 403 (sales tool, abuse risk).

What

  • `GET /api/license/usage` returns `{ plan, connectors{current,max,isOver}, mcpServers{...}, users{...}, isOverAny }` (joins Prisma counts with the existing `license.features` payload).
  • `LicenseGuardService` no longer throws on paid plans — `checkCanCreateConnector` / `checkCanCreateMcpServer` only block trial.
  • `UsageBanner` (new client component) — amber banner above the dashboard with a "Upgrade to Team / Business" CTA when any axis is over its cap. Dismissible per session, utm-tagged link.

Contract

The website's `lib/license.ts` carries the cap values in the license `features` payload — Starter 5/3/1, Team 15/10/3, Business ∞/∞/10. Backend doesn't enforce, only reports.

Plan string "team" is silently accepted (column type is String, no enum migration).

Test plan

  • `/api/license/usage` returns the right shape (try as trial, starter, team)
  • Self-hosted starter at 6 connectors: no 403, banner appears
  • Self-hosted starter, dismiss banner: gone for the session
  • Trial at 3 connectors: still 403
  • Banner doesn't show on Business / Enterprise (NEXT_TIER guard)

Adds a Team plan-shaped quota model and switches paid-tier connector /
MCP-server caps from hard 403 to a soft upgrade nudge. Trial still has
a hard cap (it's a sales tool, abuse risk).

New surface:
- GET /api/license/usage → { plan, connectors{current,max,isOver},
  mcpServers{...}, users{...}, isOverAny }
- frontend UsageBanner mounted in app/layout.tsx, fetches /usage,
  renders an amber banner above the dashboard when any axis is over,
  with a localized "Upgrade to {next-tier}" CTA + utm-tagged pricing
  link. Dismissible per session.

The website's license.features map already carries maxConnectors,
maxMcpServers and maxUsers — no new contract bytes needed, just a
new endpoint that joins those with Prisma counts.

Backend changes:
- LicenseGuardService.checkCanCreateConnector / -McpServer:
  only block when license.plan === 'trial'. Paid tiers go through
  silently — the banner does the work.
- New LicenseGuardService.getUsage(userId, organizationId): joins
  Prisma counts (connector, mcpServerConfig, user) with the
  features payload to return UsageCap[]s.
- LicenseController: new @get('/usage') with optional JWT auth,
  same shape as /status — anonymous self-hosted single-user instances
  still get their global counts.

Frontend changes:
- New UsageBanner client component (similar shape to TrialBanner)
- license.getUsage() added to lib/api.ts
- Mounted in app/layout.tsx right after TrialBanner

Compatible with the website-side plan: lib/license.ts carries Starter
caps 5/3, Team 15/10, Business unlimited; backend doesn't enforce
these values, only reports them.
@keysersoft keysersoft requested a review from mirkopoloni as a code owner May 21, 2026 13:51
@keysersoft keysersoft merged commit d237dad into main May 21, 2026
11 checks passed
@keysersoft keysersoft deleted the feat/add-team-tier branch May 21, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant