B2BEA.org V1 Entitlement Model Spec
entitlement-model-spec artifact · for B2BEA.org Rebuild · status approved
entitlement-model-spec artifact · for B2BEA.org Rebuild · status approved
- 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.
- gaps: 9 items
- title: B2BEA.org V1 Entitlement Model Spec
- version: 1
- decisions: 7 items
- next specs: 5 items
- project id: a820dd0c-6cef-4133-bfbd-d802fd806e44
- updated at: 2026-05-06T23:32:25.356Z
- source repo: name: string, local_path: string, live_supabase_host: string
- artifact type: entitlement-model-spec
- entitlement keys: 20 items
- source artifacts: 5 items
- v1 access matrix: 17 items
Machine-readable source fields
| id | area | current | required | severity |
|---|---|---|---|---|
| ENT-GAP-001 | 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. | P0 |
| ENT-GAP-002 | 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. | P0 |
| ENT-GAP-003 | 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. | P0 |
| ENT-GAP-004 | 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. | P0 |
| ENT-GAP-005 | Company seats | membership_seats and organization_members exist but are empty. | Implement org-held membership, employee role, seat assignment, revocation, and academy/careers benefit checks. | P0 |
| ENT-GAP-006 | Vendor membership | vendor_memberships is empty and not clearly tied to portal access. | Choose canonical vendor commercial source and wire vendor portal read/write/analytics checks to it. | P0 |
| ENT-GAP-007 | Auditability | No central entitlement audit event model observed. | Record grants, revocations, admin overrides, seat changes, purchases/cancellations, and denials. | P1 |
| ENT-GAP-008 | Surveys/forms | Generalized survey/form capability is missing. | New survey and form models must declare entitlement keys before implementation. | P1 |
| ENT-GAP-009 | Exports | Analytics tables exist but export entitlement rules are not centralized. | Implement own-account bounded export checks for vendors and companies. | P1 |
B2BEA.org V1 Entitlement Model Spec
1
| 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 represents participation/progress after eligibility is granted. Eligibility to enroll comes from entitlement policy, purchase, admin grant, or company seat assignment. |
| ENT-DEC-007 | HubSpot boundary | HubSpot remains CRM primary for sales and renewals. B2BEA Supabase stores product entitlements and operational access state; HubSpot can mirror status but does not enforce site access. |
- company-workspace-data-spec
- survey-system-spec
- notification-spec
- academy-certification-spec
- vendor-portal-workflow-spec
a820dd0c-6cef-4133-bfbd-d802fd806e44
2026-05-06T23:32:25.356Z
B2BEA-org-new
/Users/justinking/Vaults/Projects/B2BEA-org-new
czqxkykbhoyyjccckpqq.supabase.co
entitlement-model-spec
| 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. |
| company.academy.assign | Company | Assign included academy content to employees. |
| company.careers.submit_job | Company | Create job postings for admin review. |
| company.analytics.export | Company | Export bounded own-company reporting. |
| survey.create | Survey | Create survey definitions and assignments. |
| survey.respond | Survey | Take assigned/public surveys. |
| survey.results.read | Survey | View permitted survey results and dashboards. |
| admin.content.publish | Internal admin | Publish standard Sanity pages or imported custom HTML. |
| admin.platform.manage | Internal admin | Manage users, vendors, content review queues, and platform settings. |
- data-model-spec v1
- capability-map v5
- permission-lifecycle-matrix v3
- production-readiness-gap-register v3
- publishing-model-spec v1
| area | source | capability | required access |
|---|---|---|---|
| Public site | Sanity + Supabase public projections | Read public content/vendor profiles/jobs/events | anonymous/public unless explicitly gated |
| Member profile | people + auth user | Update own profile | account.registered + owns profile |
| Member profile | auth provider + people | Change password/account settings | authenticated own account |
| Resources | memberships + tier access_rules + protected resource metadata | Read/download Pro report or guide | resource.report.read.pro or admin override |
| Academy | membership tier, seat, purchase/grant, course policy | Enroll included course | academy.course.enroll.included or course purchase/admin grant |
| Academy | course_enrollments + entitlement status | Continue enrolled course | active course_enrollments row and not revoked |
| Academy | person_roles/platform_admins | Create/manage course | admin.platform.manage or academy admin role |
| Vendor portal | vendor_memberships/memberships + person_roles/vendor ownership | Update vendor profile | vendor.portal.write + vendor relationship |
| Vendor portal | vendor entitlement + vendor_content_submissions | Create case study/content submission | vendor.portal.write |
| Vendor portal | vendor role + own vendor analytics filters | Export own analytics | vendor.analytics.export |
| Company workspace | organization_members + membership held by org | Invite/manage employees | company.workspace.admin |
| Company workspace | membership_seats + course_assignments | Assign academy courses | company.academy.assign + available seats/content grant |
| Company workspace | organization role + admin review workflow | Submit job posting | company.careers.submit_job |
| Surveys | future survey model + roles | Create survey | survey.create or admin.platform.manage |
| Surveys | future survey assignment/audience model | Take survey | survey.respond + assignment/public rule |
| Surveys | future survey response/reporting model | Display/export results | survey.results.read + audience/ownership filter |
| Internal admin | small internal admin allowlist/role for Brett, Sarah, Justin | Publish Sanity standard page or custom HTML import | admin.content.publish |
| role | table |
|---|---|
| Defines tier category, seat model, billing model, and structured access_rules. | membership_tiers |
| Canonical commercial/product entitlement holder for person-held, organization-held, and vendor-held memberships. | memberships |
| Seat assignment from an org/vendor/company membership to an individual person. | membership_seats |
| Operational authority and relationship roles such as platform_admin, vendor_admin, company_admin, author. | person_roles |
| Learning participation/progress after eligibility is granted. | course_enrollments |
| table | fields | purpose |
|---|---|---|
| entitlement_grants | - id - subject_type - subject_id - entitlement_key - source_type - source_id - status - starts_at - ends_at - metadata - created_at - updated_at | Optional normalized/materialized grant table for fast and auditable access decisions. |
| entitlement_audit_events | - id - actor_person_id - subject_type - subject_id - entitlement_key - event_type - source_type - source_id - reason - metadata - created_at | Audit log for grants, revocations, seat assignments, admin overrides, purchases, cancellations, and policy denials. |
| course_assignments | - id - course_id - assigned_to_person_id - assigned_by_person_id - organization_id - source_type - status - due_at - created_at - updated_at | Company/admin course assignment layer separate from course participation. |
| entitlement_policy_tests | - id - scenario_key - subject_fixture - resource_fixture - expected_decision - created_at | Machine-readable policy fixtures for production gating regressions. |
- 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.
- Inventory and remove/replace all persons table references in production code.
- Define entitlement key enum/constants shared by server functions, RLS, and UI display.
- Normalize membership_tiers.access_rules into versioned machine-readable rules.
- Implement entitlement evaluator and policy test fixtures before wiring new protected features.
- Backfill current Pro member state from memberships and reconcile people.is_pro/person_roles drift.
- Normalize course access fields and write course enrollment eligibility tests.
- Wire vendor portal and company workspace gates to memberships/seats/roles.
- Add entitlement audit events and support/admin visibility.
- Add survey/company/vendor export entitlement checks as those modules are built.
- 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.
- 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.
named capability/action key such as academy.course.enroll or vendor.portal.write
request metadata, impersonation/admin mode, source surface, and time
person_id plus active organization/vendor context when applicable
resource type and id when relevant, such as course_id, vendor_id, report_id, organization_id
boolean
expiration timestamp when access is time-bound
machine-readable allow/deny reason
membership, role, seat, purchase, admin grant, or policy refs used for the decision
resolved grant key or required key
| examples | subject type | current sources |
|---|---|---|
| - registered account - Pro member - course learner - company employee seat - vendor team member - platform admin | person | - people - person_roles - memberships.held_by_person_id - membership_seats.assigned_person_id - course_enrollments |
| - private practitioner company workspace - company academy/careers account | organization | - organizations - organization_members - memberships.held_by_org_id - membership_seats |
| - public vendor profile - vendor management portal account - enhanced directory listing | vendor | - vendors - vendor_memberships - person_roles - vendor_* portal tables |
2026-05-06T23:32:25.356Z
- 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 in ways that can drift from active memberships.
- membership_tiers.access_rules exists but is not yet the single enforced entitlement contract.
| name | category | seat model | billing model | access summary | price annual |
|---|---|---|---|---|---|
| Registered | practitioner | individual | free | Free baseline profile/account access; no premium courses, reports, events, or vendor portal. | |
| Pro | practitioner | individual | subscription | All courses, reports, and events; no vendor portal; no enhanced directory. | 9500 |
| Vendor | vendor | company | invoice | Vendor portal write access and enhanced directory; no practitioner course/report/event grants by default. |
9
1
8
0
3
4
0
0
- people
- person_roles
- membership_tiers
- memberships
- membership_seats
- vendor_memberships
- organization_members
- courses
- course_enrollments
- events
- content_embeddings