Build apps
Modules and feature flags
Three separate controls decide whether a signed-in user can use a part of your app — exposure, capability and feature flag — and how each one fails.
A module is a part of your app you can turn on or off for users (invoices, alerts, an AI summary). Three controls decide whether one is available. They answer different questions, and they fail differently.
| Control | Question it answers | Set with |
|---|---|---|
| Exposure | May an end-user token start this composition at all? | identity.app.expose-virtual-workflow, identity.app.unexpose-virtual-workflow |
| Capability | May this person, in this workspace, do this verb? ({namespace}:{verb}) | Workspace roles and groups |
| Feature flag | Does this app have the module switched on? | policy.feature-flag.apply, delete, query |
Feature flags
A flag switches a composition or a module off for end-users without unexposing it.
policy.feature-flag.apply
identity_app_uuid the app
flag_key the composition name, or a module slug
enabled true | false
flag_type "workflow" | "module" (default "module")
- Applying the same app, type and key again updates the flag. It does not duplicate it.
- Only an explicit off denies. A key with no flag is not gated.
- A workflow flag is keyed on the composition name. Starting it as an end-user while the flag is off returns
END_USER_WORKFLOW_DISABLED. - The decision workflow
policy.decision.evaluatetakesfeature_flagormodulein its context and denies withflag_off.
How each control fails
The three are deliberately not uniform:
| Control | If the lookup errors |
|---|---|
| Exposure | Denies. It is the boundary. |
| Capability | Allows, so a store hiccup cannot take every end-user offline. |
| Feature flag when starting a workflow | Allows, for the same reason. |
Feature flag in policy.decision.evaluate | Denies when the flag store is unavailable. |
A flag only gates the paths that check it: starting an exposed composition and the decision workflow. It is not a data boundary. Gate data with App Data ownership, not with a flag.
Making all three planes apply to App Data CRUD and the serving path is a proposed decision, not shipped behavior (ADR-IDENTITY-011). Until then, treat exposure plus ownership as the data boundary.
Backend
An Orkestia app has no server to run — its backend is compositions of catalog workflows, exposed to signed-in users, plus schedules and approvals.
Environments
Staging and pull-request preview environments for a hosted app — what each one is, how to turn one on, promote its release and turn it off, and what they cost.
