Skip to content

Platform guarantees

Atelier holds a set of guarantees that span every subsystem — authorization, tenancy, configuration, rendering, analytics, and data evolution. They hold no matter which application you build, which tenant runs it, or how you extend it. Build on these as load-bearing assumptions.

Authorization fails closed, by construction

Every read and write is authorized before it runs. Atelier resolves the caller's grants against your application's declared policy on each request, then compiles those grants directly into the query.

  • If the authorization service is unavailable, the request is denied — never served unprotected.
  • If a caller has no matching grants, they see nothing: the query resolves to a deny-all, not an open result.
  • Public, citizen-facing reads pass two independent gates — one confirming the surface is publishable, one enforcing row-level access — and fail closed if either is unmet.
  • The same access criteria govern both whether an action's button appears and whether the action is allowed to execute, so what a user can see and what a user can do never drift apart.

You get tenant isolation and least-privilege access as the default state of the system, not as something you remember to add.

Tenant identity is derived, never asserted

The acting tenant is always derived from the caller's verified identity token and propagated on every downstream call. Inbound request headers can never set or change the tenant. The one bridge that lets a privileged author act across tenants is honored only for callers holding the specific authoring grant for that operation, checked independently of the data-plane policy. The result: a request can only ever read and write within the tenant its credentials prove.

You declare capabilities; you don't code them

Policy, actions, surfaces, and notifications are configuration rows, compiled per request — not branches buried in application code. The stored configuration is the runtime contract; there's a single source of truth and nothing shadows it.

Adding a capability means adding a row — and, if it introduces a genuinely new building block, registering that block in the catalog. There's no parallel code path to keep in sync.

Every tenant comes up fully configured

A new tenant is provisioned by forking a complete template application in one shot. Every entity, action, surface, view, and notification marked as forkable is copied into the new tenant and rewired into its own isolated plane.

  • Copy-on-create, with no live inheritance. Each tenant owns its configuration outright; changing the template never reaches back into tenants already created.
  • One set of authoring tools serves all tenants. Every authoring surface keys on the caller's resolved tenant, so the same editors operate safely on whichever tenant the author belongs to.

You get clean tenant isolation and predictable, repeatable provisioning from the very first request.

One renderer, presentation only

A single shared renderer drives every surface — staff and citizen alike. It dispatches descriptors to widgets by catalog key and carries no editing or authoring metadata; that lives in the authoring layer, cleanly separated from presentation.

Only known, registered widget keys ever reach the page. This keeps rendering predictable and the visual language consistent across every application without coupling the components to how they're edited.

Aggregates are views, never bespoke endpoints

Every count, tally, and group-by is defined as a declarative view — the single aggregate primitive — and surfaced either as a staff analytics card or as a citizen-facing public feed. There are no hand-written statistics endpoints to drift or leak.

A view freezes its author's access scope — and that of every joined entity — into the view itself, so an aggregate can never be widened or narrowed by the requesting tenant. Citizen-facing tallies expose totals without ever leaking the rows behind them.

Idempotency is guaranteed, and failures stay retryable

Actions that must run exactly once are guarded by deduplication ledgers with three properties that hold together:

  • The ledger entry is written after the guarded action succeeds, so a failed attempt can always be retried.
  • Writes are insert-or-get under a uniqueness constraint, so concurrent callers can't double-fire.
  • Composite keys are order-independent, so the same logical event always maps to the same guard regardless of argument order.

The net effect: notifications and side effects fire once on success, and transient failures recover cleanly instead of being permanently blocked.

Schema evolves additively

Your data model only ever grows. Atelier never deletes a column, and configuration changes are additive by construction — re-importing a configuration you've already applied is a no-op. Tenant scoping is an explicit, required decision in the configuration grammar, so isolation is always a deliberate choice on the record, never an accident.

Because evolution is additive and round-trips cleanly to a no-op, upgrades and re-provisioning are safe to repeat, and existing clients keep working across changes.

A clean engine-and-application boundary

The core data engine sees only ordinary tenant-scoped rows. All higher-level meaning — vocabulary, templates, tenancy, and public-surface semantics — lives in the application layer above it. This boundary is deliberate: it keeps the engine simple and portable while letting each application define exactly the experience it needs on top.

Atelier — declare your application, generate the product.