Skip to content

Provisioning & Multi-Tenancy

Atelier turns a declared application into a running, fully configured tenant — automatically. A brand-new tenant does not get hand-configured. It comes up switched-on, by copying a curated catalog of configuration from a single, polished template application and re-homing it into the new tenant's own space.

This is the platform's core ergonomic promise: the same operator editors serve every tenant, with no per-tenant special-case code. Every configuration surface is keyed to the caller's resolved tenant, so authoring against the template and authoring against a live tenant are the same action against different data.

Provisioning is the moment the platform's three planes — data, execution, and surface — all become tenant-local at once, in a single ordered, idempotent, self-healing operation.

The three planes of configuration

Every configuration row belongs to one of three planes, decided entirely by which tenant owns it — never by a code branch. The rule of thumb: anything the fork copies belongs to a template or tenant; anything structural it merely references is shared vocabulary.

ConfigurationPurposePlane
Entity schemasType definitions, shared and tenant-independentVocabulary
Event, surface, and template registriesShared catalogs every tenant referencesVocabulary
Tenant catalogThe roster of tenantsVocabulary
Application definitionsThe app catalog — code, icon, features, verticalTemplate
Entity membershipWhich entity types each application surfacesTemplate
Action definitionsExecution-plane actions, their parameters and side effectsTemplate
Action placementsWhere an action appears on a surfaceTemplate
Notification configTemplates, recipient rules, and subscriptionsTemplate
Report templatesThe report catalogTemplate
Portal pagesCitizen-facing portal surfacesTemplate
NavigationAdmin sidebar structureTemplate
Presentation configPer-tenant list layouts, dashboards, and child collectionsTenant
Root organization & membershipThe per-tenant org tree and its adminsRuntime

Vocabulary is shared and referenced everywhere. Template rows are the curated worked example that gets forked. Tenant and runtime rows are what each tenant ends up owning after a fork. Plane identity travels with the data, so the entire system stays tenant-isolated by construction.

How configuration is declared

Operators author configuration on the template through three equivalent surfaces — all writing the same configuration, all keyed to the resolved tenant:

  1. The authoring hub — a staff-admin UI where you edit applications, actions, placements, notifications, navigation, and presentation visually, with a live preview.
  2. Provisioning sheets — declarative YAML that describes entities, actions, public surfaces, views, notifications, portal pages, and identity presentation, imported by the bootstrap engine.
  3. Configuration APIs — programmatic endpoints for the same row classes.

A tenant author uses the identical hub editor against their own forked copy — their tenant is resolved from their session, and no extra code exists for the tenant case. Aggregates and KPIs are authored as declarative views in the sheet, never as bespoke endpoints.

This is the "declare, don't code" model end to end: you describe what an application is, and Atelier generates the running surfaces.

How a tenant is provisioned

Forking a tenant runs as two structural phases followed by three derived phases that re-realize what a plain row-copy cannot carry.

  • Phase 1 — per-application spine. For each application, copy its core: the application definition, its entity membership, its actions, and its presentation config. Everything feeds one fork-wide identity map, so cross-application references resolve cleanly — including adapter applications that intentionally point at another app's actions. Re-running converges: already-forked applications are matched and reconciled rather than duplicated.
  • Phase 2 — tenant-wide config. Copy everything else marked forkable — notifications, reports, portal pages, navigation, and any new forkable type. The plan is derived from the live schema: any type declared forkable is picked up automatically, its relationships rewritten to the new tenant, and the whole set ordered by dependency. A row copies only when its anchor resolves; references to shared base configuration are preserved intact.
  • Phases 3–5 — derived surfaces. Re-realize the things a row-copy can't express on its own: geospatial layers, citizen read access, and citizen submit/vote access. These ensure the new tenant's portal and back office work the moment provisioning completes.

Provisioning is idempotent — safe to re-run — and self-healing: on failure it reverses its own work in compensating order, leaving no half-built tenant. A fork requires the target tenant and its root organization to already exist, and fails fast if they don't.

For brand-new environments, Atelier runs a single bootstrap pipeline that brings a fresh stack up fully switched-on with no hand-seeding: register the shared schemas, provision the template, import the sheets, seed the shared registries and the application catalog, then fork the first tenant — and finally seed that tenant's first staff administrator (the one identity a fork deliberately does not create, so that back-office access is always an explicit, auditable step).

Per-tenant editing

Forking is copy-on-create: each tenant owns an independent copy from the moment it's provisioned. Tenant authors edit their own configuration freely through the same hub, and their changes stay theirs.

Because copies are independent, later edits to the template do not silently rewrite live tenants. Each tenant evolves on its own terms — a deliberate isolation guarantee, not a side effect.

Extending the model

Atelier is built to grow without touching the provisioning engine:

  • A new forkable configuration type — declare it forkable in a sheet, and it's picked up from the live schema automatically. No engine change.
  • A new application or vertical — author its sheet and list its entity types; it forks as a new application spine. A guided workflow scaffolds this end-to-end.
  • Identity and control-plane presentation — declare it in a dedicated sheet block with an explicit application binding.
  • Cross-vertical reach — add the pairing to the cross-vertical list; entity types are referenced across verticals, never duplicated.

Guarantees

  • Plane identity travels with the data, so tenants are isolated by construction — never by a conditional in code.
  • A type forks only when explicitly marked forkable, keeping structural vocabulary and shared graphs out of per-tenant copies.
  • Control-plane applications are shared by design and never forked.
  • Provisioning is idempotent and compensating: re-runs converge, and failures roll back their own work.
  • A row copies only when its anchor resolves; references to shared base configuration are preserved.
  • Forks are copy-on-create with no live inheritance — each tenant's configuration is its own.
  • Every configuration surface keys to the resolved tenant with no fallthrough; cross-plane authoring is permitted only for callers holding the matching grant.
  • A fork establishes a tenant's configuration and citizen access; staff access is always an explicit, separately seeded step.

Example: the identity of an application

Every sheet opens with an identity block. Its code is the application's identity — everything the sheet provisions belongs to it — and route_namespace mounts the application's public pages so multiple verticals can coexist on one tenant.

yaml
identity:
  code: participatory-budget
  name: Participatory Budget
  description: Citizens propose projects, staff review them, and the public votes.
  icon: users-three
  nav_order: 40
  is_active: true
  route_namespace: budget
  features:
    map: true
    calendar: true

Importing this on the template plane creates the worked example. Forking it onto a tenant copies every part of the application and rewires it to run standalone under the tenant's own identity.

Atelier — declare your application, generate the product.