Documentation Index
Fetch the complete documentation index at: https://grantmaster.dev/llms.txt
Use this file to discover all available pages before exploring further.
API Gateway & tRPC
| Status | Updated | Covered Files |
|---|
| 🟢 Stable | 2026-02-22 | functions/src/api/gateway.ts, functions/src/api/routers/, functions/src/api/middleware/, functions/src/api/mcp/ |
Overview
The API Gateway is a single Firebase Cloud Function (europe-west1, 512 MiB, 60 s timeout) built on Hono. It serves three protocol namespaces under one endpoint:
| Path | Protocol | Purpose |
|---|
/api/trpc/* | tRPC | Typed RPC for the React frontend |
/api/v1/* | REST | Public REST API for third-party integrations |
/api/mcp | MCP (Streamable HTTP) | AI agent tool server |
Request Flow
Client request
│
├── /api/health (unauthenticated health check)
│
└── /api/{trpc,v1,mcp}/*
│
├── CORS middleware (origin-based)
├── Logger middleware (emulators only)
├── authenticate() → AuthContext
├── rateLimiter()
├── tenantIsolation() → OrgContext
│
└── Route handler
Middleware Stack
CORS
Origin-based CORS with environment-aware configuration:
- Production: Specific allowed origins
- Emulators: Permissive
* origin
Authentication
File: functions/src/api/middleware/authenticate.ts
Three authentication paths, all resolving to a unified AuthContext:
| Path | Trigger | Flow | |
|---|
| Firebase JWT | Authorization: Bearer {token} | Verify ID token → lookup employee → resolve role permissions → get org tier | |
| API Key | X-API-Key: {key} or `Authorization: Bearer gc_(live | test)_…` | SHA-256 hash lookup in apiKeys collection → scope-to-permission mapping → usage tracking |
| Portal Token | ?token={id} or portal_ prefix | Direct lookup in portalTokens collection → limited read-only permissions | |
AuthContext shape:
interface AuthContext {
userId: string;
organizationId: string;
role: SystemRole;
permissions: Permission[];
tier: SubscriptionTier;
authMethod: 'firebase' | 'apiKey' | 'portal';
}
Rate Limiter
Token-bucket rate limiting per organization, with tier-based limits.
Tenant Isolation
Ensures all downstream data access is scoped to AuthContext.organizationId.
tRPC Layer
Router Architecture
File: functions/src/api/routers/index.ts
The root router merges domain routers:
| Router | Domain | Service Layer | Key Procedures |
|---|
health | System | — | Public health check |
projects | Projects | — | CRUD, phase transitions, risk assessment |
expenses | Expenses | ServerExpenseService | Submission, approval, receipt scanning, reports |
journals | Journals | ServerJournalService | Time entry, bulk operations, monthly submissions, reports |
employees | Users | — | Profile management, capacity, certifications |
grants | Grants | — | Pipeline, applications, milestones, closeout |
compliance | Compliance | — | Audit checklist, policy checks, risk matrix |
procurement | Procurement | ServerProcurementService | Requests, vendors, purchase orders, approvals |
mission | Mission | — | Goals, pillars, alignment metrics |
audit | Audit | — | Findings, reports, audit log |
risk | Risk | — | Risk register, matrix, mitigations |
Procedure Tiers
// Anyone can call (used for health checks)
publicProcedure
// Requires valid authentication (any auth method)
protectedProcedure
// Requires specific RBAC permission(s)
permissionProcedure([Permission.VIEW_PROJECTS, Permission.EDIT_PROJECTS])
Client Integration
The React frontend connects via @trpc/react-query:
// src/lib/trpc.ts
const trpc = createTRPCReact<AppRouter>();
// In components:
const { data } = trpc.projects.list.useQuery({ status: 'active' });
const mutation = trpc.expenses.submit.useMutation();
Domain Client Package (packages/domain-client/)
The domain-client package provides typed tRPC hooks that wrap individual router procedures with React Query configuration. This package is consumed by both the main web app and the Expo mobile app, ensuring a single data access layer across all clients.
// packages/domain-client/src/expenses.ts
export function useExpenses(projectId?: string) {
return trpc.expenses.list.useQuery({ page: 1, pageSize: 50, projectId });
}
export function useExpenseMutations() {
const utils = trpc.useUtils();
const create = trpc.expenses.create.useMutation({
onSuccess: () => utils.expenses.list.invalidate(),
});
return { create };
}
Current domain-client modules: grants, expenses, journals, contacts, documents, projects, uploads, widgets.
Each module exports:
- Read hooks —
use<Entity>List(), use<Entity>(id) — thin query wrappers with defaults
- Mutation hooks —
use<Entity>Mutations() — bundled mutations with automatic cache invalidation via trpc.useUtils()
Feature-level hooks (in src/features/<feature>/hooks/) can re-export or compose domain-client hooks with additional UI state (filters, pagination, loading).
REST API (v1)
Path: /api/v1/*
RESTful endpoints for third-party integrations. Same middleware stack as tRPC with API Key authentication as the primary auth method.
Webhook Endpoints
| Endpoint | Source | Purpose |
|---|
/api/v1/webhooks/stripe | Stripe | Payment events, subscription changes |
/api/v1/webhooks/postmark | Postmark | Email delivery status, inbound emails |
/api/v1/webhooks/kobo | KoboToolbox | Field data submission callbacks |
MCP Server
Path: /api/mcp
Model Context Protocol server for AI agent integration. Uses Streamable HTTP transport.
grantmaster_{domain}_{action}
Examples: grantmaster_projects_list, grantmaster_expenses_submit, grantmaster_grants_search
File: functions/src/api/mcp/tools.ts
Each tool declares:
| Property | Description |
|---|
name | grantmaster_{domain}_{action} |
description | Natural language description (sent to AI) |
inputSchema | JSON Schema for input validation |
handler | Async function with access to authenticated context |
requiredPermissions | RBAC permissions enforced before execution |
Permission Enforcement
function requirePermissions(ctx: AuthContext, perms: Permission[]): void {
for (const p of perms) {
if (!ctx.permissions.includes(p)) {
throw new McpError(ErrorCode.InvalidRequest, `Missing permission: ${p}`);
}
}
}
Credit Tracking
AI-powered MCP tools (those invoking Gemini) track credit usage per invocation.
Deployment
The gateway is deployed as a single Cloud Function:
export const api = onRequest({
region: 'europe-west1',
memory: '512MiB',
timeoutSeconds: 60,
}, app.fetch);
Environment Configuration
| Variable | Purpose |
|---|
CORS_ORIGINS | Allowed CORS origins (comma-separated) |
RATE_LIMIT_RPM | Default rate limit (requests per minute) |
MCP_ENABLED | Enable/disable MCP endpoint |
Key Files Reference
| File | Purpose |
|---|
functions/src/api/gateway.ts | Hono app definition, middleware mounting, route registration |
functions/src/api/routers/index.ts | Root tRPC router merging 6 domain routers |
functions/src/api/middleware/authenticate.ts | Three-path authentication middleware |
functions/src/api/middleware/rateLimiter.ts | Token-bucket rate limiting |
functions/src/api/middleware/tenantIsolation.ts | Organization scoping middleware |
functions/src/api/mcp/tools.ts | MCP tool definitions and registration |
functions/src/api/mcp/server.ts | MCP server setup and transport |
src/lib/trpc.ts | Frontend tRPC client configuration |