Skip to content

SPEC-COMMONPLACE-OPS-CONSOLE-1.0: tenant, billing, and usage register in apps/console #172

Description

@Travis-Gilbert

SPEC-COMMONPLACE-OPS-CONSOLE-1.0

An operations register in apps/console: one surface that answers who is paying, what they are entitled to, what they are using, and what is happening, for every tenant across Drifty and the harness. North-star register; donor bindings follow SPEC-COMMONPLACE-TWENTY-UI-FORK-1.0 and SPEC-UI-COMPONENT-SOURCING-AND-RESKIN.

The one architectural rule

The console reads Postgres, never Stripe. theorem-control-plane already lands the truth: iam.tenants, billing.stripe_customers, billing.subscriptions, billing.entitlements, billing.stripe_webhook_events, plus the usage tables. The console is a read-only projection over one join, served through the control plane with the caller's ResolvedIdentity (admin scopes), so RLS applies to the operator the same as everyone else. Stripe is drill-in only: stripe_customer_tenant_slug maps both directions, and the tenant slug lands in Stripe Customer metadata so the Stripe dashboard is navigable by tenant too.

Surfaces

  • Tenant roster: slug, plan, subscription status, current period end, MRR contribution, usage against each entitlement. Twenty records primitives; a tenant is a record, this is the records view.
  • Tenant detail: entitlements with source (plan vs override), usage meters, recent webhook events, Drifty installation state where one exists, link out to the Stripe customer.
  • Revenue rollup: MRR, plan mix, period-over-period movement, derived from billing.subscriptions where status is active or trialing. Chart bindings come from the console's registered component set per the sourcing spec regimes; no new chart dependency is introduced by this spec.
  • Entitlement override editor: the one write surface. Writes go through the control plane (source = 'override', which plan syncs already respect), never to Stripe.
  • Event log: stripe_webhook_events and usage events, filterable by tenant, with processed/duplicate/stale outcome visible. This is the debugging surface for billing disputes.

Deliverables

  • Control plane: one ops projection query (the join above) exposed on the existing serving surface, admin-scoped.
  • apps/console ops register route with the five surfaces, bound to Twenty donor components per the fork spec.
  • Stripe Customer metadata write: tenant slug stamped at customer creation in the existing upsert_customer path.
  • Drifty installation panel fed from the Drifty tenant provisioning table (SPEC-DRIFTY-1.0).

Acceptance

  • A Drifty subscription created in Stripe test mode appears in the roster with plan, entitlements, and usage within one webhook delivery, with no console-side Stripe API call involved.
  • Flipping an entitlement to override in the console survives a subsequent plan sync (the billing.rs override-preservation path observed working from the UI).
  • The event log shows a deliberately replayed Stripe event as duplicate.
  • An operator identity without admin scopes gets an empty roster, not an error, proving RLS holds on the ops surface itself.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions