Documentation Index
Fetch the complete documentation index at: https://grantmaster.dev/llms.txt
Use this file to discover all available pages before exploring further.
Introduction
Architecture & Codebase
March 2026 · Confidential — Internal Use OnlyTable of Contents
- What Is GrantMaster?
- The System at a Glance
- The Architecture: The Thinking Behind the Decisions
- The Domain: How the Business Works in Code
- How Data Flows: The Life of an Expense
- Security & Compliance Architecture
- Performance & Scale
- Codebase Health
- Vocabulary for Your Engineering Conversations
- Questions Worth Asking Your Engineering Team
- In Closing
1. What Is GrantMaster?
GrantMaster is a purpose-built grant management and financial tracking platform for NGOs, charities, and social enterprises. It covers the full arc of a grant — from discovery and application, through award and project execution, to compliance reporting and impact measurement — in a single unified product. The people who use GrantMaster every day are grant writers pursuing new funding, project managers tracking budgets and milestones, finance officers approving expenses, directors monitoring compliance, and auditors reviewing the paper trail. They are not technical users. The product must feel effortless to them while handling significant financial and regulatory complexity underneath. GrantMaster is a multi-tenant SaaS application. Each subscribing organisation operates in its own fully isolated environment — their data, users, and settings are completely separate from every other customer. This architecture is sometimes called a “walled garden per tenant”, and it is the first design principle from which most other decisions flow.“GrantMaster turns the grant lifecycle — which is usually managed across spreadsheets, email threads, and disconnected tools — into a single, auditable, AI-assisted workflow.”
2. The System at a Glance
GrantMaster is not a single monolithic application. It is a set of collaborating layers, each with a distinct responsibility. Understanding these layers is the key to understanding any conversation your engineers will have about the system.2.1 The Frontend — What Users See
The user interface is a React application — a modern, component-based web app that runs entirely in the user’s browser. It is built with React 19 and TypeScript, which means it is strictly typed: the compiler catches entire categories of bugs before they ever reach a user. The UI framework is Tailwind CSS with shadcn/ui, which gives GrantMaster its consistent visual language. The frontend is not just a pretty face. It owns a significant amount of business logic: form validation, state management, role-based access control, AI query interfaces, and real-time collaboration. The codebase is organised into twenty-two feature modules — each one a self-contained neighbourhood with its own components, data hooks, and business services. When an engineer says “the grants feature” or “the compliance module”, they mean one of these neighbourhoods.2.2 The Backend — Firebase
GrantMaster uses Google Firebase as its backend platform. This is not a traditional backend with a hand-written REST API server running on company-managed infrastructure. Instead, Firebase provides a suite of managed cloud services:- Firestore — a NoSQL document database. Think of it as a deeply nested filing cabinet, infinitely scalable, that the frontend can read and write directly (with security rules enforcing access control).
- Firebase Auth — handles user authentication, MFA, and session management. No passwords are ever stored by GrantMaster itself.
- Firebase Storage — stores uploaded files (receipts, documents, grant attachments). Files never touch GrantMaster’s code directly.
- Firebase Cloud Functions — serverless functions that run backend logic triggered by events (a new subscription payment, a document upload, a scheduled report). There is no always-on server to maintain.
2.3 The Intelligence Layer — AI
GrantMaster has embedded AI capabilities powered by Google Gemini, accessed via a framework called Genkit. The AI does three things: it assists grant writers in drafting application narratives, it powers a “Document Brain” that lets users query their uploaded documents in plain English, and it provides market intelligence such as funder benchmarks and funding trends. These are not experimental features — they are part of the core product offering.2.4 The Integration Ecosystem
GrantMaster connects to a number of external services that customers already use. Each integration is optional but deeply embedded when activated:| Integration | What It Does |
|---|---|
| Stripe | Billing and subscription management. All payment data stays on Stripe — GrantMaster never touches card numbers. |
| Postmark | Transactional email delivery. Every system notification — invitation emails, expense approvals, compliance alerts — goes through Postmark. |
| HubSpot | CRM synchronisation. Contact and relationship data syncs bidirectionally with the customer’s HubSpot account. |
| Xero | Accounting integration. Approved expenses can be pushed to Xero, eliminating double-entry bookkeeping. |
| Algolia / Typesense | Full-text search. Enables the fast, fuzzy search experience across grants, contacts, and documents. |
Internal notification system (src/features/notifications/) | In-house notification infrastructure managing the in-app bell icon + multi-channel delivery (Postmark email, FCM push, Sonner toasts). Replaces legacy Novu integration. |
| Sentry | Error monitoring. Every unhandled exception in production is captured, grouped, and surfaced for the engineering team. |
2.5 The Extension System
GrantMaster has a built-in extension marketplace — a plugin system that allows additional functionality to be installed per organisation without touching the core product. Extensions can contribute new dashboard widgets, new command-palette actions, and new EventBus integrations. This is the mechanism through which GrantMaster can serve specialised vertical markets (e.g. a healthcare grant compliance pack) without bloating the core product for every customer.3. The Architecture: The Thinking Behind the Decisions
Architecture is a set of deliberate choices, each made to solve a specific problem. Here are the five most consequential decisions in GrantMaster’s design, expressed in terms of the problems they solve.Decision 1 — Feature Modules, Not Layers
Most older applications are organised in horizontal layers: all the database code in one place, all the business logic in another, all the UI in a third. GrantMaster is organised vertically by feature: the grants module contains its own database access, business logic, and UI components in one place. This matters for team velocity. When an engineer works on the expense approval workflow, they never need to navigate to a shared “repository layer” or a central “service registry”. Everything they need lives insrc/features/expenses/. Adding a feature or fixing a bug requires touching far fewer files, and the blast radius of a change is contained to one neighbourhood.
Decision 2 — Firebase as the Backbone
Choosing Firebase over a self-managed database and API server was a deliberate bet on managed infrastructure. The engineering team’s time is too valuable to spend on database tuning, SSL certificate renewals, or server patching. Firebase’s Firestore scales automatically from one user to one million. Its security rules — a declarative access control language — ensure that no tenant can ever accidentally see another tenant’s data, even if there is a bug in the application code. The cost of this decision is that the team must think differently about data: Firestore is not a relational database. There are no joins. Data must be structured around the queries that will be run, not around abstract normalisation principles. The team has made thoughtful denormalization decisions throughout — for example, storing a list of funded project IDs on both the Grant and the Project document — so that common dashboards load with a single read rather than multiple sequential queries.Decision 3 — Strict Type Safety Everywhere
GrantMaster uses TypeScript throughout — both the frontend React application and the Firebase Cloud Functions backend. This is not a matter of preference; it is a risk management decision. The application handles real financial data. An incorrect type assumption — treating a budget amount as a string instead of a number, or accepting a null expense category — can produce incorrect reporting that damages a customer’s relationship with their funder. The team goes further than TypeScript alone. All external input — form submissions, API responses, data read from Firestore — is validated through Zod schemas before it touches any business logic. TypeScript catches structural errors at compile time; Zod catches semantic errors at runtime. Together they form a two-layer validation fence.Decision 4 — An Event-Driven Notification Architecture
GrantMaster uses an internal Event Bus — a publish-and-subscribe messaging system — to communicate significant events between different parts of the application. When an expense is approved, the finance service does not directly call the notification service, update the budget tracker, and write the audit log. Instead, it emits a singleEXPENSE_APPROVED event. Any module that cares about that event — the budget tracker, the compliance monitor, the notification service — picks it up independently.
This loose coupling is what allows the extension system to work. A third-party extension can subscribe to core events (a new grant won, a project phase change) and react to them without the core product needing to know the extension exists. It also means that adding a new side-effect to any business action — say, sending a Slack message when a report is submitted — requires no change to the core business logic.
Decision 5 — Zod Schemas as the Single Source of Truth
In many codebases, the database schema, the API contract, and the UI form validation are three separate things that drift apart over time. In GrantMaster, they are one thing: Zod schemas insrc/schemas/. The TypeScript types used by the UI, the validation rules applied to form input, and the structure enforced before writing to Firestore all derive from the same schema definition. Change the schema once, and the change propagates everywhere at compile time.
4. The Domain: How the Business Works in Code
GrantMaster models eleven distinct “aggregate groups” — clusters of related entities that represent the real-world concepts the system manages. Understanding these groups is understanding what the product does at a structural level.| Domain Group | What It Represents |
|---|---|
| Identity | Organisations (tenants), Users, Sessions, API keys, and Invitations. The root of everything. Every other entity belongs to an Organisation. |
| Grant Lifecycle | Five sequential entity types: Opportunity → Pipeline Entry → Application → Active Grant → Grant Report. A grant document changes shape at each stage because it carries different information. |
| Project Engine | Projects, their Budget Lines, Milestones, Tasks, and Deadlines. The operational unit through which grant funds are spent and outcomes delivered. |
| Finance | Expenses (with multi-grant allocation splitting), Invoices, and Receipts. The financial layer that connects money to projects and grants. |
| Time Tracking | Journal Entries (daily hours per project) and Monthly Submissions (bundled for manager approval). Known in Firestore as “timesheets” and “retrospectives” — a naming quirk to be aware of. |
| Compliance | Compliance Rules, Alerts, and the Audit Log. Every significant action in the system writes an immutable audit record. |
| Relations / CRM | Foundations (funder organisations), Contacts, Interactions, Segments, and Grantor Relationships. The relationship management layer. |
| Documents | Folders, Documents, and the AI Document Chunks that power the Document Brain search feature. |
| People & HR | Extended User profiles, Time-off Requests, Security Events, and scoped API Keys. |
| Platform & Extensions | Module Installations, Widget Definitions, and Widget Assignments. The extension system’s runtime state. |
| Partnerships & Credits | Partner Organisations, Referrals, Mission Credit wallets, and Credit Transactions. The partner and reseller programme. |
5. How Data Flows: The Life of an Expense
The best way to understand how the system’s layers work together is to trace a single transaction from start to finish. Here is what happens when a staff member submits an expense claim.- The staff member fills out the expense form in the browser. React Hook Form manages the field state; a Zod schema validates every keystroke. The Submit button is disabled until all rules pass. No malformed data can proceed.
- On submission, the frontend calls the Expense Service — a TypeScript class that inherits from BaseService, the shared parent of all data-access services. BaseService validates the data again (defence in depth), stamps the
organizationId, and writes the document to Firestore’s “expenses” collection. - The Expense Service emits an
EXPENSE_SUBMITTEDevent on the internal Event Bus, carrying the expense ID and relevant metadata. - The Audit Log service — which subscribes to all significant events — picks up
EXPENSE_SUBMITTEDand writes an immutable record to the “auditLogs” collection. This write happens automatically with no code in the Expense Service instructing it to do so. - The Notification service — also an Event Bus subscriber — picks up the event and calls the internal dispatcher to send an in-app notification and an email (via Postmark) to the relevant manager.
- The manager sees the notification, opens the expense, and approves it. This triggers an
EXPENSE_APPROVEDevent. The budget tracker adjusts the committed spend on the project’s budget line. The compliance monitor checks whether this approval crosses any configured thresholds. Another audit log record is written. - If the Xero integration is active for this organisation, a separate subscriber pushes the approved expense to the customer’s accounting system automatically.
6. Security & Compliance Architecture
GrantMaster handles real financial data for non-profit organisations that are accountable to funders, regulators, and the public. Security is not a feature — it is a structural property of the system.Multi-Tenancy Enforcement
Every Firestore document carries anorganizationId field. Firestore Security Rules — Google’s declarative access control system — enforce that no authenticated user can read or write documents belonging to a different organisation, regardless of what the application code does. This is a second line of defence: even a complete bug in the application logic cannot produce a cross-tenant data leak.
Authentication & Sessions
Firebase Auth handles all credential management. GrantMaster supports SMS-based multi-factor authentication and manages concurrent session limits (configurable per subscription tier). Failed login attempts trigger lockout policies after five attempts, with a fifteen-minute lockout window by default. Every authentication event — login, logout, failed attempt, MFA enrolment, password change — is written to an immutable Security Events log.Audit Trail
The AuditLog collection is write-once and append-only. It captures over eighty distinct action types across every significant operation in the system. Every record includes the actor’s user ID, the affected resource, the organisation, the IP address, and a metadata payload containing before/after diffs where relevant. Auditors have a dedicated view within the product that surfaces this log in a structured, filterable interface.API Keys
External integrations and automation scripts can authenticate to GrantMaster using scoped API keys. The raw secret is shown to the user once at creation time and never stored. What GrantMaster stores is a SHA-256 hash of the secret — a one-way transformation that cannot be reversed. Each key carries a list of explicit permission scopes, limiting what it can do even if it were to be compromised.Compliance Monitoring
GrantMaster has a built-in compliance engine. Organisations configure rules — for example, “flag any expense over €5,000 without an attachment” or “alert when reporting deadline is within 14 days”. When a rule’s condition is met, the system creates a Compliance Alert with a severity level and an SLA for acknowledgement and resolution. Critical alerts must be acknowledged within four hours. The compliance dashboard gives both internal teams and auditors visibility into open, escalated, and resolved violations.7. Performance & Scale
GrantMaster is designed to remain responsive under the data volumes that a well-funded NGO or a network of organisations generates over several years.Firestore at Scale
Firestore charges per read, write, and delete operation — not per server. It scales horizontally without any configuration change. The engineering team has made deliberate denormalization decisions (storing aggregated data in multiple places) to ensure that common dashboard queries require a single read rather than multiple sequential queries. Composite indexes are defined infirestore.indexes.json to support the specific multi-field query patterns the product requires.
Frontend Performance
The React application uses code splitting viaReact.lazy() — the browser only downloads the code for a given section of the app when the user first navigates to it. Long lists (expenses, contacts, journal entries) use virtual scrolling via react-window, meaning the DOM renders only the visible rows regardless of how many records exist in the database. These two techniques together mean that a GrantMaster instance managing ten years of data feels as fast to the user as one that went live last month.
Error Monitoring
Sentry is integrated into the frontend application. Every unhandled error in production captures a full stack trace, the user’s session state, the browser and OS, and the network requests that preceded the error. The engineering team triages Sentry issues daily. This means problems are found and diagnosed before customers escalate them.8. Codebase Health
A healthy codebase is not just about working software — it is about how confident the team can be when making changes. Here is how GrantMaster is maintained.Testing
GrantMaster has two testing layers. Unit tests — written with Vitest — cover individual services, hooks, and utility functions. The project enforces a minimum 60% code coverage threshold; the build fails if coverage drops below it. End-to-end tests — written with Playwright — simulate real user journeys through the browser. These tests are organised by user role (admin, manager, auditor) and run against the full application stack including Firebase emulators.Linting & Conventions
The codebase enforces naming conventions, import organisation, and code style automatically. Runningnpm run lint:fix before committing is the norm. Components use PascalCase. Hooks start with “use”. Services are camelCase. These are not stylistic preferences — they are enforced rules that allow any engineer to navigate any part of the codebase without friction.
Documentation
Every feature module has a companion engineering document (src/features/[feature]/[feature].md) describing its services, EventBus contracts, and data layer. Shared services have API reference documentation in docs/engineering/api-reference/. Architecture decisions are recorded in docs/overview/decision-log.md — a log of every significant design choice, the alternatives considered, and the rationale for the path taken. When an engineer says “that was an ADR decision”, they mean an Architecture Decision Record in that log.
The Team’s Workflow
Development uses Firebase Emulators — local simulations of Firestore, Auth, Storage, and Functions — which means engineers never test against production data. A seeded dataset of realistic test data is loaded automatically when emulators start. The full development environment starts with a single command:npm run dev:all.
9. Vocabulary for Your Engineering Conversations
Knowing the right terms is the difference between a productive technical conversation and one where you are nodding along. Here are the concepts your engineers will reference most frequently, with plain-English translations.| Term | What It Means |
|---|---|
| TenantScoped | Every database record that belongs to an organisation. The base requirement for any new data type. |
| BaseService | The abstract parent class all data services inherit from. Handles validation, org-scoping, logging, and event emission automatically. |
| EventBus | The internal messaging system. Services publish events here; any module can subscribe. Decouples business logic from side-effects. |
| emitCanonicalSystemEvent() | The standard helper function for publishing an event to the EventBus. If an engineer says “we forgot to emit here”, this is what they mean. |
| Feature module | One of the 22 self-contained areas of the codebase (grants, expenses, compliance, etc.), each with its own services, components, and hooks. |
| Zod schema | The canonical definition of a data structure. Validates input, infers TypeScript types, enforces constraints before any Firestore write. |
| Firestore Security Rules | Google’s declarative access control layer. The second line of defence after application-level checks. |
| Denormalization | Storing the same data in multiple places to avoid slow multi-step queries. A conscious trade-off in Firestore design. |
| Composite index | A pre-built query index on multiple Firestore fields. Required for any query that filters or orders on more than one field. |
| ADR | Architecture Decision Record. A written record of a significant design choice, the alternatives considered, and the rationale. |
| Code splitting | Loading JavaScript bundles only when a page is first visited, keeping initial load time fast regardless of total app size. |
| Virtual scrolling | Rendering only the visible rows of a long list in the DOM, keeping the UI fast even with thousands of records. |
| Extension / Module | A plugin that adds functionality to GrantMaster for a specific organisation without changing the core product. |
| GDPR consent | Managed by the ConsentService — tracks opt-in/opt-out for AI-powered intelligence features that process external data. |
| Internal notification system | In-house infrastructure managing the in-app bell + multi-channel delivery via Postmark (email) and FCM (push). |
| tRPC | A type-safe API layer that allows the frontend and Cloud Functions to share TypeScript types without code generation. |
10. Questions Worth Asking Your Engineering Team
A good CTO asks the right questions before reaching conclusions. Here are ten that will give you genuine insight into the team’s priorities, technical debt, and roadmap thinking.- Which EventBus events are we not yet emitting that we should be? The team has a gap report — ask to see it and understand the prioritisation.
- What is our Firestore read/write cost trend month over month? Firestore pricing is per-operation — understanding the trend reveals both product growth and potential inefficiencies.
- How do we handle a customer who wants to migrate away from GrantMaster? What does a data export look like, and how long does it take?
- What is the current test coverage percentage, and which features are below the 60% threshold?
- Which integrations (HubSpot, Xero, Stripe) are generating the most support tickets? Complexity often hides at integration boundaries.
- What are the three biggest Sentry error groups right now, and what is blocking their resolution?
- Where in the codebase do we have the most denormalized data, and do we have automated consistency checks for those fields?
- What is the extension marketplace strategy — are we building extensions in-house only, or do we plan to open it to third-party developers?
- How long does the full Playwright end-to-end test suite take to run, and is it blocking deployments?
- What does “multi-organisation user” mean for us — is a person who works across two NGOs a current or near-future requirement, and what would it take to support it?
11. In Closing
GrantMaster is a mature, thoughtfully designed product built by a team that cares about both code quality and the mission it serves. The architecture has been chosen for resilience and velocity — Firebase and the feature-module structure mean the team can ship new capabilities without accumulating the kind of structural debt that slows teams down over time. As incoming CTO, the most valuable thing you can do in the first thirty days is to walk a feature end-to-end with an engineer — from the Figma design or product spec, through the Zod schema definition, into the feature module, through the EventBus to its consumers, and out through a Postmark email or an in-app notification. That single journey will make every conversation in this document concrete.“The architecture is a set of deliberate choices. Your job is to understand the reasons, challenge them where the context has changed, and hold the team to the principles when the pressure is on to take shortcuts.”This document is a starting point. The engineering team’s documentation — especially the Architecture Decision Log, the EventBus reference, and the domain model — will take you deeper. The code itself is the ground truth.
GrantMaster · March 2026 · Confidential — Internal Use Only