The first dangerous version of Klevar Docs was visually convincing.
The PDF looked right. The footer looked official. The brand colors matched. The problem was that some of the text was hardcoded around one entity. That is a quiet failure in a multi-entity company. It passes screenshot review and still leaks the wrong legal identity.
Klevar has three entity contexts:
- Klevar FZE, a Nigerian free-zone entity.
- Klevar LLC, a United States entity.
- Klevar Ltd, a Nigerian private company limited by shares under CAMA 2020, currently dormant.
The Ltd correction is not cosmetic. Nigerian Ltd can issue only letterhead, compliance letters, and board resolutions while dormant. It cannot issue invoices, proposals, contracts, receipts, statements, quotes, or credit notes without changing the compliance posture of the business.
That means templates cannot be trusted to "know" an entity. The document must carry a frozen entity snapshot assembled from seed and database state at render time.
The snapshot builder is now the contract between legal configuration and document output. It captures names, registration data, address, brand, officer data, banking configuration, payment link providers, allowed document types, blocked document types, and compliance posture.
The code makes the template consume data instead of memory:
export function captureEntitySnapshot(entity: EntitySnapshotInput): EntitySnapshot {
const legal = buildLegalSnapshot(entity);
const branding = buildBrandingSnapshot(entity);
const banking = buildBankingSnapshot(entity);
const paymentLinks = buildPaymentLinkProviderSnapshot(entity);
return entitySnapshotSchema.parse({
entity_id: entity.entity_id,
legal,
branding,
banking,
payment_links: paymentLinks,
allowed_document_types: entity.allowed_document_types ?? null,
blocked_document_types: entity.blocked_document_types ?? null,
compliance_posture: entity.compliance_posture ?? null,
});
}
That object becomes part of document history. If a company address changes later, the old invoice does not silently update. If a bank account changes later, the old PDF still proves what was shown at issue time.
This was also where the PRD's "single tenant, multi-entity" language became concrete. The system does not have tenants. It has one group and several legal entities. Every data-bearing row is scoped by entity_id. Queries do not add entity scope as a convenience. They start with it.
Templates were then forced into a narrower role. A template can decide how a field appears. It cannot invent legal identity. It cannot assume a bank account. It cannot decide that Ltd behaves like LLC because the names look similar.
The regression tests became entity-neutral. Render the same template family under different entities and check that identity strings do not leak. The test is dull in the best possible way: if a future change hardcodes an entity name, the failure points at the exact class of mistake.
The snapshot contract also changed how I think about proprietary business software. The hard part is not hiding complexity behind internal tools. The hard part is making internal tools admit the business rules they carry.
For Klevar Docs, one of those rules is now explicit:
Nigerian Ltd is dormant. Treating it like a generic trading company would be wrong. Treating it like a brand variant would be worse.