Appearance
Analytics & the Citizen Portal
Every Atelier application has two outward audiences: citizens, who experience a branded public portal, and operators, who measure and report on the application through dashboards and documents. You never hand-code either one per application. A surface comes alive because you authored a row — a public data surface, a portal page, a report template, or a view definition — and Atelier generates the running experience from it.
This is also the platform's trust boundary, and it is fail-closed by construction. The same authorization engine that governs staff access is applied here to anonymous and citizen audiences, so publishing a page and opening it to the public are a single authoring act: declare what's public, and the access rules that make the read safe are emitted for you.
The building blocks
You assemble outward experiences from a small set of declared objects.
| Object | What it does |
|---|---|
| Public data surface | Declares exactly what a citizen may read from one entity — which records (by published state), which columns, which fields are filterable or searchable, and whether a record is owner-scoped. Its presence is what opens public read access for that entity. |
| Portal page | Binds a route to a page template plus per-page overrides. Publishing a page emits the access rules that make its data legal to read. |
| Portal | The per-application portal chrome: brand, logo, theme, navigation, enabled modules, and locales. |
| Page template | A reusable slot-and-block layout that portal pages reference and customize. |
| Report template | Operator-authored HTML + CSS bound to an entity type, rendered to a polished PDF. |
| View | An aggregate or KPI definition — counts, tallies, group-bys — expressed natively against your data. Views are the single sanctioned source for analytics. |
Everything is declared, not coded
You author each outward surface as data, in your application sheet or in a built-in editor.
Expose data to citizens. Add a public data surface for an entity. You name the published states that may appear, the public fields, an optional owner field, and whether the source is an entity or a view. That declaration is all it takes to open public read access — scoped to exactly those records and columns.
yaml
public_surfaces:
proposal:
published_states: [validated, in_voting, winner]
status_field: status
public_fields: [title, summary, category, status, created_at]
filterable_fields: [category, status, "created_at:range"]
searchable_fields: [title, summary]
is_active: trueAdd a portal page. Declare a route, the page template it uses, and any overrides. Routes are authored relative to the application and mounted under its own namespace, so pages never collide across applications. Publishing the page emits its citizen access rules automatically, and navigation is derived from the pages you publish.
Brand the portal. A single portal row carries logo, theme tokens, navigation, enabled modules, and locales — the chrome wrapped around every page.
Author reports. Write a report template — HTML and CSS bound to an entity type — in the report editor or seed it from a sheet. The download button appears wherever that entity is shown; child collections are pulled in by declaring which relations to expand.
Build analytics. Declare a view for any aggregate or KPI, then surface it. Staff-facing KPIs become dashboard cards wired into a dashboard configuration; citizen-facing aggregates (a vote tally, an open-by-category count) become a public data surface over the view. A built-in dashboard editor lets you compose these on the same shared renderer that powers the rest of the product.
Template-and-fork tenancy
Shared, catalog-level definitions — public surface policies, page templates, view specs — are imported once and available everywhere. The outward experience itself ships as a template application: a complete, worked example with report templates, public surface policies, portal chrome, and portal pages already in place.
When you stand up a new application, Atelier forks that template into it, copying and re-wiring every surface into the new application's own namespace. From that point on you edit your copy with the same editors and zero special-case handling. Each request is served strictly under its own application's policy, so one application never sees another's configuration — and an application with no customization simply gets the safe default.
Public reads are safe by construction
The public read path is a deliberate, narrow trust boundary, and every guarantee below holds without per-application code.
- Public endpoints stay public. The public read gate is a property of the endpoint, not of who is calling. Authenticating on the portal can never reach staff-only surfaces.
- Fail-closed by default. If authorization is unavailable, the request is refused. With no matching rule, the result is an empty page — never the whole table.
- Citizens never see less than anonymous visitors. A signed-in citizen's view is the union of their own access and the public baseline, so logging in only ever adds.
- A disclosure floor, never a leak. Public columns are restricted to exactly what you declared, falling back to a minimal safe set of identifying and status fields. Declared filters and search can only narrow what's visible, never widen it.
- Owner-scoped fields stay private. A field marked owner-only is returned solely on records the caller actually owns, even if it's otherwise listed as public.
- Tenant-isolated aggregates. A publicly surfaced view is always scoped to its own application's data; the scoping value is injected by the platform and never disclosed.
- Save-time validation. Publishing a surface validates and emits its access rules before the row is committed — there is no half-published state.
- Hardened report rendering. Report templates render in a sandbox with no outbound fetching, and record-level authorization is enforced on every row a report touches.
Extend it in one step
Growing an application's outward surface is additive and declarative:
- New public entity → add a public data surface. Read access opens, columns are scoped, no code.
- New citizen aggregate → declare a view and add a surface over it.
- New staff KPI → declare the view, add a dashboard card, wire it into the dashboard.
- New operator PDF → author a report template bound to the entity; expand child collections as needed.
- New portal page → add a page entry; access rules and navigation follow automatically.
- Narrower citizen list → extend the filterable and searchable fields.
The application scaffolding tooling generates the public-surface, view, portal-page, report, and dashboard blocks together, so a new application arrives with its outward experience already wired.