CannaHub
Multi-tenant B2B ecosystem of six applications covering cultivation, IoT monitoring, medical records, commerce and compliance for 6+ organisations in Argentina.
- Role
- Lead — architecture across web, mobile, commerce and content
- Period
- 2024 — Present
- Type
- Platforms
- Status
- production
- 373k
- Lines of code
- 223
- API routes
- 38
- Domain modules
- 6
- Applications
Showcase
Walkthrough
Overview
Cannabis clubs in Argentina run a business that is part farm, part pharmacy, part retailer, and entirely regulated. The legal requirement is traceability: prove where any gram came from, from the seed that produced it to the member it was dispensed to. Most of them were doing this in spreadsheets.
CannaHub is the platform that replaced them. Six applications share one Medusa commerce backend and a Strapi CMS: an admin webapp carrying 38 domain modules, a React Native companion for members, a marketing site, a documentation portal, a scraper and a demo surface.
Each organisation gets its own subdomain and its own brand — high-up, familias, medicann and others each run on app.cannahub.tech under their own logo, with data isolated from every other tenant, and one of them on its own domain entirely.
The admin webapp alone is 293,000 lines and 223 API routes, all of it behind a BFF layer — no UI component ever calls Medusa, Strapi or the IoT API directly.
Architecture
Six applications, one backend
Scroll to pan
Seed to sale
Scroll to pan
Cannabis-shaped inventory
Scroll to pan
Screens








Member app — dispensary 
Member app — orders 
Member app — appointments
Modules
Cultivation
The farm half of the business, modelled as a chain rather than a stock count — this is what makes traceability possible.
- Crops, plants and growth cycles
- Seed and strain library with genetics
- Harvest scheduling and yield records
- Spaces, lights and fertiliser tracking
IoT & environment
Live environmental telemetry from the grow rooms, integrated through the Growcast API.
- Temperature, humidity and CO2 streams per space
- Out-of-range alerting
- Historical series and visualisation
- Sensor management and calibration state
Commerce
A Medusa backend extended to carry product attributes that off-the-shelf commerce does not have.
- Products with strain genetics and batch lineage
- Price lists per organisation and membership tier
- Orders, dispensing and fulfilment
- Inventory, procurement and storage locations
Members
Who is allowed to buy what, and the paperwork that proves it.
- Member records and onboarding flows
- Membership tiers and renewals
- Document storage and verification
- Per-member dispensing limits
Medical
The clinical side: these are patients as much as customers, and the records have to hold up.
- Doctor records and prescriptions
- Appointment scheduling
- Member health history
- Treatment tracking
Operations
Day-to-day running of the organisation, plus the reporting the regulator asks for.
- Tasks and recurring task plans
- Workforce and locations
- Audit log of every access to sensitive data (Res. 1780/2025, AAIP 47/2018)
- Seed-to-sale traceability and compliance reporting
- Invoices, transactions and analytics
Applications
One backend, six surfaces, each with its own audience and its own repository conventions.
- Admin webapp — 293k lines, the operational core
- React Native companion for members
- Marketing website and MDX documentation portal
- Strapi CMS and a scraper service
Engineering
Cannabis-shaped inventory on generic commerce
Medusa has products, variants and stock. It does not have strain genetics, THC and CBD levels, or batch lineage back to a specific plant. Extending its product model to carry those without forking the engine is what let the platform inherit years of battle-tested commerce logic instead of rewriting checkout.
Traceability constrains everything upstream
Proving where a gram came from is not a reporting feature you add later — it is a shape the data model has to have from the first migration. Every harvest, transfer and dispensing event is an immutable link in a chain, which dictated decisions all the way up into how the UI records a routine task.
Six tenants, one deployment
Organisation scoping runs through every query and every API route via a mandatory tenant header. Six businesses share one deployment while never seeing each other’s data, pricing or members, and each gets a storefront that looks like their brand rather than like CannaHub.
A BFF layer as an architectural law
No UI component or feature service may call Medusa, Strapi or Growcast directly — everything routes through the app’s own API layer, which owns auth headers, tenant context and error shaping. Written into the project constitution because it is the rule that quietly rots first when a deadline appears.
AI harness
CannaHub spans eight repositories, so the harness is about consistency across them: a written constitution the architecture is checked against, ecosystem-wide conventions, and bespoke skills that scaffold and document a module in the house shape.
An architecture constitution
Written as numbered, non-negotiable principles: BFF-first (the UI never touches Medusa, Strapi or Growcast directly), mandatory tenant isolation on every data endpoint, the exact shape of a feature module down to the directory tree, and TypeScript strict with no escape hatches. Architecture decisions stop being re-litigated every session.
- cannahub-webapp/.specify/memory/constitution.md
Ecosystem-wide conventions
A root CLAUDE.md maps all six applications and the shared backend; CONVENTIONS.md pins the decisions that must not drift between them — Lucide over react-icons, Radix over Headless UI, locale codes, commit format, the shared-code candidates queued for extraction. Each app then carries its own CLAUDE.md for local detail.
- CLAUDE.md
- CONVENTIONS.md
Bespoke skills for repetitive work
create-feature scaffolds a module in the constitutional shape — components, overlays, query-key factory, queries, mutations, services, types, views — and wires translation keys for all three locales. create-api-route, debug-api and review-changes cover the other paths worn into the codebase. The house pattern is executable rather than described.
- cannahub-webapp/.claude/skills/
Documentation generated from the source
A sync-docs skill reads the actual route handlers and feature types for a domain and regenerates that domain’s MDX page in the docs app — endpoint table, request and response types, React Query examples, error codes. Documentation drift stops being a manual chore that nobody wins.
- .claude/skills/sync-docs/SKILL.md
Rules split by concern
Separate rule files for components, API routes and testing, loaded per task rather than as one wall of context. A component task gets component rules; a route task gets the route contract.
- cannahub-webapp/.claude/rules/
Spec Kit with git extensions
The full spec-driven pipeline — constitution, specify, clarify, plan, tasks, analyze, implement, checklist — plus git extension skills for feature branches, validation, commits and remotes. Large changes get planned in writing before an agent starts editing.
- cannahub-webapp/.specify/workflows/
GitHub MCP
The GitHub MCP server is wired into the webapp so agents can read issues and pull requests as first-class context instead of having the state of the work pasted at them.
- cannahub-webapp/.mcp.json
Stack
- 01Next.js 16
- 02React Native
- 03TypeScript
- 04Medusa.js
- 05Strapi
- 06Tailwind CSS
- 07Radix UI
- 08React Query
- 09Zustand