The boundary
Ntropii Workspace is what you reach when you runntro tenant create or call an MCP tool from an LLM. It holds metadata: who owns which tenant, which entities (SPVs, funds) belong to which tenant, which workflow versions have been pushed, which deployments are active, which scheduled runs are coming up.
It does not hold any financial data. No journal lines, no trial balances, no extracted documents. Those never leave Ntropii Tenant.
Ntropii Tenant is the runtime that actually executes your runbooks. One tenant binds to exactly one data platform — Ntropii’s managed Postgres (provisioned per tenant, isolated from every other tenant), a Snowflake account, or a Microsoft Fabric workspace. The credentials for the chosen platform live inside Ntropii Tenant; they never cross to Workspace.
Every tenant gets its own isolated infrastructure regardless of which strategy it picks. The boundary is the tenant; what differs is who hosts the platform underneath. With managed Postgres, Ntropii hosts the database and isolates per tenant. With BYO (Snowflake, Microsoft Fabric), the customer hosts the platform and Ntropii holds only the credentials needed to read from it.
When a workflow runs, it executes inside Ntropii Tenant against the chosen data platform. Status events (started, step completed, run finished) flow back to Workspace so you can monitor in the CLI / UI / MCP. The actual data — the documents, the trial balance, the journal entries — stays in the data platform.
Tenants and entities
A service provider (e.g. accounting firm, fund administrator, transfer agent, multi family office) or asset manager would signup as an organisation creating a workspace. “Fund Admin Workspace” is the organisation name in our example below who has a workspace. A tenant is a client of the workspace’s organisation — typically a fund. Acme Fund is a tenant. They map 1:1 to a data platform binding. An entity is a company, SPV, protected cell or incorporated cell within a tenant. A tenant can have many entities — each with its own schema in the underlying data platform, its own scheduled workflows, its own task history. “Acme Commercial SPV 1” is an entity belonging to the Acme Fund tenant, sharing Acme’s data platform but living in its own schema.Why the split
Three reasons, all rooted in the regulated nature of fund operations:Data sovereignty
Data sovereignty
Customer financial data — invoices, bank statements, GL entries, NAV history — never enters Ntropii Workspace. It stays in Ntropii Tenant. With managed Postgres that database is hosted by Ntropii but isolated to one tenant; with BYO (Snowflake, Microsoft Fabric) the data stays in the customer’s own warehouse under their own credentials and retention policies. If a customer’s compliance team requires that no fund data crosses a particular border, the BYO route deploys Ntropii Tenant in their own region (or their own cloud account) without changing the architecture.
Tenant isolation
Tenant isolation
Each tenant has its own runtime, its own credentials, and its own data platform binding. A bug in one tenant’s runbook can’t leak data to another tenant — there’s no shared execution surface. API keys are scoped to a tenant (the organisation is recorded only as provenance), so a leaked key affects exactly one client’s surface area.
Auditability
Auditability
The control plane has a clean audit trail of what was deployed when. The data plane has a clean audit trail of what was processed when. They join on workflow ID + run ID. An auditor reconstructing a NAV from six months ago can pull the runbook source from Workspace at that commit, the input documents from the customer’s data platform at that timestamp, and re-run the flow to verify the output bit-for-bit.
What this means for you
You’ll see this split in three concrete places:ntro tenant create --data-platform <choice>declares the data-platform strategy. Withmanaged-postgres, that’s all there is — Ntropii provisions and isolates the database. Withsnowflakeormicrosoft-fabric, the tenant binds to a config registered separately viantro integration add, which is the moment Ntropii Tenant gets the customer credentials.ntro workflow create --path ./runbooks/<slug>/uploads the runbook bundle to Ntropii Tenant’s worker, where it can execute against the data platform.ntro workflow create --entity <entity>(same command, with--entity) also creates a workflow binding so the runbook can be triggered for that specific entity.
Data lifecycle: from agent input to GL
Inside a tenant, data tightens through four stages on its way from a fuzzy agent submission to an immutable general-ledger entry. Each stage has its own type strictness, its own storage location, and its own reviewer. By the time a value reaches stage 4, every prior stage’s invariants are encoded in the type — you cannot post an unbalanced journal because the type system has already eliminated invalid states. This is the regulatory contract: “parse, don’t validate” is what makes a NAV reproducible from the source code plus its inputs, six months after the fact, bit-for-bit. JFSC, CSSF, FSRA, and Big-4 auditors all want the same thing — a paper trail showing exactly which checks ran when. The lifecycle below is what materialises that trail in code rather than policy.Stage 1 — Agent input (no validation)
Stage 1 — Agent input (no validation)
A coding agent or MCP client submits a payload via
ntro_task_data_ingest. The platform stores the raw data field as JSONB in ingest.submitted_records inside the tenant’s Postgres, alongside provenance (event id, source ref, received at, kind). Nobody type-checks the payload at this stage — agents send fuzzy strings, dicts, partial fields, locale-specific dates, whatever the source document looked like. The point is preserving exactly what was submitted so an auditor can reconcile back to the wire format.Stage 2 — Subledger PENDING (forgiving types)
Stage 2 — Subledger PENDING (forgiving types)
A runbook activity reads the JSONB payload and
model_validates it through a Pydantic boundary model declared with ntro.types.* types — ForgivingDate for fuzzy dates, eventually ForgivingMoney / Currency siblings as runbooks need them. These types coerce ISO strings, locale-tolerant strings, and natural language into typed values; on parse failure they degrade to None rather than raising. The activity then writes the typed values to a subledger (e.g. ledgers.expenses) with status=PENDING. This is the first rung where the data is typed; rows are still allowed to have NULLs because the next stage’s reviewer fills them in.Stage 3 — Subledger APPROVED (HITL review)
Stage 3 — Subledger APPROVED (HITL review)
A human reviewer sees every PENDING row in a
DATA_TABLE HITL step — typically a fund accountant during the period close. They correct mis-categorised vendors, fill in missing dates, flag ambiguous amounts, and approve the row. On approval the SDK flips status PENDING → APPROVED. Required fields are now present; lifecycle invariants hold. Nothing reaches the GL without passing through this gate.Stage 4 — GL journal (immutable, balanced)
Stage 4 — GL journal (immutable, balanced)
A separate runbook step (or a downstream workflow) rolls up APPROVED subledger rows into balanced journal proposals and posts them to the customer’s general ledger — Xero, QBO, or whichever GL the tenant binds to. The GL provider rejects anything that doesn’t balance, doesn’t reference a valid CoA code, or duplicates an existing posting (idempotent by external ref). Once posted, the row is immutable; corrections are issued as offsetting journals, never as updates.
JournalLine is constructed, its existence is evidence that the agent submission parsed cleanly, the reviewer signed off, and the CoA code is valid. That’s the regulator’s audit trail rendered as types — not a separate compliance artefact, but the same code that runs in production.
The next page walks through both setup steps.
Configure environment
Bind a data platform and set up your runbook repo.
API keys
Generate, scope, and rotate keys for CLI / MCP / SDK access.