{"deprecation_notice":{"deprecated":true,"canonical_url":"https://apps.mi2.com.mx/stack","message":"This endpoint is a frozen snapshot of MI AI Manifest v3.22.1's stack content. The canonical, live MI stack reference is now maintained by the Coolify Apps platform at https://apps.mi2.com.mx/stack. Submit changes via https://suggestions.mi2.com.mx/api/suggestions instead of editing this snapshot.","frozen_from_version":"3.22.1","frozen_at":"2026-05-22"},"title":"MI AI Framework — Developer Reference Guide","version":"3.22.1","generatedAt":"2026-07-05T11:51:38.014Z","url":"https://manifest.miglobal.com.mx/stack","description":"Complete reference for teams building on the MI AI stack. This JSON endpoint is designed for AI agent and programmatic consumption.","sections":[{"id":"overview","title":"Overview","content":"MI AI Framework is a full-stack TypeScript application for logistics and warehouse management (pallets, shipments, containers, billing, fleet). It is a monolithic Express + React app with PostgreSQL, deployed via PM2 behind Nginx with Let's Encrypt SSL. The app supports bilingual (EN / es-MX) UI, role-based access, real-time scanning workflows, and an AI chat system (TLI)."},{"id":"tech-stack","title":"Technology Stack","content":[{"layer":"Frontend","technologies":"React 18, TypeScript 5, Vite 5, Tailwind CSS 3, shadcn/ui (Radix primitives), wouter (router), TanStack React Query, react-i18next, Zustand (minimal), Framer Motion"},{"layer":"Backend","technologies":"Express.js 4, TypeScript, Drizzle ORM, Passport.js (session auth), connect-pg-simple, Zod validation, Multer (uploads), node-cron"},{"layer":"Database","technologies":"PostgreSQL 16 (localhost:5432, user: master, password: master). DEV db: mi_ai_manifest_dev, PROD db: mi_ai_manifest"},{"layer":"Storage","technologies":"MinIO on QNAP NAS (S3-compatible, bucket: mi-ai-manifest). Region: us-west-1"},{"layer":"Email","technologies":"AWS SES (us-east-1). V1 SESClient for internal, V2 SESv2Client for customer-facing. MJML + Handlebars templates."},{"layer":"AI","technologies":"Anthropic Claude SDK (chat, analysis, artifact generation)"},{"layer":"Testing","technologies":"Vitest (unit/integration), Playwright (E2E via MCP server, DISPLAY=:99)"},{"layer":"Observability","technologies":"Sentry (@sentry/node + @sentry/react), opt-in via SENTRY_DSN env var"},{"layer":"API Docs","technologies":"Scalar interactive explorer at /api/docs (OpenAPI 3.1, hand-maintained in server/docs/openapi.ts)"},{"layer":"Infrastructure","technologies":"PM2 process manager, Nginx reverse proxy, Let's Encrypt SSL, GitHub Actions CI/CD"},{"layer":"Mobile","technologies":"Android WebView wrapper with LPAPI SDK for Bluetooth label printing (DP26s printer)"},{"layer":"PDF","technologies":"jsPDF, pdfkit, pdf-lib, JsBarcode, QRCode"},{"layer":"Excel","technologies":"xlsx (SheetJS)"},{"layer":"Scanning","technologies":"quagga (barcode), html5-qrcode, react-qr-code"}]},{"id":"project-structure","title":"Project Structure","content":"```\nmi-ai-manifest/\n├── client/src/               # React frontend\n│   ├── components/           # 100+ UI components\n│   ├── lib/                  # queryClient, utils, PDF generators\n│   ├── hooks/                # React hooks (useColumnPreferences, useDebounce, usePermissions, useIsMobile)\n│   ├── pages/                # Top-level page components\n│   └── i18n/config.ts        # i18n configuration\n├── client/public/locales/    # Translation JSON files (en/, es-MX/) — 23 namespaces per language\n├── server/\n│   ├── index.ts              # Server entry point (Express + Passport + session)\n│   ├── routes.ts             # Main API routes (~14k lines)\n│   ├── routes/               # Extracted sub-routers (tliRoutes, publicTracking, publicPreferences, publicStack)\n│   ├── dbStorage.ts          # Database access layer (~12.6k lines, IStorage interface)\n│   ├── db.ts                 # PostgreSQL connection (pg driver)\n│   ├── s3Storage.ts          # S3/MinIO integration\n│   └── docs/openapi.ts       # OpenAPI 3.1 spec\n├── shared/\n│   └── schema.ts             # Drizzle ORM schema (~5k lines, 70+ tables)\n├── tests/                    # Vitest test files\n├── migrations/               # SQL migration files (applied in order)\n├── android-print-middleware/  # Android WebView wrapper\n└── dist/                     # Build output\n```\n\n**Path Aliases** (vite.config.ts): `@/` → `client/src/`, `@shared/` → `shared/`, `@assets/` → `attached_assets/`"},{"id":"user-management","title":"User Management & RBAC","content":"### Roles\n- **admin**: Full system access — user management, settings, all CRUD, RBAC configuration\n- **supervisor**: Extended permissions — reporting, manage all records, delete pallets\n- **logistics**: Shipment and container management\n- **customer**: View-only access filtered by assigned customers (via user_customer_access junction table)\n- **user**: Basic pallet scanning, can only manage own pallets\n\n### Authentication\n- Session-based via Passport.js with bcrypt password hashing\n- Sessions stored in PostgreSQL via connect-pg-simple\n- Cookie: connect.sid, httpOnly, secure, sameSite: lax, 7-day expiration\n- All endpoints require auth except /api/login and public routes\n\n### Backend Middleware\n- `requireAuth` — all authenticated endpoints\n- `requireAdmin` — admin-only operations\n- `requireSupervisor` — supervisor+ operations\n- `requireUserOrAbove` — user, supervisor, admin\n\n### Frontend Permission System\n- `usePermissions()` hook returns `{ isAdmin, isSupervisor, canEdit, canDelete }`\n- `<WithPermission>` / `<WithRole>` wrapper components for conditional rendering\n\n### API Endpoints\n- POST /api/auth/login — username + password\n- POST /api/auth/logout — destroy session\n- GET /api/auth/me — current session user\n- GET/POST /api/users — list / create\n- GET/PUT/DELETE /api/users/:id — read / update / delete\n- PUT /api/users/:id/language — update language preference"},{"id":"database-schema","title":"Database Schema (70+ tables)","content":"Schema-first approach: Drizzle ORM generates migrations from shared/schema.ts. Uses createInsertSchema/createSelectSchema for automatic Zod validation.\n\n### Core Tables by Domain\n\n| Domain | Key Tables |\n|--------|-----------|\n| Auth/Users | users (roles: admin, supervisor, logistics, customer, user) |\n| Customers | customers, customerContacts, customerAddresses, customerItemFields, customerImportTemplates |\n| Shipments | shipments, shipmentDocuments, shipmentStops, stopPalletAssignments, shipmentTrackers |\n| Containers | containers, containerItems, containerDocuments |\n| Pallets | pallets, scannedItems, palletWarehouseHistory, palletOwnershipHistory |\n| Status Config | shipmentStatuses, containerStatuses + history tables |\n| Fleet | drivers, vehicles, vehicleLicensePlates, maintenanceRecords, shipmentFleetAssignments |\n| Billing | bills, billLineItems, payments, billDisputes, billingCompanies, billingAuditLog |\n| Freight/Carriers | freightCompanies, carriers, carrierDocuments, customsBrokers |\n| Rail | railCarriers, railPorts, railShipments, railShipmentItems |\n| Documentation | documentationCategories, documentationPages, pageDocumentationLinks |\n| Docks | facilities, dockDoors, dockAppointments |\n| Photos | photos (entity-specific paths for containers, pallets, shipments) |\n| AI Chat | tli_threads, tli_messages, tli_artifacts, tli_usage, tli_user_quotas |\n| Bug Tracking | improvements, improvement_attachments, improvement_comments, improvement_status_history |\n| System | changelog, emailLogs, emailConfiguration, notificationSettings |\n\n### Database Access\n```bash\n# DEV database (safe for testing)\nPGPASSWORD=master psql -h localhost -U master -d mi_ai_manifest_dev\n\n# PRODUCTION database (caution: real data)\nPGPASSWORD=master psql -h localhost -U master -d mi_ai_manifest\n```"},{"id":"i18n","title":"Internationalization (i18n)","content":"Full bilingual support (English `en` and Spanish Mexico `es-MX`) via react-i18next with HTTP backend.\n\n### Configuration\n- Supported: ['en', 'es-MX'], Fallback: 'en'\n- Default namespace: 'common'\n- Load path: /locales/{{lng}}/{{ns}}.json?v={version}\n- Detection: cookie → localStorage → navigator\n- Cookie: i18nextLng (7-day expiry)\n\n### 23 Namespaces\ncommon, auth, navigation, pallets, shipments, containers, settings, customers, billing, maintenance, admin, changelog, improvements, documentation, multiStop, tli, import, rail, truckloads, dock-schedule, photos, incoming, recovery\n\n### File Location\n`client/public/locales/{en|es-MX}/{namespace}.json` — loaded at runtime via HTTP backend\n\n### Usage Patterns\n```typescript\n// Single namespace (default)\nconst { t } = useTranslation('customers');\nt('customer_name') // ✓\n\n// Multiple namespaces — first is default\nconst { t } = useTranslation(['orders', 'common']);\nt('order_status')      // ✓ resolves from 'orders' namespace\nt('common:save')       // ✓ explicit namespace prefix for 'common'\nt('orders:order_status') // ✗ WRONG — redundant prefix, will fail\n```\n\n### Key Rule\nWhen adding translation keys, ALWAYS add to BOTH `en` and `es-MX` files for the appropriate namespace."},{"id":"file-storage","title":"File Storage (S3/MinIO)","content":"### Architecture\n- Primary: MinIO on QNAP NAS (S3-compatible, bucket: mi-ai-manifest)\n- Region: us-west-1\n\n### S3 Path Structure\n```\n{bucket}/\n├── {entity-type}/{entityId}/{photoType}_{timestamp}_{uuid}.{ext}\n├── {entity-type}/{entityId}/thumbnails/{file}.jpg\n├── documents/{entityId}/{docType}_{timestamp}.pdf\n├── companies/{companyId}/logo_{timestamp}.{ext}\n├── uploads/improvements/improvement-{ts}-{randomId}.{ext}\n├── documentation/images/{uuid}\n└── documentation/videos/{uuid}\n```\n\n### Upload Endpoints\n| Endpoint | Max Size | Storage |\n|----------|----------|---------|\n| POST /api/photos/upload | 10 MB | S3 (memory) |\n| POST /api/upload | 10 MB | Local (disk) |\n| POST /api/objects/upload | N/A | S3 presigned |\n| POST /api/improvements/:id/attachments | 50 MB | Local (disk) |\n| POST /api/documentation/admin/images/upload | 10 MB | S3 (memory) |\n| POST /api/documentation/admin/videos/upload | 50 MB | S3 (memory) |\n\n### Presigned URL TTL\nUploads: 15 min, Photos: 24h, Logos: 1h"},{"id":"components","title":"UI Components & Patterns","content":"### Component Library\nBuilt on shadcn/ui (Radix UI primitives) with Tailwind CSS. All components in `client/src/components/ui/`.\n\n### Key UI Patterns\n- **Data Grid**: Column definitions with sorting, filtering, persistent preferences via useColumnPreferences hook. No pagination — full dataset loaded.\n- **Modal Pattern**: ResponsiveModal — Dialog on desktop, Drawer on mobile. Nested modals require z-[60] on DialogContent.\n- **Form Pattern**: React Hook Form + Zod validation\n- **Inline Editing**: InlineDateEditor for grid cell date editing\n- **Filter Pattern**: TextFilterContent, DateFilterContent, NumberFilterContent components\n- **Toast Pattern**: 1 toast max visible, auto-dismiss\n- **Loading Pattern**: Skeleton, LoadingSpinner, Loader2 icon\n- **Role-Based UI**: usePermissions() hook, WithPermission/WithRole components\n- **Responsive**: useIsMobile() hook for mobile/desktop layouts\n- **Server State**: React Query with apiRequest helper, 5-minute staleTime\n- **Debounce**: useDebounce hook, 300ms delay for search inputs\n- **Status Badges**: StatusBadge with dynamic colors/icons from database configuration\n\n### Critical Dual Component Pattern\nTwo SEPARATE component trees for pallet functionality:\n1. **PalletManagement.tsx** — User view (Home > Pallets tab), shows user's own pallets\n2. **AdminPalletOverview.tsx** — Admin view (Settings > Pallets), shows all pallets\nWhen adding features or fixing bugs, ALWAYS update BOTH components."},{"id":"document-viewers","title":"Document Viewers & Editors","content":"### Supported Formats\n| Extension | Viewer |\n|-----------|--------|\n| .pdf | react-pdf v10 (pdfjs-dist v5) — pagination, zoom 50-200%, keyboard shortcuts |\n| .xlsx, .xls | SheetJS v0.18 — multi-sheet tabs, HTML table rendering |\n| .docx | mammoth v1.11 — DOCX only (not .doc) |\n| .jpg, .png, .gif, .bmp, .webp | Image Viewer with zoom/pan |\n| .mp4, YouTube, Vimeo, Loom | react-player v3.4 |\n| .md | react-markdown v10 |\n\n### Image Annotation (Fabric.js)\nDrawing (pencil, highlighter), shapes (rect, circle, arrow, polygon), text tool, eraser, undo/redo, color picker, serializable JSON output.\n\n### Rich Text Editor (TipTap v3.15)\nStarterKit, Underline, TextAlign, Link, Image, Table, Highlight, Color, Placeholder. Bilingual EN/ES tabs for content editing."},{"id":"app-shell","title":"App Shell & Navigation","content":"### Routing (wouter)\nApp.tsx defines top-level routes:\n- `/track/:token` → Public tracking page (no auth)\n- `/preferences/:token` → Public notification preferences (no auth)\n- `/stack` → Developer reference guide (no auth)\n- `/docks` → Dock schedule page\n- `/improvements` → Bug/feature tracking page\n- `/manual/*` → Documentation viewer\n- `/*` → ManifestMakerApp (main authenticated shell)\n\n### ManifestMakerApp Tabs\nAll authenticated navigation happens inside ManifestMakerApp.tsx via tabs:\n- Scanner (pallet scanning workflow)\n- Pallets, Shipments, Containers, Truckloads, Rail\n- Billing, Customers, Fleet\n- Settings (admin)\n\n### Settings Menu Items\n| Item | Roles | Description |\n|------|-------|-------------|\n| Companies | admin | Company management |\n| Customers | admin | Customer accounts & contacts |\n| Email | admin | Email templates & notifications |\n| Status Manager | admin | Custom status workflows |\n| Users | admin | User CRUD, roles, access |\n| Deleted Items Recovery | admin | Restore soft-deleted records |\n| Mobile App | all | Android APK download |\n| Product Settings | all | App-level configuration |\n| Update History | all | Changelog viewer |"},{"id":"bug-system","title":"Bug & Improvement System","content":"### Overview\nFull-featured bug and feature request tracking built into the app. Multi-step submission wizard, screenshot annotation (Fabric.js), video uploads, comment threads, email notifications.\n\n### Status Lifecycle\ncreated → in_progress → completed|cancelled OR created → rejected (forward-only)\n\n### API Endpoints\n| Method | Path | Description |\n|--------|------|-------------|\n| GET | /api/improvements | List all (filterable) |\n| POST | /api/improvements | Create improvement |\n| GET | /api/improvements/:id | Get by ID |\n| PATCH | /api/improvements/:id | Update |\n| DELETE | /api/improvements/:id | Soft delete |\n| PATCH | /api/improvements/:id/status | Update status (admin) |\n| PATCH | /api/improvements/:id/resolution | Set resolution (admin) |\n| POST | /api/improvements/:id/attachments | Upload attachment |\n| GET | /api/improvements/:id/attachments | List attachments |\n| POST | /api/improvements/:id/comments | Add comment |\n| GET | /api/improvements/:id/comments | List comments |\n| GET | /api/improvements/stats | Aggregate counts |\n| GET | /api/improvements/export/markdown | Export as Markdown |"},{"id":"tli-system","title":"TLI AI Chat System","content":"### Architecture\nServer-Sent Events (SSE) streaming with Anthropic Claude SDK.\n\n### SSE Event Types\n- `connected` — stream established\n- `text` — streamed text chunk\n- `tool_use` — tool invocation\n- `tool_result` — tool result\n- `complete` — includes messageId, input/output tokens, latency\n- `error` — error message\n\n### Database Tables\n- tli_threads — userId, title, topic, topicContext (JSONB), language, model, visibility\n- tli_messages — threadId, role, content, toolCalls, toolResults, artifacts, token counts, latency\n- tli_artifacts — artifactType (excel|pdf|word|csv|chart), fileUrl, contentData\n- tli_usage — per-request token tracking with estimated cost\n- tli_user_quotas — daily/monthly token limits, isUnlimited flag\n\n### Features\nMulti-topic support, language-aware responses, artifact generation to S3, token tracking/billing, quota enforcement, thread sharing, blind spot analysis, email composition tool."},{"id":"testing","title":"Testing & Observability","content":"### Vitest (Unit/Integration)\n```bash\nnpm run test              # Run all tests\nnpm run test:watch        # Watch mode\nnpm run test:coverage     # Coverage report (v8 provider)\n```\nConfig: `vitest.config.ts`, resolves `@/` and `@shared/` aliases. Tests in `tests/` directory.\n\n### Playwright (E2E)\nMCP server with DISPLAY=:99 (Chromium). Used by /checkbugs and /checkimprovements for visual verification of fixes.\n\n### Sentry Error Tracking\nOpt-in via env vars: `SENTRY_DSN` (backend) and `VITE_SENTRY_DSN` (frontend).\n- Only 5xx errors reported (4xx excluded)\n- Cookies stripped from events (no PII)\n- 10% traces sample rate\n\n### Scalar API Docs\nInteractive explorer at `/api/docs`. Raw spec at `/api/docs/openapi.json`.\nHand-maintained OpenAPI 3.1 in `server/docs/openapi.ts`.\nTags: Auth, Users, Pallets, Shipments, Containers, Customers, Billing, Improvements, Photos, TLI, Documentation."},{"id":"android","title":"Android APK (WebView + Bluetooth Printing)","content":"### Architecture\nReact Web App → WebView → window.DzLPAPI bridge → LPAPI SDK → Bluetooth SPP → DP26s label printer\n\n### Build\n```bash\ncd android-print-middleware && \\\nJAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \\\nANDROID_HOME=/home/master/android-sdk \\\n./gradlew assembleRelease\n```\n\n### Versioning\n- versionCode: integer, increment by 1 each release\n- versionName: semantic (e.g., \"1.78.0\")\n\n### Deployment\n1. Build release APK\n2. Copy as `app-fresh.apk` (auto-update endpoint) and `app-v{version}.apk` (archive)\n3. UpdateManager in app checks every 24h for new version\n\n### JavaScript Bridge (window.DzLPAPI)\n`openPrinterSync()`, `startJob()`, `drawLabelText()`, `commitJob()`, `printLabel()`"},{"id":"agents","title":"Claude Code Agents, Skills, Slash Commands & MCP","content":"Everything the Claude Code agent uses to operate on this codebase lives in the `.claude/` directory. There are four distinct primitives — **agents** (specialist sub-agents the orchestrator can delegate to), **skills** (reusable workflows the user can invoke directly with `/skill-name`), **commands** (project-specific shortcuts), and **hooks** (deterministic shell commands the runtime fires automatically). Plus MCP servers that extend the toolset.\n\n### Sub-agents (`.claude/agents/*.md`)\nSpecialist agents the main Claude session can spawn via the Task tool. Each `.md` file is a frontmatter+prompt definition.\n\n| Agent | Tools | Purpose |\n|-------|-------|---------|\n| **changelog-manager** | Read, Write, Edit, Bash, mcp__github, mcp__filesystem | Build changelogs, release notes, bilingual content from improvements DB; bump versions |\n| **debugging** | Read, Write, Edit, Glob, Grep, Bash, mcp__github, mcp__filesystem, mcp__sequential-thinking, mcp__context7 | Investigate errors, analyze stack traces, identify root causes, propose fixes |\n| **documentation** | Read, Write, Edit, Glob, Grep, Bash, mcp__filesystem, mcp__github, mcp__context7 | Maintain architecture docs, API references, setup guides, progress tracking |\n| **frontend-testing** | Read, Write, Edit, Glob, Grep, Bash, Task, mcp__playwright, mcp__filesystem, mcp__github, mcp__sequential-thinking | Create + run Playwright E2E tests on DISPLAY=:99 to catch UI regressions |\n| **full-stack-developer** | Read, Write, Edit, Glob, Grep, Bash, Task, mcp__filesystem, mcp__sequential-thinking, mcp__context7, mcp__github | Build features end-to-end: frontend UI + backend routes + DB models + integration |\n| **github-manager** | Read, Write, Edit, Glob, Grep, Bash, mcp__github, mcp__filesystem, mcp__sequential-thinking | Version control, commits, PRs, releases, semver, repo health |\n| **orchestrating** | Read, Write, Edit, Glob, Grep, Bash, Task, mcp__github, mcp__filesystem, mcp__sequential-thinking | Coordinate other agents, assign tasks, resolve conflicts, track progress |\n| **ubuntu-system-admin** | Read, Write, Edit, Glob, Grep, Bash, mcp__filesystem, mcp__sequential-thinking, mcp__github | Server config, security, networking, firewall, nginx, SSL, infra |\n\n### Skills (`.claude/skills/*.md`) — user-invocable\nRun with `/<skill-name>`. Skills are stateless task definitions: title, allowed tools, optional argument hint, prompt body.\n\n| Skill | Allowed tools | Argument hint | Purpose |\n|-------|---------------|---------------|---------|\n| **/db-migrate** | Read, Write, Edit, Grep, Glob, Bash | `<description of the schema change>` | Create + apply idempotent DB migration: write SQL → run on DEV → verify |\n| **/learn-from-fix** | Read, Write, Edit, Grep, Glob, Bash | `<brief description of the bug that was just fixed>` | After a fix, extract root-cause pattern and save it to CLAUDE.md + memory |\n| **/review-code** | Read, Grep, Glob, Bash | (none) | Review uncommitted changes for quality, reuse, performance, correctness |\n| **/self-eval** | Read, Grep, Glob, Bash | (none) | 8-category checklist: bugs, missing i18n, untested logic, CLAUDE.md compliance |\n\n### Slash Commands (`.claude/commands/*.md`) — project-specific\nHeavier scripted workflows. Distinct from skills in that they're often tied to project deployment/CI rather than general patterns.\n\n| Command | Argument | Description |\n|---------|----------|-------------|\n| **/approved** | `[version-type] [changelog-summary]` | **The ONLY sanctioned path from DEV to production.** Bumps version → builds → commits → tags → pushes to git → waits for CI/CD (GitHub Actions, Coolify, etc.) → runs prod migrations → inserts changelog + docs → rebuilds APK → health-checks. Deploy-target-agnostic; the workflow contract is identical whether prod runs on GitHub Actions, Coolify, Vercel, fly.io, or a custom VPS. **Never bypass with a manual git push or pm2 restart.** Source: `.claude/commands/approved.md` |\n| **/bugloop** | (none) | Automated bug-fix loop. Use with `/loop 30m /bugloop` for continuous triage |\n| **/checkbug** | `{ticketNumber}` | Fetch and analyze a specific bug from production DB |\n| **/checkbugs** | (none) | Batch review all open bugs, fix them, deploy to DEV |\n| **/checkimprovements** | (none) | Review open feature requests, plan implementation, build them |\n| **/inbox** | `[recent\\|<n>\\|search <query>]` | Check dev email inbox for bug reports + screenshots |\n| **/getreadytoclear** | (none) | Prepare SESSION_HANDOFF.md before context clearing |\n\n### Hooks (`.claude/settings.json` → `hooks`)\nDeterministic shell commands the Claude Code runtime executes automatically. Hooks don't run \"as Claude\" — they run before/after the tool call regardless of what the model wanted to do.\n\n| Hook | Matcher | What it does |\n|------|---------|--------------|\n| **PreToolUse** | `Bash` | Blocks `rm -rf /`, `DROP DATABASE` before they reach the shell. Exits non-zero with a message; agent must adjust |\n| **PostToolUse** | `Edit`\\|`Write` | When an EN locale file (`locales/en/*.json`) is edited, warns if the matching es-MX counterpart is missing |\n| **Stop** | (any) | Runs the Vitest suite after every Claude response so regressions surface immediately |\n\n### MCP Servers (`.claude/settings.json` → `mcpServers`)\nExtend Claude's toolbelt with capabilities the host doesn't provide natively.\n\n| Server | Purpose |\n|--------|---------|\n| **playwright** | Browser automation for E2E testing (DISPLAY=:99, Chromium) |\n| **context7** | Up-to-date library documentation — used by debugging/full-stack-developer agents |\n| **filesystem** | Sandboxed file operations |\n| **github** | GitHub repository integration (PRs, issues, releases) |\n| **sequential-thinking** | Multi-step reasoning capabilities |\n| **postgres** | Direct database access (production-read or DEV-read/write depending on env) |\n| **git** | Git operations beyond Bash |\n\n### Permissions (`.claude/settings.json` → `permissions`)\nAllow/deny lists that gate every tool call. Designed so the common case auto-approves but anything risky requires user confirmation.\n\n- **allow**: git read ops, `npm run build/test/check`, `pm2 status/logs`, DEV pm2 restarts, DEV database queries, file operations within the project\n- **deny**: `rm -rf /`, `DROP DATABASE`, `DROP TABLE` on prod, production PM2 restarts (these require explicit user approval each time)\n\n### Discovery from outside\nAgents and tools that need this list can fetch it from:\n- `GET /api/public/stack` (canonical, JSON)\n- `GET /stack.json` (short URL, same JSON)\n- `GET /stack.md` (Markdown rendering)\n- `GET /stack` (Markdown if requested by an LLM/curl/python UA, HTML SPA for browsers)\n- `GET /llms.txt` (top-level discovery + auth model)\n- `GET /sitemap.xml` (lists every public discovery URL)"},{"id":"self-improvement","title":"How to Implement Agents, Skills, Commands & Hooks","content":"Copy-pasteable templates and the conventions every `.claude/` file in this project follows. Use these when building a new agent, skill, command, or hook.\n\n### The flywheel\nSession N writes code + hooks enforce invariants → memory persisted in `~/.claude/projects/*/memory/` + CLAUDE.md → Session N+1 reads memory, doesn't repeat past mistakes → monotonic quality improvement. **Hooks are the safety net; memory is the institutional knowledge.**\n\n### File-system layout\n```\n.claude/\n├── agents/          # Sub-agents (frontmatter + prompt). Spawned via Task tool.\n│   ├── debugging.md\n│   ├── full-stack-developer.md\n│   └── …\n├── skills/          # User-invocable workflows (/skill-name)\n│   ├── db-migrate.md\n│   ├── review-code.md\n│   └── …\n├── commands/        # Project-specific slash commands\n│   ├── approved.md\n│   ├── checkbugs.md\n│   └── …\n├── settings.json    # hooks, mcpServers, permissions (checked into git)\n└── settings.local.json  # personal overrides (gitignored)\n```\n\n### Implementing a new sub-agent\nCreate `.claude/agents/<name>.md` with frontmatter:\n\n```markdown\n---\nname: my-agent\ndescription: One sentence the orchestrator uses to decide when to spawn this agent. Be specific about inputs and outputs.\ntools: Read, Write, Edit, Glob, Grep, Bash, mcp__github, mcp__sequential-thinking\n---\n\n# My Agent\n\nYou are a specialist that does X. Your job is to:\n1. Step one\n2. Step two\n\nConstraints:\n- Always …\n- Never …\n\nWhen done, produce: <one-line description of the output shape>\n```\n\n**Rules:**\n- `name` must be kebab-case and unique across `.claude/agents/`.\n- `description` is what the orchestrator sees — write it from the perspective of \"should I delegate to this agent?\".\n- `tools` lists only what the agent actually needs. Less is more — narrower tool surface = safer agent.\n- The agent body is the system prompt. Be terse, specific, and end with the output contract.\n\n### Implementing a new skill\nCreate `.claude/skills/<name>.md`:\n\n```markdown\n---\nname: my-skill\ndescription: One-sentence description shown to the user when they type /\nuser-invocable: true\nallowed-tools: Read, Grep, Glob, Bash\nargument-hint: \"<describe-the-argument>\"   # Optional, omit if none\n---\n\n# /my-skill\n\nSteps Claude should follow when this skill runs:\n1. …\n2. …\n\nIf \\${argument} is empty, ask the user for clarification.\n```\n\n**Rules:**\n- `user-invocable: true` makes it appear in `/help` and tab-completion.\n- `allowed-tools` is enforced — the skill can't use tools outside this list even if it tries.\n- Skills should be **stateless** — assume nothing about prior conversation state.\n\n### Implementing a new slash command (heavier project workflow)\nSame shape as a skill, lives under `.claude/commands/`. Use commands when the workflow is **specific to this app/repo** (e.g. `/approved` is the MI AI Manifest release flow). Use skills when the workflow is **generally applicable** (e.g. `/review-code`).\n\n### Adding a hook\nHooks run deterministically — they're NOT prompted to Claude. Add them to `.claude/settings.json`:\n\n```json\n{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [{\n          \"type\": \"command\",\n          \"command\": \"cmd=$(jq -r '.tool_input.command'); if echo \\\"$cmd\\\" | grep -qE 'rm\\\\s+-rf\\\\s+/'; then echo 'BLOCKED' >&2; exit 2; fi; exit 0\"\n        }]\n      }\n    ],\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Edit|Write\",\n        \"hooks\": [{ \"type\": \"command\", \"command\": \"…shell snippet…\" }]\n      }\n    ],\n    \"Stop\": [\n      { \"hooks\": [{ \"type\": \"command\", \"command\": \"npm run test 2>&1 | tail -3\" }] }\n    ]\n  }\n}\n```\n\n**Rules:**\n- Exit 0 = pass; exit 2 = block the tool call and surface stderr to Claude.\n- The tool call payload is piped to the hook on stdin as JSON. Parse with `jq -r '.tool_input.<field>'`.\n- Matchers are regex against the tool name (e.g. `Bash`, `Edit|Write`).\n- Keep them fast — they run on every matching tool call.\n\n### Adding an MCP server\n```json\n{\n  \"mcpServers\": {\n    \"my-server\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@me/my-mcp-server\"],\n      \"env\": { \"API_KEY\": \"${MY_API_KEY}\" }\n    }\n  }\n}\n```\n\nThe server's tools become available to Claude under the prefix `mcp__my-server__*`. They must also appear in the relevant agent's `tools:` frontmatter to be usable by that agent.\n\n### Memory architecture\nTwo parallel persistence layers:\n\n1. **CLAUDE.md** (git-checked, team-shared, always in context). Use for: architecture rules, workflows, gotchas, anything that *must* be loaded every session.\n2. **Auto Memory** (`~/.claude/projects/-home-master-projects-mi-ai-manifest/memory/`). Use for: corrections, user feedback, ephemeral discoveries. Files are pointer-indexed in `MEMORY.md` and loaded on demand. Types: `user`, `feedback`, `project`, `reference`.\n\n### Discoverability — make the catalog readable for other agents\nEvery entry in `.claude/{agents,skills,commands}/` flows automatically into:\n- The Settings → API Keys reference (for permissions UI)\n- The stack guide (this section, served as JSON/Markdown/HTML)\n- `/llms.txt` discovery file\n\nTo add your new agent/skill/command to the **discovery surface**, just commit the `.md` file — the next release picks it up via `server/lib/stackContent.ts`'s agents section. (When making material changes, also bump the version in `package.json` so the JSON's `version` field reflects what's in production.)"},{"id":"git-workflow","title":"The Required Workflow: DEV → /approved → Production","content":"**This is the mandatory development cycle for every team and every project built on this stack.** It is not optional, not \"ideal-case\" — it is the only path code takes from a developer's machine to a production user. Skipping any step (especially pushing directly to production) is not allowed.\n\nThe same workflow applies regardless of deploy target — whether your production environment is hosted on GitHub Actions + a self-managed VPS (this app), **Coolify**, Vercel, fly.io, Render, AWS, or anything else. The `/approved` slash command abstracts the deploy mechanism; the **workflow contract** stays the same.\n\n### The cycle every change must follow\n\n```\n  ┌────────────────────┐    ┌────────────────────┐    ┌─────────────────────┐    ┌────────────────────┐\n  │  1. WRITE on LOCAL │ →  │  2. TEST on DEV    │ →  │  3. USER APPROVAL   │ →  │  4. /approved      │\n  │  (your machine)    │    │  (hosted DEV env)  │    │  (explicit, human)  │    │  (deploys to prod) │\n  └────────────────────┘    └────────────────────┘    └─────────────────────┘    └────────────────────┘\n```\n\n**Step 1 — Write & build locally.** Run the code on your own machine first. `npm run dev` for hot reload, `npm run check` for TypeScript, `npm run test` for the suite. Iterate until the change works locally. This is where 95% of debugging happens — never skip it by editing the DEV server directly.\n\n**Step 2 — Push to your hosted DEV environment and test there.** Local works ≠ DEV works (different DB, different env vars, real S3 instead of mocks, real session middleware, real reverse proxy). Run `npm run build && pm2 restart <app>-dev` (or your equivalent on Coolify/Vercel/etc.) and verify the actual deployed DEV URL behaves as expected. Run E2E tests (Playwright via MCP if available). Reproduce the original bug-report flow end-to-end.\n\n**Step 3 — Get explicit user approval.** When testing on DEV looks good, **stop and wait**. Show the user what changed, what was tested, and what the verified URLs are. **Never** assume \"looks good to me, I'll push\" — that is not a substitute for user approval. The user is the only authorized trigger for Step 4.\n\n**Step 4 — Run `/approved` to deploy to production.** Only the `/approved` slash command pushes to production. It bumps the version, builds, commits, tags, pushes to git, waits for CI/CD (GitHub Actions / Coolify / etc.) to deploy, runs DB migrations on prod, inserts the changelog entry and documentation page on prod, rebuilds the Android APK if applicable, and health-checks the result. The 13-step contract is documented under \"CI/CD & Production Deployment\" below; the source lives in `.claude/commands/approved.md`.\n\n### What `/approved` is — and what it is NOT\n\n`/approved` is **the only sanctioned production-deploy entry point**. It is NOT:\n- a manual `git push` to main\n- a manual SSH-into-prod + `pm2 restart`\n- a \"hotfix\" that bypasses the build step\n- a \"small change so it's fine\" justification\n\nIf you find yourself wanting to deploy without `/approved`, the answer is always: fix `/approved` itself, then run it. Most \"I need to skip the workflow\" cases are actually \"the workflow needs an extra step\" — add the step to `approved.md` so every future deploy benefits.\n\n### Deploy targets — the workflow is the same regardless\n\n| Production host | What `/approved` does on push | Where the contract lives |\n|---|---|---|\n| **GitHub Actions** (this app) | Triggers `.github/workflows/deploy-production.yml` on tag push | Workflow YAML |\n| **Coolify** | Coolify auto-pulls main on push and rebuilds the container | Coolify config + git webhook |\n| **Vercel / Netlify / Render** | Push to main triggers automatic build & deploy | Vercel/Netlify/Render dashboard |\n| **fly.io** | `flyctl deploy` runs inside `/approved` after the git push | `fly.toml` |\n| **Custom VPS** | `/approved` SSHs in, pulls, builds, restarts the process manager | `.claude/commands/approved.md` |\n\nIn every case: **dev → test → approve → `/approved` → prod**. The deploy target plugs into Step 4 — it doesn't replace Steps 1-3.\n\n### Git commit format\n\nAll commits should use this format (the format `/approved` produces automatically):\n\n```\n<type>: <subject>\n\n<body explaining what and why, not how>\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\nTypes: `feat` (new feature), `fix` (bug fix), `docs`, `style`, `refactor`, `test`, `chore`, `perf`.\n\n### What you must NEVER do\n\n- **Push to production without going through `/approved`.** No exceptions.\n- **Test only on your local machine.** Local works ≠ deployed works.\n- **Add test/synthetic data to the production database.** Use DEV for that.\n- **Run destructive git commands** (`reset --hard`, `push --force`) without explicit user approval.\n- **Skip the build step.** Both PM2 processes run `dist/index.js` — without rebuilding, nothing you changed actually runs.\n- **Modify production .env files directly.** The deploy pipeline handles `.env` backup/restore; manual edits get clobbered on next deploy.\n- **Push doc-only changes \"just to update\".** They still go through `/approved` so the changelog + stack data version stays accurate."},{"id":"environments","title":"Environments & Infrastructure","content":"### Two-Server Architecture\n\n| | DEV Server | PRODUCTION Server |\n|---|---|---|\n| **Hostname** | manifestdev.miglobal.com.mx | manifest.miglobal.com.mx |\n| **IP** | 45.22.197.163 | 108.61.207.152 |\n| **SSH** | standard port 22 | port 2215 |\n| **App Port** | 5000 | 7000 |\n| **Database** | mi_ai_manifest_dev | mi_ai_manifest |\n| **PM2 Process** | mi-ai-manifest-dev | mi-ai-manifest-prod |\n| **Env File** | .env.dev | .env |\n| **Contains** | Test data, safe to experiment | REAL CUSTOMER DATA |\n\n### PM2 Configuration (ecosystem.config.cjs)\nBoth processes run `dist/index.js` (compiled output, not TypeScript):\n- Node args: `--max-old-space-size=512`\n- Auto-restart at 1GB memory\n- Fork mode, single instance\n- Logs: `/home/master/.pm2/logs/mi-ai-manifest-{dev|prod}-{out|error}.log`\n\n### Reverse Proxy\nNginx with Let's Encrypt SSL on both servers. Certbot auto-renewal runs twice daily via `certbot.timer`. Deploy hook reloads nginx after renewal.\n\n### Database\nPostgreSQL 16 on localhost:5432 (both servers). User: master, Password: master. Each server has its own database.\n\n### S3 Storage\nMinIO on QNAP NAS (S3-compatible). Bucket: mi-ai-manifest. Region: us-west-1. Used for photos, documents, logos, attachments."},{"id":"cicd","title":"CI/CD & Production Deployment","content":"### GitHub Actions Pipeline\nFile: `.github/workflows/deploy-production.yml`\n\n**Trigger**: Push to `main` branch (except `**.md` and `docs/**` changes). Also supports manual `workflow_dispatch`.\n\n**Pipeline Steps** (runs on ubuntu-latest, SSHs into production server):\n1. Backup production `.env` to `/tmp/` (critical — git reset would destroy it)\n2. `git stash` any local changes, `git fetch origin main`, `git reset --hard origin/main`\n3. Restore `.env` from backup\n4. Validate DATABASE_URL doesn't point to dev database (auto-fixes if it does)\n5. `npm install --production=false` (need devDependencies for build)\n6. `npm run build` (Vite frontend + esbuild backend)\n7. Apply all SQL migrations: `for migration in migrations/0*.sql; do psql -f \"$migration\"; done`\n8. `pm2 restart mi-ai-manifest-prod` (falls back to `pm2 start ecosystem.config.cjs`)\n9. Wait 5s, then health-check: `curl http://localhost:7000` expects HTTP 200\n\n**Timeout**: 10 minutes total. If any step fails, deployment stops and reports failure.\n\n### The /approved Slash Command (Full Release Automation)\nWhen the user says `/approved`, Claude Code executes a 13-step release:\n\n1. **Calculate version** — read current from package.json, bump patch/minor/major\n2. **Update package.json** — write new version\n3. **Sync stack guide** — when `.claude/` (agents, skills, commands, hooks, MCP, permissions) or the release workflow itself changed since the last release, update all three discovery surfaces so the public stack reference matches reality:\n   - `server/lib/stackContent.ts` — canonical agent-readable source for `/api/public/stack`, `/stack.json`, `/stack.md`\n   - `client/src/components/stack-guide/StackAgentsGuide.tsx` — HTML/React rendering of the agents catalog\n   - `client/src/components/stack-guide/StackApprovedWorkflow.tsx` (`APPROVED_WORKFLOW_CONTENT`) — HTML rendering of this 13-step list itself\n4. **Build** — `npm run build` on dev server (stack content is bundled here)\n5. **Git commit** — `chore: Release vX.X.X` with changelog summary\n6. **Git tag** — annotated tag `vX.X.X`\n7. **Push to GitHub** — `git push origin main --tags` → triggers GitHub Actions\n8. **Wait for deployment** — ~90s for build + restart on production\n9. **Run migrations** — SSH to production, apply SQL migrations\n10. **Changelog entry** — insert into `changelog_entries` table on production DB\n11. **Documentation page** — insert release notes into `documentation_pages` table\n12. **Android APK** — increment versionCode, build with Gradle, deploy `app-fresh.apk` + versioned archive\n13. **Health check** — verify PM2 online + HTTP 200\n\n**Why Step 3 matters:** the `/stack` discovery surface is consumed by AI agents (via JSON/Markdown) and humans (via the HTML view). If the listing drifts from `.claude/`, agents will spawn sub-agents that don't exist, miss new skills, or invoke obsolete tools. Treating stack sync as part of every release keeps the catalog honest.\n\n### SSH to Production\n```bash\nssh -p 2215 master@108.61.207.152\n# Default directory: /home/master (NOT the project dir)\n# Project: /home/master/projects/mi-ai-manifest\n```\n\n### Database Migrations\n- Migrations are in `migrations/0*.sql` (numbered, applied in order)\n- All migrations use idempotent SQL (`IF NOT EXISTS`, `ADD COLUMN IF NOT EXISTS`)\n- Safe to re-run — the CI/CD pipeline runs all of them on every deploy\n- For new schema changes: create migration file → apply to DEV first → verify → include in release"},{"id":"common-commands","title":"Common Development Commands","content":"### Building & Running\n```bash\nnpm run dev        # Development mode with hot reload (tsx server/index.ts)\nnpm run check      # TypeScript type checking\nnpm run build      # Production build (vite frontend + esbuild backend → dist/)\nnpm start          # Start production server (node dist/index.js)\n```\n\n### Testing\n```bash\nnpm run test              # Run all tests (Vitest)\nnpm run test:watch        # Watch mode\nnpm run test:coverage     # Coverage report (v8 provider)\n```\n\n### PM2 Process Management\n```bash\npm2 status                                           # Check all processes\npm2 logs mi-ai-manifest-dev --lines 50 --nostream    # View DEV logs\npm2 logs mi-ai-manifest-prod --lines 50 --nostream   # View PROD logs\npm2 restart mi-ai-manifest-dev                       # Restart DEV (after build)\npm2 restart all --update-env                         # Restart with env changes\n```\n\n### Database\n```bash\nnpm run db:push                   # Apply schema changes via Drizzle ORM\nnpm run seed:test-data            # Seed test data (DEV ONLY)\n\n# DEV database\nPGPASSWORD=master psql -h localhost -U master -d mi_ai_manifest_dev\n\n# PRODUCTION database (via SSH)\nssh -p 2215 master@108.61.207.152 \"PGPASSWORD=master psql -h localhost -U master -d mi_ai_manifest\"\n\n# Backup before dangerous operations\nPGPASSWORD=master pg_dump -h localhost -U master -d mi_ai_manifest -F c -f backup_$(date +%Y%m%d_%H%M%S).backup\n```\n\n### Git\n```bash\ngit status              # Check working tree\ngit log --oneline -10   # Recent commits\ngit diff                # Unstaged changes\ngit add <files>         # Stage specific files (never use git add -A blindly)\n```"},{"id":"starter-template","title":"Starter Template","content":"The stack guide includes copy-pasteable starter files for bootstrapping a new app on this stack:\n\n1. **package.json** — React/Express dependencies\n2. **vite.config.ts** — Vite setup with path aliases (@/, @shared/)\n3. **tailwind.config.ts** — Tailwind config with shadcn/ui theme\n4. **ecosystem.config.cjs** — PM2 ecosystem configuration\n5. **server/index.ts** — Express server with session/passport/CORS\n6. **server/db.ts** — PostgreSQL connection via Drizzle + pg\n7. **shared/schema.ts** — Drizzle ORM schema with Zod validation\n8. **client/src/App.tsx** — React app with wouter routing\n9. **client/src/lib/queryClient.ts** — React Query config + apiRequest helper\n10. **client/src/i18n/config.ts** — i18next configuration\n11. **client/src/hooks/usePermissions.ts** — Role-based permission hook\n12. **client/src/hooks/useDebounce.ts** — Debounce hook (300ms)\n13. **client/public/locales/en/common.json** — English translations\n14. **client/public/locales/es-MX/common.json** — Spanish translations\n\nVisit https://manifest.miglobal.com.mx/stack#starter-template for the full code of each file."},{"id":"api-management","title":"External API v1 & API Key Management","content":"### Surface\n`/api/v1/external/*` — bearer-token-authenticated REST API for third-party apps (partner integrations, internal scripts, customer portals).\n\nDistinct from `/api/public/*` (one-time tracking tokens, not reusable) and `/api/*` (session-authed, used by the web app itself).\n\n### Authentication\n```\nAuthorization: Bearer <key_id>.<secret>\n```\nBoth halves issued at creation time. Secret is bcrypt-hashed at rest and shown only once.\n\n### Three Scopes\n| Scope | What it sees | Use case |\n|---|---|---|\n| customer | Only shipments with matching customer_id | Customer ERP/portal integrations |\n| partner | All shipments, optionally filterable | 3PL/carrier dispatch and tracking apps |\n| internal | Full access, no rate limit | Internal scripts, admin tools |\n\n### Rate Limits (3 tiers)\n| Tier | per minute | per hour | per day |\n|---|---|---|---|\n| standard | 60 | 1,000 | 10,000 |\n| elevated | 300 | 10,000 | 100,000 |\n| unlimited | ∞ | ∞ | ∞ |\n\nPostgreSQL-backed sliding fixed-window counters in `api_rate_limit` table. Per-key override via `rate_limit_override` jsonb.\n\n### Permissions Model\n```json\n{\n  \"shipments\": [\"read\", \"write_status\"],\n  \"pallets\": [\"read\"],\n  \"items\": [\"read\"],\n  \"products\": [\"read\"],\n  \"documents\": [\"read\", \"write\"],\n  \"customers\": [\"read\"],\n  \"webhooks\": [\"manage\"]\n}\n```\nWildcards: `{\"*\": [\"*\"]}` (internal) grants everything. `{\"shipments\": [\"*\"]}` grants all actions on that resource.\n\n### Endpoints\n**Meta**: GET /health, /version, /whoami\n**Shipments**: GET /shipments (cursor pagination), /shipments/:id, /shipments/:id/pallets, /items, /stops, /trackers, /by-bol/:bolNumber\n**Items** (fully denormalized — pallet + shipment + stops + customer + scanned_by user + order + product master + trackers + optional documents/photos/history): GET /items/by-serial/:serialNumber, /items/by-upc/:upc, /items/by-lpn/:lpn, /items/by-sku/:sku, /items/by-order/:orderId, /items (flexible search via ?serial=&upc=&lpn=&sku=&make=&model=&order_id=&line_number=)\n**Products**: GET /products/by-upc/:upc — resolve UPC → make/model/description/harmonized_code from the product master\n**Documents**: GET /shipments/:id/documents, /documents/:docId/download (streamed proxy from MinIO — endpoint never exposed)\n**Writes**: PATCH /shipments/:id/status, /stops/:stopId/status; POST /stops/:stopId/pod (multipart)\n**Webhooks**: GET/POST/DELETE /webhooks; GET /webhooks/:id/deliveries\n**Customers**: GET /customers (partner/internal only)\n\n### Webhooks\nHMAC-SHA256 signing (`X-MIT-Signature: t=<ts>,v1=<hex>` Stripe-style). Exponential backoff retry (1m, 5m, 30m, 2h, 12h, 24h), 6 attempts then dead. Auto-disable after 20 consecutive failures.\n\nEvents: shipment.created, shipment.status_changed, shipment.delivered, shipment.stop.status_changed, shipment.stop.delivered, shipment.document_uploaded, pallet.shipped, pallet.delivered.\n\nWebhook secrets are AES-256-GCM encrypted at rest (not bcrypt) so the dispatcher can sign with the plaintext.\n\n### Logging & Audit\n| Table | Retention | Purpose |\n|---|---|---|\n| api_request_logs | 90 days | Every authed request: status, duration, bytes, IP |\n| api_denial_logs | 365 days | Every 4xx denial: invalid_key, revoked, rate_limited, permission_denied, customer_scope_violation |\n| api_usage_daily | indefinite | Precomputed daily aggregates for dashboard |\n| api_key_events | indefinite | Admin audit trail (created, rotated, revoked, etc.) |\n| api_webhook_deliveries | 90 days (terminal) | Per-event delivery log with retry state |\n\nCleanup runs daily at 03:30 via `apiRetentionJob`.\n\n### Security Guarantees\n- Customer scoping enforced at the DB layer (WHERE clause), not just request layer\n- Customer-scope violations return 404 (not 403) to prevent enumeration\n- All denials logged with IP and user_agent\n- 24-hour secret rotation grace period\n- Optional IP allowlist (CIDR) and CORS origin allowlist per key\n- HTTPS-only (nginx redirects)\n\n### Admin Dashboard\nSettings → API Keys & External API:\n- List/create/edit/revoke/rotate keys\n- Per-key 30-day usage chart + top endpoints\n- Recent requests + denials with replay buttons for failed webhook deliveries\n- Global metrics: total req/24h, top keys, top endpoints, error rate\n- Webhook subscription manager per key\n\n### Files\n| File | Role |\n|---|---|\n| migrations/0137_external_api_infrastructure.sql | 7 new tables + indexes |\n| server/middleware/apiKeyAuth.ts | Bearer parse, bcrypt compare, scoping helpers |\n| server/middleware/apiRateLimit.ts | PG-backed fixed-window rate limiter |\n| server/routes/externalApi.ts | /api/v1/external/* endpoints |\n| server/routes/apiKeyAdminRoutes.ts | /api/admin/api-keys/* admin endpoints |\n| server/services/apiKeyService.ts | Generate/rotate/revoke + audit logging |\n| server/services/secretCrypto.ts | AES-GCM webhook secret encryption |\n| server/services/webhookEmitter.ts | Enqueues deliveries from write endpoints |\n| server/services/webhookDispatcher.ts | Background HTTP sender with retry |\n| server/services/apiRetentionJob.ts | Daily log pruning + aggregate refresh |\n| client/src/components/ApiKeyManagement.tsx | Admin dashboard UI |\n\n### Docs\n- docs/guides/EXTERNAL_API_GUIDE.md — integrator getting started\n- docs/guides/EXTERNAL_API_REFERENCE.md — every endpoint\n- docs/guides/EXTERNAL_API_WEBHOOKS.md — event catalog + HMAC verification\n- docs/stack/api-management.md — full system documentation"}]}