B2BEA.org V1 Entitlement Model Spec
internal prototype · canonical JSON + Dreamborn Forge HTML
internal generated
design_doc · markdown

B2BEA.org V1 Entitlement Model Spec

B2BEA.org V1 Entitlement Model Spec Source of record: RedKey Supabase Studio artifact. Project: B2BEA.org Rebuild Project ID: a820dd0c 6cef 4133 bfbd d802fd806e44 Artifact: entitlement model spec Artifact ID: 355b3249 3af9 45a4 9c45 67777bd2d72d Version: 1 Status: draft Updated: 2026 05 06T23:32:25.756783+00:00 Live Supabase Check Checked against the active ...

B2BEA.org V1 Entitlement Model Spec

Source of record: RedKey Supabase Studio artifact.

  • Project: B2BEA.org Rebuild
  • Project ID: a820dd0c-6cef-4133-bfbd-d802fd806e44
  • Artifact: entitlement-model-spec
  • Artifact ID: 355b3249-3af9-45a4-9c45-67777bd2d72d
  • Version: 1
  • Status: draft
  • Updated: 2026-05-06T23:32:25.756783+00:00
Live Supabase Check

Checked against the active B2BEA Supabase project and current B2BEA-org-new code signals.

  • Supabase host: czqxkykbhoyyjccckpqq.supabase.co
  • Source repo: /Users/justinking/Vaults/Projects/B2BEA-org-new
  • Tables checked: people, person_roles, membership_tiers, memberships, membership_seats, vendor_memberships, organization_members, courses, course_enrollments, events, content_embeddings

Current entitlement-related row counts:

| Table | Rows | Notes | |---|---:|---| | membership_tiers | 3 | Seed tiers: Registered, Pro, Vendor. | | memberships | 1 | One active Pro membership. | | membership_seats | 0 | Seat assignment base exists but is unused. | | vendor_memberships | 0 | Vendor commercial/access layer exists but is unused. | | organization_members | 0 | Company workspace membership base exists but is unused. | | courses | 9 | Course catalog has mixed access/pricing fields. | | course_enrollments | 4 | Current learner participation records. | | person_roles | 8 | Role grants include Pro/vendor/admin-style roles. |

Current tiers:

| Tier | Category | Billing | Seat Model | Access Summary | |---|---|---|---|---| | Registered | practitioner | free | individual | Baseline account/profile access; no premium courses, reports, events, or vendor portal. | | Pro | practitioner | subscription | individual | All courses, reports, and events; no vendor portal; no enhanced directory. | | Vendor | vendor | invoice | company | Vendor portal write access and enhanced directory; no practitioner course/report/event grants by default. |

Code signals:

  • src/assets/js/authbridge.js still checks the legacy persons table when evaluating Pro access.
  • Reports and guides call client-side authbridge.checkProAccess() for Pro gating.
  • Courses combine courses.access_tier, courses.is_included_with_pro, pricing fields, and course_enrollments.
  • Admin user tooling edits people.is_pro and person_roles, which can drift from active memberships.
  • membership_tiers.access_rules exists but is not yet the single enforced entitlement contract.
Decisions

| ID | Topic | Decision | |---|---|---| | ENT-DEC-001 | Canonical entitlement truth | Active memberships, membership_tiers.access_rules, and membership_seats are the canonical source for paid/product access. Roles are permissions and operational responsibility, not paid entitlement truth. | | ENT-DEC-002 | people.is_pro | people.is_pro may remain as a denormalized display/cache flag during migration, but it must not be treated as authoritative access truth. | | ENT-DEC-003 | Server-side enforcement | Protected reads, protected downloads, enrollments, mutations, exports, and portal actions require a server-side entitlement check. Client-side checks may only improve UX. | | ENT-DEC-004 | Company workspace | Practitioner company workspace uses organization-held memberships plus membership_seats for employee access to academy/careers benefits. Company public profiles are excluded from V1. | | ENT-DEC-005 | Vendor portal | Vendor portal access requires a vendor relationship role and an active/approved vendor commercial entitlement when commercial gating is required. Vendor public profiles remain V1. | | ENT-DEC-006 | Course enrollment | course_enrollments repre

Target Principles
  • Every protected capability resolves through one entitlement evaluator with auditable inputs and deterministic outputs.
  • Entitlement decisions are explicit: subject, action, resource, source, status, start time, end time, and reason.
  • Roles grant authority to administer or act on behalf of an account; memberships and seats grant product access.
  • A user can hold multiple contexts at once: individual Pro, vendor admin, company admin, employee seat, and internal platform admin.
  • Entitlement state should be explainable in the UI for support: why access exists, where it came from, when it expires, and who assigned it.
Entitlement Subjects

| Subject | Examples | Current Sources | |---|---|---| | Person | Registered account, Pro member, course learner, company employee seat, vendor team member, platform admin | people, person_roles, memberships.held_by_person_id, membership_seats.assigned_person_id, course_enrollments | | Organization | Private practitioner company workspace, company academy/careers account | organizations, organization_members, memberships.held_by_org_id, membership_seats | | Vendor | Public vendor profile, vendor management portal account, enhanced directory listing | vendors, vendor_memberships, person_roles, vendor_* portal tables |

Entitlement Keys

| Key | Area | Grants | |---|---|---| | account.registered | Identity | Baseline authenticated account and profile access. | | membership.pro | Membership | Individual Pro status and Pro-only product eligibility. | | resource.report.read.pro | Resources | Read/download Pro reports and guides. | | academy.course.enroll.included | Academy | Enroll in courses included with the member tier or company seat. | | academy.course.purchase | Academy | Enroll in a course through a direct purchase/admin grant path. | | academy.certification.earn | Academy | Attempt certification requirements and receive certificates. | | event.register.member | Events | Register for member-included or member-priced events. | | vendor.portal.read | Vendor | View vendor portal dashboard and own profile/submission state. | | vendor.portal.write | Vendor | Submit vendor profile edits, case studies, media, and portal updates. | | vendor.analytics.export | Vendor | Export bounded own-vendor analytics. | | company.workspace.read | Company | Access private company workspace. | | company.workspace.admin | Company | Manage company users, jobs, academy assignments, and workspace settings. | | `com

Target Data Model

Use and harden the existing tables:

| Table | Role | |---|---| | membership_tiers | Defines tier category, seat model, billing model, and structured access_rules. | | memberships | Canonical commercial/product entitlement holder for person-held, organization-held, and vendor-held memberships. | | membership_seats | Seat assignment from an org/vendor/company membership to an individual person. | | person_roles | Operational authority and relationship roles such as platform_admin, vendor_admin, company_admin, author. | | course_enrollments | Learning participation/progress after eligibility is granted. |

Add or harden:

| Table | Purpose | Core Fields | |---|---|---| | entitlement_grants | Optional normalized/materialized grant table for fast and auditable access decisions. | id, subject_type, subject_id, entitlement_key, source_type, source_id, status, starts_at, ends_at, metadata, timestamps | | entitlement_audit_events | Audit log for grants, revocations, seat assignments, admin overrides, purchases, cancellations, and policy denials. | id, actor_person_id, subject_type, subject_id, entitlement_key, event_type, source_type, source_id, reason, metadata, created_at | | course_assignments | Company/admin course assignment layer separate from course participation. | id, course_id, assigned_to_person_id, assigned_by_person_id, organization_id, source_type, status, due_at, timestamps | | entitlement_policy_tests | Machine-readable policy fixtures for production gating regressions. | id, scenario_key, subject_fixture, resource_fixture, expected_decision, created_at |

Enforcement Contract

Input shape:

| Field | Meaning | |---|---| | subject | person_id plus active organization/vendor context when applicable. | | action | Named capability/action key such as academy.course.enroll or vendor.portal.write. | | resource | Resource type and ID when relevant, such as course_id, vendor_id, report_id, organization_id. | | context | Request metadata, impersonation/admin mode, source surface, and time. |

Output shape:

| Field | Meaning | |---|---| | allowed | Boolean decision. | | entitlement_key | Resolved grant key or required key. | | reason_code | Machine-readable allow/deny reason. | | source_refs | Membership, role, seat, purchase, admin grant, or policy refs used for the decision. | | expires_at | Expiration timestamp when access is time-bound. |

Hard rules:

  • No protected mutation trusts only localStorage, client profile state, or people.is_pro.
  • RLS policies and server routes/functions must agree on the same entitlement keys.
  • Admin override must be logged with actor, subject, reason, and expiration when temporary.
  • Exports must be filtered by own vendor/company context before data leaves Supabase.
V1 Access Matrix

| Area | Capability | Required Access | Source | |---|---|---|---| | Public site | Read public content/vendor profiles/jobs/events | Anonymous/public unless explicitly gated | Sanity + Supabase public projections | | Member profile | Update own profile | account.registered + owns profile | people + auth user | | Member profile | Change password/account settings | Authenticated own account | Auth provider + people | | Resources | Read/download Pro report or guide | resource.report.read.pro or admin override | Memberships + tier rules + protected resource metadata | | Academy | Enroll included course | academy.course.enroll.included or course purchase/admin grant | Membership tier, seat, purchase/grant, course policy | | Academy | Continue enrolled course | Active course_enrollments row and not revoked | course_enrollments + entitlement status | | Academy | Create/manage course | admin.platform.manage or academy admin role | person_roles/platform_admins | | Vendor portal | Update vendor profile | vendor.portal.write + vendor relationship | Vendor entitlement + vendor ownership role | | Vendor portal | Create case study/content submission | vendor.portal.write

Gaps

| ID | Severity | Area | Current | Required | |---|---:|---|---|---| | ENT-GAP-001 | P0 | Legacy identity table | authbridge.js checks persons for Pro access while live schema uses people. | Replace persons usage with people or create/test an intentional compatibility view before launch. | | ENT-GAP-002 | P0 | Fragmented Pro truth | people.is_pro, person_roles.pro_member, active memberships, and course fields can disagree. | Centralize Pro decision logic and backfill/derive display fields from canonical entitlement state. | | ENT-GAP-003 | P0 | Client-side gating | Some reports/guides/course paths rely on client authbridge checks. | Server-side checks for protected reads/downloads/enrollments/mutations with RLS/function parity. | | ENT-GAP-004 | P0 | Course access consistency | Courses have price fields, member prices, access_tier values, and is_included_with_pro flags that are not normalized. | Define a course access policy contract and migrate course rows to consistent values. | | ENT-GAP-005 | P0 | Company seats | membership_seats and organization_members exist but are empty. | Implement org-held membership, employee role, seat assignment, revoc

Migration Sequence

1. Inventory and remove/replace all persons table references in production code. 2. Define entitlement key enum/constants shared by server functions, RLS, and UI display. 3. Normalize membership_tiers.access_rules into versioned machine-readable rules. 4. Implement entitlement evaluator and policy test fixtures before wiring new protected features. 5. Backfill current Pro member state from memberships and reconcile people.is_pro/person_roles drift. 6. Normalize course access fields and write course enrollment eligibility tests. 7. Wire vendor portal and company workspace gates to memberships/seats/roles. 8. Add entitlement audit events and support/admin visibility. 9. Add survey/company/vendor export entitlement checks as those modules are built.

Acceptance Criteria
  • A developer can answer "why does this person have access?" from one entitlement decision path.
  • Protected resources cannot be downloaded by editing client state or bypassing the UI.
  • Pro, vendor, company, and admin access are separately testable and can coexist on one person account.
  • Seat assignment and revocation changes access immediately or within a documented cache window.
  • All entitlement-changing operations create an audit event.
  • The old persons-table access path is gone or intentionally shimmed and covered by tests.