Public Access
1
0

feat: introduce app-admin authorization and audit logging

- add migrations for owner/member workspace roles and application admins

- centralize /admin access checks with DB-backed admin resolution

- audit admin analytics/billing route access

- update account/admin UI typing and env/docs for ADMIN_EMAILS fallback behavior
This commit is contained in:
pguerrerox
2026-05-25 15:25:59 +00:00
parent 5508e15da1
commit f5e7e966e3
14 changed files with 269 additions and 302 deletions
+21 -259
View File
@@ -6,102 +6,29 @@
- [ ] Protect infrastructure with quotas, credits, throttling, and priority processing.
- [ ] Preserve room for future AI, enrichment, API, collaboration, and enterprise expansion.
## Open Questions
- [ ] What is the initial break-glass process if all app admins are disabled or misconfigured (seed command, SQL playbook, or deployment-time bootstrap)?
- [ ] Should app-admin permissions launch as full-access first, or start with explicit scopes (e.g., `billing.read`, `analytics.read`) from day one?
- [ ] Will research capacity be marketed as runs, credits, or both?
- [ ] Which collaboration/team features are truly launch-ready?
- [ ] Should workspace limits be hard-enforced at launch or soft-gated initially?
- [ ] Which add-ons launch on day one vs later?
- [ ] Is founder/LTD part of launch or a separate campaign?
- [ ] What exact enterprise triggers require custom sales instead of self-serve?
- [ ] Which plan data belongs in the canonical catalog versus presentation metadata?
## 1) Product & Marketing Alignment
- [x] Update product messaging to emphasize:
- local market intelligence
- geographic prospecting
- territory discovery
- operational prospecting workflows
- [x] Remove or reduce copy that frames the product as lead scraping or raw export tooling.
- [x] Define a concise plan-comparison narrative for Starter, Growth, Pro, and Enterprise.
- [x] Make Growth the obvious value anchor in pricing page design and copy.
- [ ] Decide whether to update historical/internal naming artifacts separately:
- `CHANGELOG.md` historical branding references
- `package.json` package name
## 2) Canonical Plan Definitions
- [x] Create a single source of truth for canonical plan definitions in code.
- [x] Keep the canonical catalog separate from presentation metadata:
- catalog = entitlements/commercial packaging data
- presentation = pricing-card copy, marketing bullets, comparison-table labels
- [x] Keep step `#2` scoped to catalog/type design and pricing-page integration only.
- [x] Define these initial SKUs:
- `starter_monthly`
- `growth_monthly`
- `pro_monthly`
- `enterprise_custom`
- [x] Add annual counterparts with 20% discount support.
- [x] Reserve type support for future founder/LTD SKUs without adding them to the active catalog yet.
- [x] Add explicit catalog identity fields for each plan:
- `tier`
- `billingInterval`
- `isSelfServe`
- `contactSalesRequired`
- [x] Include in each plan definition:
- pricing
- monthly usage limits
- workspace/user limits
- feature flags
- queue priority / processing tier
- add-on eligibility
- [x] Treat workspace/user limits as commercial allowances first, not guaranteed enforceable constraints yet.
- [x] Use customer-facing `researchRunsPerMonth` in the initial catalog and defer internal credit-ledger semantics to step `#3`.
- [x] Add lightweight helper accessors around the catalog, for example:
- `getPlanByCode`
- `getSelfServePlans`
- `isAnnualPlan`
- `getPlanDisplayMeta`
- [x] Expand shared billing/account types only enough to support future plan display:
- current plan code nullable
- billing interval nullable
- billing status/message
- no real subscription persistence yet
- [x] Add explicit listing semantics so public pricing visibility does not depend on billing interval.
- [x] Add plan family / sibling linkage to support future annual toggles, plan switching, and analytics rollups.
- [x] Reduce quantitative pricing bullet duplication by deriving core plan facts from structured catalog limits.
- [x] Encode internal feature readiness notes for marketed-but-not-yet-enforced capabilities.
- [ ] Follow-up recommendation: clarify whether `getPlanByCode` should stay active-catalog-only or be renamed to make reserved-code behavior explicit.
- [ ] Follow-up recommendation: revisit whether `planFamily` should remain separate from `tier` or be consolidated later.
- [ ] Follow-up recommendation: consider moving shared plan price/period formatting helpers into the billing domain once account and pricing UI expand.
- [ ] Follow-up recommendation: extend readiness modeling beyond feature flags if later steps need readiness for support, processing, or add-on availability.
## 3) Packaging & Entitlement Model
- [x] Decide the internal usage model:
- plan-based research runs, or
- credit ledger with variable credit consumption per action
- [x] Recommended default: use a credit system internally and simpler plan language externally.
- [x] Keep the public catalog and pricing page centered on plan allowances, not internal billing mechanics.
- [x] Define the research credit schedule, for example:
- small local search = 1 credit
- multi-radius query = 3-5 credits
- enriched search = 10 credits
- [x] Define export limits by plan:
- Starter = 2,500/month
- Growth = 15,000/month
- Pro = 75,000/month
- [x] Define what happens at limit exhaustion:
- block
- upgrade prompt
- add-on purchase path
- enterprise/contact sales path
- [x] Implement a shared entitlement policy layer with:
- usage resources/actions
- plan-to-allowance translation helpers
- action cost estimation helpers
- pure entitlement decision helpers
- [x] Separate capability gating from allowance checks in the entitlement layer.
- [x] Add explicit allowance semantics so `null` does not silently mean allowed/unlimited.
- [x] Add canonical action policy definitions for:
- `basic_search_run`
- `deep_research_preview`
- `deep_research_batch_run`
- `csv_export`
- future `enrichment_run`
- future `api_request`
- [x] Keep step `#3` policy-only:
- no DB persistence yet
- no route enforcement yet
- no billing-provider integration yet
- [ ] Future note: `evaluateActionEntitlement()` is policy-only and later steps must provide real remaining-usage inputs from subscription/account state.
- [ ] Future note: missing readiness metadata currently implies `launch_ready`; keep readiness annotations current as new gated features are added.
- [ ] Future note: `api_requests` and `enrichments` are modeled ahead of full product implementation; do not treat them as launch-ready by default.
@@ -111,79 +38,12 @@
- [ ] Future note: `territoryMapping` currently carries deep-research capability semantics and may need a dedicated capability later if gating becomes more granular.
## 4) Feature Gates by Plan
- [x] Implement a shared feature-gate interpreter layer that resolves feature state by plan using:
- plan feature flags
- feature readiness metadata
- self-serve vs enterprise upgrade paths
- [x] Starter
- [x] CSV export
- [x] map search
- [x] radius search
- [x] basic filters
- [x] exclude automations
- [x] exclude API access
- [x] exclude enrichments
- [x] exclude CRM integrations
- [x] exclude collaboration
- [x] Growth
- [x] saved searches
- [x] territory mapping
- [x] advanced filtering
- [x] deduplication
- [x] export history
- [x] tagging & notes
- [x] faster processing
- [x] priority support
- [x] Pro
- [x] shared lists
- [x] scheduled research
- [x] bulk exports
- [x] CRM integrations
- [x] webhooks/API
- [x] enrichment credits
- [x] collaboration features
- [x] Enterprise
- [x] pooled or custom usage
- [x] SSO
- [x] SLA
- [x] white-labeling
- [x] onboarding / account management
- [x] dedicated infrastructure options
- [x] custom integrations
- [x] Align feature-gate interpretation with entitlement action mappings in shared code.
- [x] Keep step `#4` shared-policy only:
- no broad UI rollout yet
- no backend route enforcement yet
- no usage-ledger coupling yet
- [ ] Future note: make upgrade recommendations readiness-aware so users are not prompted to upgrade into tiers where the target feature is still `coming_soon`.
- [ ] Future note: consolidate action ↔ feature mapping into one canonical source shared by `entitlements.ts` and `feature-gates.ts` to avoid drift between UI gating and backend action policy.
- [ ] Future note: for Enterprise plans, included-but-not-ready features should usually resolve to `coming_soon` instead of `contact_sales`.
- [ ] Future note: revisit the fallback `coming_soon` state for unavailable or unmapped features before broad UI rollout so hidden vs upgrade vs future behavior stays intentional.
## 5) Billing & Data Model Design
- [x] Design subscription/account state separately from the canonical plan catalog.
- [x] Keep billing-provider identifiers out of the canonical catalog until payments integration work begins.
- [x] Design subscription state storage for current plan, billing interval, and status.
- [x] Design a monthly usage ledger for:
- research credits/runs
- exports
- enrichments
- API usage (future)
- [x] Design add-on purchases and remaining balances.
- [x] Define renewal/reset behavior for monthly quotas.
- [x] Define annual billing behavior and renewal terms.
- [x] Define LTD handling with monthly quotas and non-unlimited usage.
- [x] Implement workspace-scoped billing foundation storage for:
- billing accounts
- usage periods
- usage counters
- add-on purchases
- add-on balances
- [x] Add billing repository/service layers and minimal account-data integration.
- [x] Keep step `#5` foundation-only:
- no Stripe/webhook integration yet
- no route enforcement yet
- no full billing UI rollout yet
- [ ] Future note: `remaining = 0` for `not_available` resources is intentional and should stay aligned with entitlement semantics.
- [ ] Future note: expired billing periods should fail closed for current usage-window resolution until subscription lifecycle automation can advance billing periods reliably.
- [ ] Future note: consider exposing `usagePeriodId` later if enforcement, debugging, or admin tooling needs period-level traceability.
@@ -192,126 +52,42 @@
- [ ] Future note: usage ownership is workspace-scoped in storage, but current operational enforcement is still catching up to that model.
## 6) Enforcement Architecture
- [x] Create a centralized entitlement/usage policy service on the backend.
- [x] Ensure all high-cost actions check entitlements before execution.
- [x] Start with enforcement on:
- [x] research routes
- [x] Bootstrap new and existing workspaces into a usable pre-payments Starter billing state so enforcement does not hard-block all chargeable actions before subscriptions exist.
- [x] Reuse a shared deep-research estimate path so entitlement cost estimation and batch creation use the same preview-derived basis.
- [x] Add clear API responses for quota exhaustion and upgrade flows.
- [ ] Future note: the current enforcement slice should be treated as the core entitlement/runtime gate, not the full operational control layer.
- [ ] Future note: the default Starter bootstrap is a pre-payments usability policy and should be revisited when real subscription lifecycle automation is implemented.
## 7) Workspace, User, and Collaboration Readiness
- [x] Review whether current data ownership is sufficiently workspace-scoped for plan promises.
- [x] Identify gaps between current user-scoped data model and promised team/workspace packaging.
- [x] Document which catalog limits can be enforced immediately versus only represented commercially at launch.
- [x] Define how to enforce:
- [x] users included
- [x] workspace limits
- [x] shared assets/lists
- [x] collaboration permissions
- [x] Decide whether some collaboration features need phased rollout rather than immediate sale.
- [x] Add a shared workspace-readiness matrix covering:
- current ownership scope
- target ownership scope
- enforceability state
- collaboration phase definitions
- [x] Document the current collaboration phase as workspace billing with mostly personal data ownership.
- [x] Define the migration target for workspace ownership of:
- `search_jobs`
- `deep_research_batches`
- `businesses`
- `search_job_results`
- [ ] Future note: before true collaboration is sold as real runtime behavior, core domain entities need `workspace_id` ownership and repository/query updates.
- [ ] Future note: users included and workspace limits should remain soft-gated until multi-workspace UX and shared data ownership mature.
- [ ] Future note: shared lists, tagging/notes, and collaboration permissions should not be treated as hard-enforceable features until the workspace migration is complete.
## 8) Pricing Page & Account UX
- [x] Build pricing page from canonical plan definitions instead of hardcoded copy.
- [x] Derive pricing-card and comparison-table content from presentation metadata layered on top of the canonical catalog.
- [x] Add plan comparison table.
- [x] Add annual/monthly toggle.
- [x] Add upgrade CTAs and contact-sales CTA.
- [x] Add account/billing page showing:
- [x] current plan
- [x] billing interval
- [x] usage this month
- [x] remaining quota
- [x] available add-ons
- [x] upgrade options
- [x] Add quota warning UX before hard exhaustion.
- [x] Keep migration-dependent collaboration messaging honest by surfacing included-but-not-ready capabilities as `Coming soon` instead of pretending they are fully live.
- [ ] Future note: the pricing comparison table should stay aligned with workspace-readiness decisions as collaboration and shared asset features move toward workspace ownership.
- [ ] Future note: upgrade CTAs are present, but actual checkout/subscription management should remain tied to the post-payments hardening step.
- [ ] Future note: pricing and account UX should keep users included, workspace limits, and collaboration-adjacent promises explicitly soft-gated until workspace-owned shared data and hard enforcement are ready.
- [ ] Future note: replace placeholder upgrade CTAs in the account billing UI with a real upgrade path, pricing-page jump, contact-sales flow, or explicit `coming soon` behavior before broader rollout.
## 9) Add-On Strategy
- [x] Define export add-ons:
- +10k exports = $29
- +50k exports = $99
- [x] Define enrichment packs:
- 1,000 enrichments = $49
- [x] Reserve future add-ons for:
- AI prospecting assistant
- white-label / agency tools
- higher API capacity
- [x] Decide whether add-ons are one-time, monthly recurring, or both.
- [ ] Future note: launch active add-ons should stay limited to one-time export packs until enrichment delivery and payments lifecycle handling are live.
- [ ] Future note: recurring feature add-ons should not be sold until the underlying capabilities and subscription management flows exist end-to-end.
## 10) Payments Integration
- [x] Choose billing provider (Stripe).
- [x] Map internal SKUs to external billing products/prices.
- [x] Support subscriptions, annual billing, add-ons, and enterprise/manual invoicing.
- [x] Define webhook handling for subscription state changes.
- [ ] Future note: Stripe is now the active integration path; keep the internal plan/add-on catalog as the canonical packaging source and treat Stripe price IDs as environment-specific mappings.
- [ ] Future note: the current customer-facing integration supports self-serve subscriptions, export-pack checkout, and the Stripe billing portal, while enterprise invoicing remains a manual sales workflow.
- [ ] Future note: webhook idempotency currently relies on the `billing_webhook_events` store; keep Stripe event processing centralized there as billing lifecycle coverage expands.
- [ ] Future note: post-payments hardening should tighten downgrade, cancellation, retry, and grace-period policy before broad rollout so Stripe portal actions and runtime entitlements stay aligned.
## 11) Post-Payments Hardening & Admin Visibility
- [x] Application admin model: define app-wide `admin` as a separate identity domain from workspace memberships (`owner`/`member`).
- [x] Implement DB-backed app-admin identities as the primary source of truth (active/disabled status, normalized email principal, optional scoped permissions, audit fields).
- [x] Add migration path for current internal admin access: keep temporary env fallback only during rollout, then remove once DB-seeded admins are verified.
- [x] Centralize admin authorization middleware (`requireAdmin`) and replace route-local billing-admin checks so `/admin/*` authorization semantics are consistent.
- [x] Add admin audit visibility: log admin route access and key admin support actions with actor, route/action, target workspace, and timestamp.
- [ ] Define explicit downgrade behavior:
- effective timing for scheduled vs immediate plan changes
- entitlement/usage treatment when the target plan is below current usage
- account messaging for pending downgrade state
- [x] Define explicit cancellation behavior:
- end-of-period vs immediate access policy
- post-cancellation account state and reactivation path
- handling for active add-on balances and usage windows
- [x] Define explicit payment retry and grace-period behavior:
- which Stripe states map to degraded vs blocked access
- grace-period duration and user-facing messaging
- when usage actions should warn, soft-block, or hard-block
- [x] Wire pricing-page CTAs to real billing actions:
- self-serve paid plans -> Stripe checkout
- active paid workspaces -> plan-change or billing-portal path
- enterprise plans -> contact-sales path
- [x] Add account redirect notices after Stripe return:
- successful checkout/portal return confirmation
- canceled or incomplete checkout messaging
- failed or unresolved billing-return guidance
- [x] Expand internal admin billing visibility for operational maintenance and debugging:
- show workspace billing summary with current plan, interval, status, renewal date, cancel-at-period-end, and trial/grace-period state
- show current usage period and counters for research, exports, add-ons, and remaining balances
- show recent Stripe/customer/subscription identifiers and latest webhook processing outcomes
- show recent billing timeline entries for checkout, subscription changes, payment failures, cancellations, and add-on purchases
- flag workspaces with stale billing sync, failed webhook processing, or status mismatches needing support follow-up
- document the minimum admin view(s) needed so support can verify billing state without direct database inspection
## 12) Analytics, Ops, and Revenue Instrumentation
- [x] Track pricing-page conversion by plan.
- [x] Track quota exhaustion events.
- [x] Track upgrade triggers:
- export limit hit
- research limit hit
- feature-gate encounter
- [x] Track add-on attach rate.
- [x] Track plan mix, churn, expansion revenue, and annual conversion.
- [x] Add internal dashboards for billing and usage health.
## 13) Operational Enforcement Follow-Up
## 12) [DEFER] Operational Enforcement Follow-Up
- [ ] Add queue prioritization by plan tier.
- [ ] Add throttling/fair-usage controls.
- [ ] Add export-route enforcement once CSV/export generation moves to a backend endpoint.
@@ -321,7 +97,7 @@
- [ ] Future note: export enforcement remains deferred until CSV/export generation moves to a backend endpoint.
- [ ] Future note: enrichment-route enforcement remains deferred until enrichment actions/routes are implemented.
## 14) Founder / LTD Strategy
## 13) [DEFER] Founder / LTD Strategy
- [ ] Decide whether to launch founder LTD at all.
- [ ] If yes, define strict quantity cap (e.g. first 100-250 customers).
- [ ] Define founder SKUs:
@@ -330,10 +106,12 @@
- [ ] Ensure founder plans have monthly quotas and exclude unlimited compute/API.
- [ ] Define which future features are excluded from LTD plans.
## 15) Rollout Plan
## 14) Rollout Plan
- [ ] Phase 1: finalize canonical plan definitions, presentation metadata boundaries, and entitlement model.
- [ ] Phase 2: implement usage ledger and backend enforcement.
- [ ] Phase 3: review workspace, user, and collaboration readiness before expanding team/workspace promises.
- [ ] Phase 3a: execute workspace-role migration (`owner`/`member` only), convert legacy workspace admins to members by default, and validate owner-only management paths.
- [ ] Phase 3b: launch DB-backed app-admin identity management and migrate `/admin/*` authorization to centralized app-admin middleware.
- [ ] Phase 4: update pricing page and account/billing UI based on the workspace/collaboration readiness decisions.
- [ ] Phase 5: finalize add-on strategy before wiring payment products.
- [ ] Phase 6: integrate payments and subscription lifecycle handling.
@@ -342,19 +120,3 @@
- [ ] Phase 9: launch collaboration, API, enrichment, and enterprise features as architecture matures.
- [ ] Phase 10: complete deferred operational enforcement work such as queue prioritization, throttling, and backend export enforcement when runtime scale justifies it.
- [ ] Phase 11: decide and implement founder/LTD strategy only after the app/site, billing lifecycle, admin/support visibility, analytics, and broader product maturity work are in place.
## Recommended Execution Order
- [ ] Next: `#11 Post-Payments Hardening & Admin Visibility`
- [ ] Then: `#13 Analytics, Ops, and Revenue Instrumentation`
- [ ] Then: collaboration, API, enrichment, and enterprise feature maturation from `#15 Rollout Plan`
- [ ] Keep `#14 Operational Enforcement Follow-Up` deferred until async worker routing, backend exports, or higher-volume execution patterns make it necessary.
- [ ] Last: `#12 Founder / LTD Strategy` once the app/site, billing lifecycle, admin/support visibility, analytics, and broader product maturity work are in place.
## Open Questions
- [ ] Will research capacity be marketed as runs, credits, or both?
- [ ] Which collaboration/team features are truly launch-ready?
- [ ] Should workspace limits be hard-enforced at launch or soft-gated initially?
- [ ] Which add-ons launch on day one vs later?
- [ ] Is founder/LTD part of launch or a separate campaign?
- [ ] What exact enterprise triggers require custom sales instead of self-serve?
- [ ] Which plan data belongs in the canonical catalog versus presentation metadata?