CNBS

Operator console

Operator console

Last updated 5/24/2026

CNBS Operator Console

Document ID: OPS-CONSOLE-001 Version: 0.1 (Spark) System: Cannabis Business System (CNBS) — midwestco/cnbs Audience: Platform operators, DevOps engineers, support engineers, on-call responders


Table of Contents

  1. System Overview
  2. Admin Dashboard & Routes
  3. Internal Tooling Inventory
  4. Operational Workflows
  5. Monitoring & Alerting
  6. Support Workflows
  7. Environment Management
  8. Secret Management
  9. Health Check Endpoints & Status

1. System Overview

CNBS is a multi-tenant Cannabis Business System built on Next.js 15 (App Router), Supabase (PostgreSQL + Realtime), and Clerk for identity management. The platform provides point-of-sale, inventory management, compliance reporting, analytics, and ecommerce capabilities for licensed cannabis dispensaries.

AttributeValue
FrameworkNext.js 15 (App Router, Turbopack)
RuntimeNode.js (containerized via Docker)
DatabaseSupabase (PostgreSQL + Realtime)
IdentityClerk (organization-based multi-tenancy)
Deployment targetVercel (primary), Docker (self-hosted)
CI/CDGitHub Actions (5 active workflows)
Package namedispensary-pos v0.1.0

The system operates a parent–child organization hierarchy (see docs/architecture/PARENT_CHILD_ORGANIZATION_ARCHITECTURE.md). Each child organization (individual dispensary location) maps to a Clerk organization and a Supabase row-level-security tenant.


2. Admin Dashboard & Routes

2.1 Admin API Routes

All admin API routes are prefixed /api/admin/ and require elevated Clerk role verification enforced at the route handler level via /api/auth/check-role.

Route IDPathHandler FilePurpose
API-001POST /api/admin/onboarding/completesrc/app/api/admin/onboarding/complete/route.tsMarks an organization's onboarding workflow complete; triggers initial Clerk sync
API-002GET/POST /api/auth/check-rolesrc/app/api/auth/check-role/route.tsValidates caller's Clerk role against required permission level
API-003GET /api/auth/check-user-contextsrc/app/api/auth/check-user-context/route.tsReturns active org context, role, and session metadata for the authenticated user
API-004POST /api/clerk/add-to-organizationsrc/app/api/clerk/add-to-organization/route.tsAdds a Clerk user to a target organization with a specified role
API-005POST /api/clerk/sync/organizationsrc/app/api/clerk/sync/organization/route.tsBidirectional sync of Clerk organization data to Supabase organizations table
API-006POST /api/clerk/sync/usersrc/app/api/clerk/sync/user/route.tsSyncs individual Clerk user record to Supabase users table
API-007POST /api/clerk/sync/customersrc/app/api/clerk/sync/customer/route.tsSyncs customer identity record from Clerk to Supabase
API-008POST /api/clerk/sync/membership/addsrc/app/api/clerk/sync/membership/add/route.tsAdds org membership in Supabase when Clerk membership webhook fires
API-009POST /api/clerk/sync/membership/removesrc/app/api/clerk/sync/membership/remove/route.tsRemoves org membership in Supabase; does not delete the user record
API-010POST /api/clerk/syncsrc/app/api/clerk/sync/route.tsCatch-all Clerk webhook dispatcher; routes to user/org/membership sub-handlers
API-011POST /api/auth/employeesrc/app/api/auth/employee/route.tsIssues short-lived employee PIN/session tokens for POS station login

2.2 Analytics & Reporting Routes

These routes power the internal analytics dashboards accessible to dispensary managers and CNBS operators.

Route IDPathPurpose
API-012GET /api/analytics/dashboardAggregate dashboard KPIs (revenue, units sold, customer count)
API-013GET /api/analytics/dashboard/real-timeWebSocket-compatible real-time metrics feed via Supabase Realtime
API-014GET /api/analytics/dashboard/performanceSystem and transaction performance indicators
API-015GET /api/analytics/dashboard/trendsPeriod-over-period trend data (daily, weekly, monthly)
API-016GET /api/analytics/revenueRevenue breakdown by product category and location
API-017GET /api/analytics/customersCustomer cohort and segmentation data
API-018GET /api/analytics/inventoryCurrent inventory levels, turnover rates
API-019GET /api/analytics/inventory-insightsAI-augmented inventory recommendations
API-020GET /api/analytics/predictionsML-based demand forecasting
API-021GET /api/analytics/insightsCross-dimensional insight aggregations
API-022GET /api/analytics/vitalsCore Web Vitals and application performance metrics
API-023GET /api/analytics/performanceBackend latency, query performance
API-024GET /api/analytics/reportsReport index listing available report types
API-025GET /api/analytics/reports/salesSales report with configurable date range
API-026GET /api/analytics/reports/inventoryInventory snapshot reports
API-027GET /api/analytics/reports/complianceCompliance audit trail reports for METRC submission

2.3 Operational API Routes

Route IDPathHandler FilePurpose
API-028GET/POST /api/backupsrc/app/api/backup/route.tsTriggers or retrieves database backup operations
API-029GET/POST /api/cash-drawersrc/app/api/cash-drawer/route.tsCash drawer open/close/count operations tied to POS sessions
API-030GET/POST /api/associatessrc/app/api/associates/route.tsList and create associate (budtender) records
API-031GET/PUT/DELETE /api/associates/:associateIdsrc/app/api/associates/[associateId]/route.tsIndividual associate management
API-032POST /api/careers/applysrc/app/api/careers/apply/route.tsCareer application intake; does not require auth

2.4 Canonical Data Routes (Reference Data Management)

Route IDPathPurpose
API-033GET/POST /api/canonical/brandsManage canonical brand records shared across organizations
API-034GET/POST /api/canonical/categoriesManage canonical product category taxonomy
API-035GET/POST /api/canonical/effectsManage canonical cannabis effects/terpene reference data

2.5 AI Tooling Routes

These routes are used by the CMS and product management interface to generate assets.

Route IDPathHandler FilePurpose
API-036POST /api/ai/generate-imagesrc/app/api/ai/generate-image/route.tsGenerates product images via OpenAI/Anthropic SDK
API-037POST /api/ai/generate-herosrc/app/api/ai/generate-hero/route.tsGenerates storefront hero section images
API-038POST /api/ai/generate-promotionsrc/app/api/ai/generate-promotion/route.tsGenerates promotional copy and visuals
API-039POST /api/ai/extract-colorssrc/app/api/ai/extract-colors/route.tsExtracts brand color palette from uploaded images
API-040POST /api/ai/save-generated-imagesrc/app/api/ai/save-generated-image/route.tsPersists AI-generated images to Supabase Storage

3. Internal Tooling Inventory

3.1 Node.js Scripts (scripts/)

All scripts are invoked via npm run <script> or directly with node scripts/<name>.js.

Tool IDScript FileNPM CommandPurposeSecurity Notes
TOOL-001scripts/detect-hook-provider-mismatches.jsnpm run validate:hooksPre-build validation; detects React hook/provider ordering errorsRuns as prebuild hook automatically
TOOL-002scripts/scan-style-violations.jsnpm run lint:styleScans source for inline style violations against docs/STYLE_RULES.mdRead-only; safe for CI
TOOL-003scripts/build-errors-only.shnpm run build:errorsRuns Next.js build and extracts only TypeScript errors from outputRequires bash
TOOL-004scripts/sync-clerk-data.jsnpm run sync-clerkSyncs all Clerk organizations and users to Supabase; also called by npm run db:resetRequires CLERK_SECRET_KEY and Supabase service role key
TOOL-005scripts/test-db-setup.jsnpm run db:setup / npm run db:seedSeeds the database with test fixtures; in NODE_ENV=test seeds test-isolated dataDo not run against production
TOOL-006scripts/sync-puffutica-inventory.jsnpm run sync:puffuticaPulls inventory data from the Puffutica third-party API into Supabase products tablePR #105 open: 2 security issues flagged
TOOL-007scripts/process-product-images.jsnpm run process:imagesBatch-processes product image assets (resize, optimize, upload to Supabase Storage)Requires Storage bucket write access
TOOL-008scripts/test-puffutica-api.jsnpm run test:puffutica-apiManual integration test for Puffutica API connectivity and response shapeDo not use in production pipelines — has open security PR #105
TOOL-009scripts/setup-complete-db.js(direct invocation)Full database schema setup scriptPR #102 closed: 6 security issues fixed
TOOL-010scripts/migrate-to-cloud.js(direct invocation)Migrates local Supabase schema to cloud instancePR #103 closed: 9 security issues fixed

3.2 Database Migration Files (database/)

These SQL files are applied manually or via Supabase CLI. They are not managed by an automated migration runner.

FilePurpose
database/local_schema.sqlBaseline schema for local development
database/cloud_migration.sqlSchema delta for cloud (Supabase) deployment
database/combined-migrations.sqlConsolidated migration set
database/migrations-to-apply.sqlPending migrations queue
database/data_migration.sqlData transformation migration
database/customers_inserts.sqlCustomer seed data
database/users_inserts.sqlUser seed data
database/organizations_inserts.sqlOrganization seed data
database/locations_inserts.sqlLocation seed data
database/user_orgs_inserts.sqlUser-organization junction seed data
database/insert-all-brands.sqlCanonical brand data seed
database/insert-products-batch1.sqlInitial product catalog seed (batch 1)

Operational note: There is no automated migration runner present in this repository. Migrations must be applied manually via supabase db reset (development) or direct SQL execution against the target Supabase project (production). Coordinate with the database owner before applying any file from database/.

3.3 Data Export Files (data_export/)

These CSV files represent point-in-time data exports and are used for migration validation and audit purposes. They are not live data and must not be used as source-of-truth in production operations.

FileContents
data_export/organizations.csvOrganization registry snapshot
data_export/users.csvUser account snapshot
data_export/customers.csvCustomer record snapshot
data_export/products.csvProduct catalog snapshot
data_export/brands.csvBrand registry snapshot
data_export/vendors.csvVendor registry snapshot
data_export/locations.csvLocation data snapshot
data_export/payment_sessions.csvPayment session log snapshot
data_export/request_logs.csvAPI request log snapshot
data_export/webhook_events.csvWebhook event log snapshot
data_export/delivery_zones.csvDelivery zone configuration snapshot

3.4 Data Migration Source Files (data-migration/)

Reference data used for seeding canonical cannabis product attributes.

FileContents
data-migration/cannabis-strain-database.jsonStrain names, genetics, and cannabinoid profiles
data-migration/07_cannabis_pricing_structures.jsonTiered pricing structure definitions
data-migration/08_cannabis_vendor_database.jsonVendor contact and product catalog data
data-migration/09_lab_testing_database.jsonLab test result reference data
data-migration/sample-products.jsonSample product catalog for seeding
data-migration/sample-products.csvCSV format of sample products

3.5 GitHub Actions CI/CD Workflows (.github/workflows/)

Workflow IDFileTriggerPurpose
TECH-001cannabis-pos-testing.ymlPush / PRRuns the full POS and compliance test suite including Cypress specs
TECH-002performance-monitoring.ymlSchedule / PushExecutes tests/performance/performance-test-suite.js and reports against thresholds
TECH-003pre-commit.ymlPre-commit hook via pushRuns validate:hooks and lint:style before merges
TECH-004test.ymlPush / PR to main & stagingJest unit, integration, and security test suite
TECH-005visual-regression.ymlPRPlaywright visual regression baseline comparison

3.6 Docker Infrastructure (infrastructure/docker/)

FilePurpose
infrastructure/docker/DockerfileProduction container image definition
infrastructure/docker/docker-compose.production.ymlMulti-service production compose configuration

4. Operational Workflows

4.1 Deployment

CNBS supports two deployment targets: Vercel (primary) and Docker self-hosted.

Vercel Deployment

# Standard deployment via Vercel CI (triggered on merge to main)
# Build command used by Vercel:
npm run vercel-build   # equivalent to: next build

# Pre-build validation runs automatically:
npm run prebuild       # runs scripts/detect-hook-provider-mismatches.js

Environment variables must be set in the Vercel dashboard for the target environment. See Section 8 for the full secret inventory. The SKIP_ENV_VALIDATION=true flag is available but should never be set in production (npm run build:nolint uses it; this command is for local debugging only).

Docker Self-Hosted Deployment

# Build production image
docker build -f infrastructure/docker/Dockerfile -t cnbs:latest .

# Start all services
docker-compose -f infrastructure/docker/docker-compose.production.yml up -d

# Verify container health
docker-compose -f infrastructure/docker/docker-compose.production.yml ps

Branch Strategy

BranchEnvironmentAuto-Deploy
mainProductionYes (Vercel)
stagingStagingYes (Vercel Preview)
Feature branchesPreviewYes (Vercel Preview per PR)

PRs from feature branches merge into staging first (see PR history: #95, #100, #101 follow the pattern feature → staging → main).

4.2 Rollback

Vercel Rollback

Navigate to the Vercel dashboard → Project → Deployments. Select the last known-good deployment and click Promote to Production. This is an instant cut-over with no downtime.

Database Rollback

There is no automated database rollback mechanism. Database changes must be reversed manually:

  1. Identify the migration file that introduced the regression (check database/migrations-to-apply.sql and git history).
  2. Write a compensating SQL statement.
  3. Apply to Supabase via the SQL editor in the Supabase dashboard or via psql with the service role connection string.
  4. Notify the team via the incident channel before applying any compensating migration to production.

Docker Rollback

# Tag and push a known-good image before deploying
docker tag cnbs:latest cnbs:rollback-<date>

# To rollback:
docker-compose -f infrastructure/docker/docker-compose.production.yml down
docker tag cnbs:rollback-<date> cnbs:latest
docker-compose -f infrastructure/docker/docker-compose.production.yml up -d

4.3 Feature Flags

No dedicated feature flag service (e.g., LaunchDarkly) is present in the repository. Feature gating is currently implemented at the component and API route level using environment variables and Clerk organization metadata.

Current pattern:

  • Environment variable NEXT_PUBLIC_FEATURE_<NAME>=true/false controls client-side feature visibility.
  • Server-side gating uses Clerk's organization metadata checked via GET /api/auth/check-user-context.

Operator procedure to enable a feature for a specific organization:

  1. Access Clerk Dashboard → Organizations → select target org.
  2. Add a metadata key matching the feature flag convention (confirm with engineering for current key names).
  3. Changes take effect on the next user session refresh; no deployment required.

4.4 User Management

Creating a New Dispensary Organization

  1. Create the organization in Clerk Dashboard or via the CNBS admin signup flow.
  2. Trigger organization sync: POST /api/clerk/sync/organization with the Clerk org ID.
  3. Verify the organization row appears in Supabase organizations table.
  4. Complete onboarding: POST /api/admin/onboarding/complete with the org ID.

Adding a User to an Organization

Via API:

POST /api/clerk/add-to-organization
Body: { "userId": "<clerk_user_id>", "organizationId": "<clerk_org_id>", "role": "org:admin|org:member" }

Via Script (bulk operations):

npm run sync-clerk   # Re-syncs all Clerk users and org memberships to Supabase

Webhook-driven (production path): Clerk fires organizationMembership.created → hits POST /api/clerk/sync → dispatches to POST /api/clerk/sync/membership/add.

Removing a User

POST /api/clerk/sync/membership/remove removes the Supabase membership record. The Clerk membership must be removed separately via Clerk Dashboard. User record is retained in both systems.

Employee POS Access

Budtenders access POS stations using short-lived tokens issued by POST /api/auth/employee. These tokens are scoped to the employee's associated organization and expire on session end. PINs are managed through the associate management interface (/api/associates).

4.5 Database Reset (Development Only)

# Full reset: drops and recreates local Supabase schema, then re-syncs Clerk data
npm run db:reset

# Seed only (no schema reset)
npm run db:seed

# Test environment seed
npm run db:seed:test

Never run npm run db:reset against a production Supabase project. The supabase db reset command destroys all data.

4.6 Inventory Sync (Puffutica)

# Sync Puffutica inventory to Supabase products table
npm run sync:puffutica

# Test connectivity without syncing
npm run test:puffutica-api

Current status: scripts/sync-puffutica-inventory.js has 2 open security issues (PR #105). Do not use in production until PR #105 is merged and verified.


5. Monitoring & Alerting

5.1 What Is Observed

SignalSourceRoute/Mechanism
Real-time transaction metricsSupabase Realtime + APIGET /api/analytics/dashboard/real-time
System performance indicatorsNext.js telemetry + APIGET /api/analytics/dashboard/performance
Core Web VitalsNext.js + Vercel AnalyticsGET /api/analytics/vitals
Backend query latencySupabase + APIGET /api/analytics/performance
Compliance audit eventsCypress E2E + compliance APIGET /api/analytics/reports/compliance
Load test resultstests/performance/performance-test-suite.jsGitHub Actions: performance-monitoring.yml
Real-time analytics load__tests__/load/real-time-analytics-load.test.tsJest load suite

5.2 GitHub Actions Performance Workflow

The .github/workflows/performance-monitoring.yml workflow runs tests/performance/performance-test-suite.js on a schedule and on push. Failures in this workflow indicate performance regressions. Review the workflow run output in GitHub Actions for threshold details.

The load test file __tests__/load/real-time-analytics-load.test.ts validates that the real-time analytics pipeline sustains concurrent load without degradation.

5.3 Application Performance Monitoring

The GET /api/analytics/vitals endpoint returns Core Web Vitals data. Vercel automatically captures Web Vitals when deployed to Vercel infrastructure and surfaces them in the Vercel Speed Insights dashboard.

Customer analytics performance is validated by __tests__/performance/customer-analytics-performance.test.ts. This test establishes latency baselines; review it for current threshold values.

5.4 Security Monitoring

  • __tests__/security/customer-data-security.test.ts — validates that customer PII is not exposed through API responses.
  • src/__tests__/security/cms-security.test.ts — validates CMS endpoint authorization controls.
  • tests/security/security-test-suite.js — invoked via npm run test:security; covers authentication bypass, injection, and authorization escalation scenarios.
  • GDPR/CCPA compliance: __tests__/compliance/gdpr-ccpa-compliance.test.ts

Security test runs are integrated into the main CI pipeline via .github/workflows/test.yml. Any failure blocks merge.

5.5 Alerting

No external alerting platform (PagerDuty, OpsGenie, etc.) is configured in the repository. Current alerting operates through:

  1. GitHub Actions failures — Workflow failures on main branch should be treated as P1 and immediately routed to the on-call engineer.
  2. Vercel deployment failure notifications — Configure in Vercel Dashboard → Notifications to route to your team's Slack/email.
  3. Supabase dashboard alerts — Configure database connection pool exhaustion and query timeout alerts in the Supabase project settings.

Escalation path (current):

  1. GitHub Actions failure notification → engineering Slack channel.
  2. If production is affected: open a Jira/Linear ticket (current ticket prefix: ZP- as seen in commit history).
  3. On-call engineer coordinates rollback per Section 4.2.

6. Support Workflows

6.1 Ticket Triage

Current tickets follow the ZP-XXXX numbering system (visible in commit messages: ZP-4305, ZP-4298, etc.). Use this prefix when filing production incidents.

Severity classification:

SeverityDefinitionTarget Response
P1Production outage, payment processing failure, compliance system downImmediate (< 15 min)
P2Analytics unavailable, user cannot log in, inventory sync failure< 2 hours
P3UI display errors, report generation slow, non-critical feature brokenNext business day
P4Cosmetic issues, documentation gapsBacklog

6.2 Common Issues & Resolution Playbooks

PLAY-001: User Cannot Access Organization After Invitation

Symptom: User accepts Clerk invitation but receives authorization error in CNBS.

Root cause: Clerk membership webhook may not have fired or POST /api/clerk/sync/membership/add failed silently.

Resolution:

  1. Verify the user exists in Supabase users table.
  2. Verify the user_organizations junction record exists.
  3. If missing, trigger manual sync:
    POST /api/clerk/sync/membership/add
    Body: { "userId": "<clerk_id>", "organizationId": "<clerk_org_id>", "role": "org:member" }
    
  4. Have the user sign out and back in to refresh their Clerk session.
  5. Reference: commit 22451dc (fix ZP-4298: invitation-fix).

PLAY-002: Onboarding Not Completing

Symptom: New dispensary organization stuck at onboarding step; POST /api/admin/onboarding/complete returns 4xx.

Root cause: Organization not yet synced to Supabase, or missing required fields in the org record.

Resolution:

  1. Verify org exists in Supabase: check the organizations table for the Clerk org ID.
  2. If missing, run: POST /api/clerk/sync/organization with the Clerk org ID.
  3. Retry POST /api/admin/onboarding/complete.
  4. See docs/analysis/CNBS_ADMIN_SIGNUP_ANALYSIS.md for the full onboarding state machine.

PLAY-003: POS Station Employee Login Failure

Symptom: Budtender cannot log in to POS station; PIN rejected or session not created.

Root cause: Employee associate record missing or POST /api/auth/employee returning an error.

Resolution:

  1. Verify the associate record exists: GET /api/associates filtered by the employee's name/email.
  2. If missing, create the associate: POST /api/associates.
  3. Re-issue POS credentials via the associate management interface.
  4. Check src/components/budtender/__tests__/BudtenderHeader.test.tsx for session token validation logic.

PLAY-004: Puffutica Inventory Sync Failure

Symptom: npm run sync:puffutica exits with error; products not updating in Supabase.

Root cause: API key rotation, network connectivity to Puffutica, or the open security issues in PR #105.

Resolution:

  1. Run npm run test:puffutica-api to test raw API connectivity.
  2. Verify PUFFUTICA_API_KEY (or equivalent secret) is current.
  3. Check Puffutica API status page externally.
  4. Do not deploy scripts/sync-puffutica-inventory.js to automated pipelines until PR #105 is resolved.

PLAY-005: Analytics Dashboard Returning Stale Data

Symptom: GET /api/analytics/dashboard returns data that doesn't reflect recent transactions.

Root cause: Cache staleness or Supabase Realtime subscription dropped.

Resolution:

  1. Check src/__tests__/lib/cache.test.ts for current cache TTL configuration.
  2. Verify Supabase Realtime WebSocket connectivity: the CSP connect-src directive in next.config.ts allows wss://*.supabase.co.
  3. Check GET /api/analytics/dashboard/real-time for active subscriber count.
  4. If cache is stale: trigger cache invalidation per the cache layer's API (implementation in src/lib/cache).

PLAY-006: Build Failure Due to Hook/Provider Mismatch

Symptom: npm run build fails with React hook error; prebuild step exits non-zero.

Root cause: scripts/detect-hook-provider-mismatches.js detected a hook called outside its required provider context.

Resolution:

  1. Run npm run validate:hooks to get the full error report.
  2. Locate the component flagged and wrap it with the appropriate provider.
  3. See docs/PROVIDER_PATTERNS.md for the canonical provider hierarchy.

PLAY-007: Logo Upload Failing in Branding Settings

Symptom: Organization logo upload returns an error; branding page shows no logo after upload.

Root cause: Supabase Storage write permissions or image processing pipeline failure.

Resolution:

  1. Verify NEXT_PUBLIC_SUPABASE_URL and service role key are correct for the environment.
  2. Run npm run process:images manually to test the image processing pipeline.
  3. Check Supabase Storage bucket policies for the logos bucket.
  4. Reference: commit bfb98f8 (fix ZP-4300: upload-logo-branding).

6.3 Compliance-Specific Escalation

Cannabis compliance failures (METRC integration, purchase limit violations, waste tracking) are P1 by default and must be escalated to the compliance officer in addition to engineering. Relevant test specs:

  • cypress/e2e/cannabis-compliance.cy.ts
  • cypress/e2e/cannabis-purchase-limits.cy.ts
  • cypress/e2e/cannabis-transfer-manifest.cy.ts
  • cypress/e2e/cannabis-waste-tracking.cy.ts
  • cypress/e2e/metrc-package-scanning.cy.ts
  • cypress/e2e/regulatory-reporting.cy.ts

Compliance report data is available at GET /api/analytics/reports/compliance.


7. Environment Management

7.1 Environment Matrix

EnvironmentBranchDatabaseClerk InstancePurpose
DevelopmentFeature branchesLocal Supabase (supabase start)Clerk dev instanceLocal development
StagingstagingSupabase staging projectClerk staging instancePre-production validation
ProductionmainSupabase production projectClerk production instanceLive dispensary operations

7.2 Development Environment Setup

# Start local Supabase instance
supabase start

# Reset database with schema and seed data
npm run db:reset

# Start Next.js development server
npm run dev

# If SSL issues with external APIs:
npm run dev:unsafe-ssl

# Debug mode with Node.js inspector:
npm run dev:debug

7.3 Environment Variables

The following environment variables are required. Values differ per environment. Set them in:

  • Local: .env.local (gitignored)
  • Staging/Production: Vercel Environment Variables