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 ...

Agent Handoff
Start Here

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 ...

Completion Evidence

No explicit evidence field yet. Require tests, screenshots, linked PRs, or reviewed outputs before marking complete.

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.