Local-first, multi-tenant reference implementation for extracting structured action items from meeting notes.
This repository demonstrates:
- Strict tenant isolation (partition key = tenantId)
- Document-oriented schema aligned with Cosmos DB
- Async processing pipeline (API → Job → Worker → Extraction → Tasks)
- Deterministic extraction provider (rules-based, testable)
- Zero-infrastructure Local Mode
- Azure reference architecture (Managed Identity + Key Vault)
- Architecture Decision Records (ADRs)
- CI-enforced quality gates
Local Mode runs fully offline with no cloud dependencies.
From repo root:
pnpm install
pnpm store:seedRun in separate terminals:
# Terminal 1
cd apps/api
pnpm dev# Terminal 2
cd apps/worker
pnpm dev# Terminal 3
cd apps/web
pnpm devMinimal path (without long-running worker):
# Terminal 1
cd apps/api
pnpm dev# Terminal 2 (run once after submitting a note)
pnpm worker:once# Terminal 3
cd apps/web
pnpm devDev Context values in web UI:
- Tenant ID:
tenant-demo - User ID:
user-admin-demo - Email:
admin@demo.local - Roles:
admin,member - Allow Delete Notes (feature flag): enabled (for controlled delete demo)
Sample note:
Meeting: Weekly Team Sync - Feb 16, 2026
Action Items:
ACTION: Hank to provide development plan by Friday
ACTION: Ira to complete Q1 metrics report due 2026-02-20
NEXT: Schedule follow-up meeting @Sarah
FOLLOW UP: Review performance improvement plan with HR
- Set up mentorship sessions for Ira
- Document quarterly objectives
Azure Mode is documentation + IaC skeleton only. It is designed for architecture reviews and deployment planning.
- Architecture mapping: docs/architecture/local-vs-azure.md
- Azure reference topology: docs/architecture/azure-reference-architecture.md
- ADR index: docs/architecture/adr_readme.md
- Observability + feature flags: docs/architecture/observability-feature-flags.md
- IaC skeleton: infra/azure
- Azure deployment notes: infra/azure/README.md
Local Mode remains the default and requires no cloud setup for lint, typecheck, tests, or demo walkthroughs.
pnpm installpnpm store:seedThis creates a demo tenant with admin and member users in .local-data/store.json.
Open three terminals and run:
Terminal 1 - API Server:
cd apps/api
pnpm devAPI runs at http://localhost:3000
Terminal 2 - Worker (Task Extraction):
cd apps/worker
pnpm devWorker polls for jobs every few seconds
Terminal 3 - Web UI:
cd apps/web
pnpm devWeb UI runs at http://localhost:5173
- Navigate to
http://localhost:5173 - Expand the Dev Auth Context panel at the top
- Enter demo credentials:
- Tenant ID:
tenant-demo - User ID:
user-admin-demo - Email:
admin@demo.local - Roles:
admin,member
- Tenant ID:
Click "New Note" and paste this sample:
Meeting: Weekly Team Sync - Feb 16, 2026
Attendees: @Hank (Manager), @Ira (Employee)
Discussion Points:
- Q1 Performance Review
- Development opportunities
Action Items:
ACTION: Hank to provide development plan by Friday
ACTION: Ira to complete Q1 metrics report due 2026-02-20
NEXT: Schedule follow-up meeting @Sarah
FOLLOW UP: Review performance improvement plan with HR
Additional tasks:
- Set up mentorship sessions for Ira
- Document quarterly objectives
The worker will automatically extract 6 tasks from this note within a few seconds!
- Tasks appear automatically (page polls every 3 seconds while processing)
- Click Approve, Reject, or Edit for each task
- Export approved tasks as CSV
apps/api— Fastify REST API with tenant auth + RBACapps/worker— Background job processor with rules-based extractionapps/web— React + MUI web interfacepackages/db— File-backed local store (tenant-isolated)packages/extractor— Rules-based task extraction enginepackages/shared— Shared types/utilitiesinfra/local— Local infrastructure placeholdersinfra/azure— Azure reference IaC skeleton
The rules-based extractor looks for:
Keywords:
ACTION:NEXT:FOLLOW UP:- Additional explicit task prefixes supported by the extractor
Heuristics:
- Bullet points starting with action verbs (e.g., "Schedule", "Review", "Complete")
Metadata Parsing:
- Owners:
@NameorOwner: Name - Due Dates:
YYYY-MM-DDformat
Local development runs offline without Azure dependencies.
- File-backed store at
.local-data/store.json - Deterministic seed data for demo tenant
- Tenant-scoped reads and writes
- Job queue for worker processing
Azure mode is a reference architecture (not implemented yet). Target: App Service + Functions + Key Vault + Managed Identity.
From project root:
pnpm store:seed # Seed local data
pnpm dev # Start API only
pnpm dev:worker # Start worker only (from repo root)
pnpm dev:web # Start web UI only (from repo root)
pnpm worker:once # Run worker once (manual)
pnpm test # Run all tests
pnpm lint # Lint all packages
pnpm typecheck # Type check all packagesRequest headers for auth context:
x-tenant-id— Tenant identifierx-user-id— User identifierx-user-email— User emailx-user-roles— Comma-separated roles:admin,member,readerx-feature-flags— Local feature overrides (example:notes.allowDelete=true)
These headers are development-mode only and used for local demo auth context.
Development mode: Falls back to demo context if headers missing
Production mode: Returns 401 if headers missing
GET /health— Health checkGET /me— Current user contextGET /notes— List notesPOST /notes— Create note (member+)GET /notes/:id— Get note detailsDELETE /notes/:id— Delete note and related tasks/jobs (admin +notes.allowDelete=true)GET /notes/:id/tasks— Get tasks for notePATCH /tasks/:id— Update task (member+)GET /tasks/export.csv?status=approved— Export CSV
Health check:
curl -H "x-tenant-id: tenant-demo" \
-H "x-user-id: user-admin-demo" \
-H "x-user-email: admin@demo.local" \
-H "x-user-roles: admin" \
http://localhost:3000/healthCreate note:
curl -X POST http://localhost:3000/notes \
-H "content-type: application/json" \
-H "x-tenant-id: tenant-demo" \
-H "x-user-id: user-member-demo" \
-H "x-user-email: member@demo.local" \
-H "x-user-roles: member" \
-d '{"title":"Weekly Sync","rawText":"ACTION: Review budget\nNEXT: Submit report"}'Export tasks as CSV:
curl -H "x-tenant-id: tenant-demo" \
-H "x-user-id: user-member-demo" \
-H "x-user-email: member@demo.local" \
-H "x-user-roles: member" \
"http://localhost:3000/tasks/export.csv?status=approved"OpenTelemetry tracing is enabled for API and worker via shared initialization.
- Default local exporter: console
- Optional OTLP exporter: set
OTEL_EXPORTER=otlpandOTEL_EXPORTER_OTLP_ENDPOINT - Key attributes:
tenantId,userId,requestId,jobId,noteId
Environment variables:
OTEL_EXPORTER_OTLP_ENDPOINTOTEL_EXPORTER(otlporconsole)OTEL_TRACES_SAMPLEROTEL_RESOURCE_ATTRIBUTES
Flag evaluation priority:
x-feature-flagsheader (non-production only)FEATURE_*environment variables- Hardcoded defaults
Implemented flags:
telemetry.enabled(defaulttrue)extractor.provider(defaultrules)notes.allowDelete(defaultfalse)ui.devContextPanel(defaulttruein local)
Controlled delete behavior:
DELETE /notes/:idis available only for admins whennotes.allowDelete=true- If the delete flag is disabled, endpoint returns
404 - Delete removes the note plus tenant-scoped related tasks/jobs and writes
note_deletedaudit event
- Never commit secrets, keys, tokens, or connection strings
- Use
.env.examplefor placeholders only - Azure mode must use Managed Identity + Key Vault
- Every record includes
tenantId - Every query filters by
tenantId - Cross-tenant access is prevented at the data layer
Roles:
admin— Manage tenant members + all member permissionsmember— Create notes, review/approve/edit tasksreader— View-only access
The local store is intentionally simple so tests run without network access.
Conceptual mapping to Cosmos DB for Azure mode:
- Tenant isolation key:
tenantId - Recommended partition key:
/tenantId - All entities modeled with tenant ownership first
This keeps query patterns aligned with multi-tenant isolation and minimizes cross-partition access.
