Muhammad Mudassir
Founder & CEO, Cognilium AI
The situation
A multi-family office answers a narrow set of questions for wealthy families: what do we own, what have we committed to but not yet funded, what falls due, and what did we actually agree in that document three years ago.
The second of those is the dangerous one. A commitment that has been signed but not yet called is a liability that does not appear in a bank balance, and it arrives on someone else's timetable. An office that cannot see its unfunded commitments at a glance is one capital call away from an avoidable liquidity problem.
The answers existed. They were spread across accounting software, a shared drive of private placement memoranda, subscription agreements and cap tables, a calendar, and an inbox. Assembling any one of them meant a person opening files and reading — which made the answers slow, and made them only as accurate as whoever last read the paperwork.
The harder constraint was structural. A platform serving several families cannot let one family's data touch another's, ever, for any reason. The usual industry answer is to run a separate deployment per client. That is safe and it does not scale: every improvement has to be shipped as many times as you have clients.
What we did about it
The platform answers questions in plain language, and the isolation is enforced by structure rather than by discipline.
Investment documents — private placement memoranda, subscription agreements, SAFEs, cap tables — are read automatically and turned into structured records, so the terms buried in a PDF become facts the system can answer with. Those facts are linked into a graph connecting companies, investments, entities and documents, which is what makes "what do we own through this entity" a question rather than an afternoon.
Seven specialists sit behind one router, so a person asks a question rather than choosing a tool. And each client organisation is bound only to the integrations it has actually connected — the system does not know the others exist for that tenant. Every one of the platform's 70+ endpoints sits behind that boundary, with 60-plus permission scopes across five roles. Cross-tenant access is not policed after the fact; there is no path that reaches it.
The technical detail, for the person who will be asked whether this is sound.
Every request hits TenantContextMiddleware which reads the immutable Firebase custom claim, fetches organizations/{orgId}/permissions, and attaches the merged context to request.context. The supervisor factory takes that context and assembles a fresh Agent: which specialists to register, which tools to bind to each, which system-prompt fragments to splice in. The output is cached by {orgId, integrations_hash} — cold path ~150ms (Firestore reads + tool wiring), warm. Pub/Sub invalidates the cache <1s after any integration change.
Layer one (tool registration): the factory only binds tools the org is allowed to use. The LLM literally does not know the others exist — system prompt is shorter, hallucination cannot reach into a non-connected Salesforce. Layer two (per-tool permission check inside the handler): every tool starts with assert_permission(request.context, "salesforce:read"). Defense in depth — the factory layer can be bypassed accidentally; the tool layer is enforced last. Together they cover both gaps.
Parser (PDF / DOCX / XLSX text extraction) → Classifier (document type via Gemini 2.0 Flash) → Evidence Extractor (supporting text per field) → Extractor (structured field extraction via Gemini 2.5 Pro) → Validator (cross-field consistency: dates, party names, amounts) → Scorer (confidence per field) → Graph Writer (Neo4j upsert with cross-document entity linking). The pipeline handles PPMs, SPAs, SAFEs, and cap tables. Confidence below threshold triggers a human-review queue rather than silent low-quality writes.
Every collection lives under organizations/{orgId}/. There is no top-level documents collection — only organizations/{orgId}/documents. A query that omits the orgId path segment fails at dispatch. The system has no way to "accidentally" query across tenants because the path itself enforces scope. 60+ permissions follow the format "{resource}:{action}:{scope}" — examples: "documents:read:org", "documents:*:org", "billing:read:platform". Five roles bundle them; wildcards expand at check-time, not at storage.
Gmail push notifications land in a Cloud Pub/Sub topic the backend subscribes to; new threads index into the per-org Vertex AI Search engine within ~1 second. Google Drive uses watch-channel webhooks with polling fallback (Drive's webhook reliability is good-but-not-perfect; the poll catches missed deliveries). QuickBooks uses scheduled syncs on Cloud Scheduler — 8 jobs cover financials, transactions, and entity reconciliation.
TL;DR
A family office's records lived across accounting, a document drive, a calendar and an inbox. How one platform made them answerable — for many families, from one deployment.
Strip away the sophistication and a family office does something simple: it knows what a family owns, what it has committed to, and what falls due. Every other service rests on that being reliably true.
In practice the answer was scattered. Accounting lived in one system, the paperwork that defines the commitments lived in a document drive, dates lived in a calendar, and the context for all of it lived in an inbox. Nothing was missing. It was simply that no one could see it at once.
So the answer to "what do we own through this entity" was not a lookup. It was an afternoon, and its accuracy depended on whoever last read the subscription agreement.
Of the questions a family office answers, the one that does damage is unfunded commitments — capital that has been legally committed to a fund or a deal but not yet called. It is not in the bank balance. It does not appear in a profit-and-loss statement. And it is drawn down on someone else's schedule.
An office that can see invested, committed, funded and unfunded in one place is managing liquidity. An office that has to assemble that view from subscription agreements when a call arrives is reacting to it. The same platform view that answers "what do we own" has to answer "what could be asked of us next quarter", or it has answered the easy half of the question.
Adjacent to it sits reconciliation: what the documents say a position is, against what the accounting says it is. Those two drift, quietly, and the drift is only ever found by someone comparing them. Making both readable by the same system is what turns that from an audit exercise into a view.
The terms that actually govern a position are written in prose: a private placement memorandum, a subscription agreement, a SAFE, a cap table. They are the authoritative record, and they are unreadable at scale. A person extracting those terms by hand is slow and occasionally wrong, and neither failure announces itself.
So the documents are read automatically and turned into structured records. The effect is not that reading gets faster — it is that a term buried on page forty of a PDF becomes a fact the system can answer with, and can show its source for.
Those facts are then linked: this entity holds this position in this company, evidenced by this document. Ownership questions are rarely one hop deep, and a graph is what makes the second and third hop answerable instead of manual.
A platform serving several families has one absolute rule: one family's data must never reach another's, under any failure, ever. The comfortable way to guarantee that is to run a separate deployment per client.
It works, and it quietly caps the business. Every improvement ships as many times as you have clients, every client drifts onto a slightly different version, and the cost of serving the tenth family is the same as the first. The economics of software stop applying.
The alternative is to make isolation structural. Each organisation is bound only to the integrations it has actually connected — for that tenant, the others do not exist. All 70-plus endpoints sit behind that boundary, with more than sixty permission scopes across five roles. Cross-tenant access is not something the system detects and blocks; it is something the system has no route to.
Underneath, different questions need different competence — financial, legal, document, calendar, correspondence. Exposing that as five tools would push the work back onto the user, who now has to know which one to ask. A router in front of the specialists means a person asks their question and the system decides who answers it.
If you are building software for an industry where each client's data is confidential from every other client's, you will be offered the per-client deployment early, and it will look like the responsible choice. It is worth understanding what it costs before you accept it — not in infrastructure, but in the rate at which you can improve the product for anyone.
The second lesson is about documents. In finance, law and insurance the authoritative record is prose, and most platforms treat it as an attachment — something to store and link to. Everything interesting starts when the contents of that prose become facts the system can reason over, with the document still attached as evidence.
Find answers to common questions about the topics covered in this article.
The engineering writeups that explain how the system was built.
Building a multi-tenant agent platform on Google ADK where the supervisor binds only the tools each org has paid for and integrated — without forking the agent definition per tenant.
Hard tenant isolation on Firestore: middleware, immutable claims, wildcard permissions. The architecture that makes leakage structurally impossible.
Two-tier retries, atomic DynamoDB chunk claims, and checkpoint-based cancellation — the failure-recovery layer that lets a multi-agent contract review pipeline finish even when 5% of LLM calls fail.