Skip to main content
One Alfiz Application owns its tables outright. Before 0.8.0 that was literal: point a second Application at the same store and the two collided, silently and destructively. Unfiltered lists returned both applications’ rows (a privilege leak), the catalog head and invalidation-log singletons clobbered each other in turns, event sequences interleaved, audit hash chains forked, and caller-assigned ids denied each other’s imports. 0.8.0 adds two layers, each useful alone:
  1. Partitioned storage. Several Applications share one set of tables with strict isolation and zero interaction. No contract changes; this is the default behavior once you set a partition.
  2. The mesh. An opt-in topology on top, in which applications can be granted read or write access to each other’s partitions. Because the shared database already provides the cross-application vantage that federation’s registry exists to supply, a mesh delivers federation-shaped capability (first-party imports, a centrally administrable org) with no new infrastructure and no network hops.
The topology family runs standalone → mesh → federated, with partitioned storage as the mesh’s degenerate case: a mesh with no edges.
This page is about several applications of one organization sharing a database. Serving many customer organizations is a different problem with a different answer. See Multi-tenancy.

Layer 1: partitioned storage

Both database drivers take a partition option that pins the driver, at construction and irrevocably, to one application’s slice of the backend. The recommended key is the application’s primary catalog namespace, which is already unique org-wide.
On Prisma the discriminator is unforgettable by construction: the delegate types require app on every query shape, so a query inside the driver that omitted the partition fails to compile rather than scanning every tenant. On Mongo, choose the mechanism: the collection prefix means no query can forget the predicate (physically stronger, but a parallel collection set per application), while layout: "rows" keeps one shared collection set (compound { app, id } document ids, app-led indexes, partition-scoped lease keys) and routes every query through one partition-scoping helper. "rows" requires an explicit partition, and the rule is one layout per shared Db. On every path, isolation is pinned by a published conformance suite (isolationContractCases in @alfiz/application/driver-suite), and third-party drivers claiming multi-app support are graded against the same bar. Two rules, stated plainly:
  • All partitioned or none. An application that omits the option lands in partition "" (Prisma) or the unprefixed collections (Mongo), beside any legacy data. The boot log’s driver line includes the partition; check it.
  • Isolation here is logical and cooperative. Every co-tenant holds credentials to the whole table set, so this protects against bugs and accidents, not against a compromised co-tenant. When you need isolation that holds adversarially, use physical partitioning: a schema or database per application, with separate credentials. Both drivers support that with zero Alfiz configuration.
At this layer the applications interact in no way. No registry, no edges, nothing shared but the connection pool.

Layer 2: the mesh

The mesh starts from one design invariant: the storage seam never learns cross-partition addressing. A driver physically cannot reach a neighboring partition, and there is no listGrants({ partitions: [...] }). A raw cross-partition write would bypass the owning application’s catalog validation, graph integrity, audit chain, and invalidation events, which for an authorization system is the cardinal failure. Instead, cross-application access is composition of Applications over the shared store, through exactly one road:
openPeerApplication consults the registry (below) for an edge authorizing the access and refuses loudly without one. It then reads the target’s published catalog head from the target’s partition, which is sitting in the same database, and constructs a full in-process Application over a driver pinned to that partition. Every operation flows through the target’s own enforcement; audit lands in the target’s log, where its operators expect it, naming the cross-app actor; and the handle re-checks the stored catalog version before every catalog-validating write, so validation never runs against a stale contract. Read-mode handles refuse every write at the seam. The admin-dashboard topology falls out for free: enumerate the mesh’s members, open a peer Application per partition, and reuse the entire headless admin kit against each one.

The registry

A reserved meta-partition (__mesh) holds the mesh’s one shared document: members, edges, and the optional org designation.
Edges are coarse on purpose: whole-partition read or write, where write implies read. The registry has one writer by discipline (the org partition’s owner, like every other piece of org-domain data), versions monotonically, and audits every change. It gives you three things: discoverability (the dashboard’s member list; namespace-to-partition resolution for imports), boot verification, and answerability, meaning one place that answers “who can touch my partition?”.

First-party imports

Your catalog already declares the namespaces it references but does not own. In a mesh, those imports stop being vocabulary-only:
The registry maps each imported namespace to its publishing partition, and a read edge authorizes the resolution. No edge means a loud no_mesh_edge refusal rather than a silent degrade. Closure assembly then merges the neighboring partition’s global-scope grants and role definitions for the session’s subjects into evaluation: if billing granted Jane Okafor its “Invoice auditor” role globally, a check in the docs application sees it. Checks still run entirely in your process, because the rows are sitting in the same database. Invalidation crosses partitions the same way everything else does, through the shared store. startMeshEventPoller(app, mesh) tails your own event log plus each partition you hold an edge to, so a revoked billing role invalidates docs-side caches at the same staleness bound as local writes. The v1 limit, stated: instance-scoped peer grants stay out of the merge. They reference resources only the owning application can resolve, and cross-app hierarchical scope matching would require the peer’s ancestry resolver, which is exactly the path that fails closed. Global-scope grants and roles cover the “people holding permissions and roles from other applications” case.

Write edges

“Application A writes application B’s partition” does not break the single-writer rule, framed correctly: authority stays singular, being B’s Application semantics, and the mesh adds a second process executing them. That is exactly one application deployed on multiple nodes, which the drivers already handle. The multi-node requirements therefore become mandatory for any partition with an inbound write edge, even if every application is single-node, and openPeerApplication in write mode enforces them fail-loudly:
  • Cross-process runExclusive. Built into @alfiz/mongo (its lease); on Prisma, supply options.lock or write mode refuses.
  • The persisted event log. Invalidation must cross process boundaries, and in a mesh the shared log is the transport: the peer’s write appends to the target’s partition log, the target’s processes poll their own log, caches invalidate.

The org partition

The homing rule gains a third clause: standalone, the Application is the org root; in a mesh, the org partition is; federated, Alfiz Cloud is. Designate a partition in the registry and it homes organizational-domain data for the whole mesh (groups, roles, the reporting hierarchy, global-scope grants and revokes): one copy, one writer, zero sync lag, because every member was already reading this database.
Members reject local org-domain writes exactly as federated ones do, and route them through a peer Application over the org partition instead. Promotion reuses the same org-snapshot machinery the federation promotion path uses, which is what makes the mesh a genuine stepping stone: federating later lifts the org partition and the registry into Alfiz Cloud with the dataset already centralized.

The threat model, stated plainly

Mesh isolation is cooperative rather than adversarial. Every member holds credentials to the whole table set; partition discipline and access edges are enforced by driver construction and boot checks, not by the database. A compromised or misbehaving member can read and write any partition with the raw database client, and no mesh mechanism prevents it. Among first-party applications of one organization that is a fine trade, since the threat model is bugs and accidents and the conformance suites pin exactly those. It is still a trade. Deployments needing adversarial isolation between applications use physical partitioning with separate credentials, and pay federation’s costs for centralization. The guarantee differs by mechanism, and you should know which one you are getting.

Choosing a topology