Canonical App Structure¶
Mozaiks apps use one workspace shape across generated apps, hosted product apps, and first-party dogfood apps. The structure is app-agnostic: app behavior lives in modules, app intent lives in config, implementation support lives in app/services, and AI orchestration lives in workflows.
workspace/
├── app/
│ ├── app.json
│ ├── config/
│ │ ├── ai.json
│ │ ├── shell.json
│ │ ├── data.json
│ │ ├── secrets.yaml
│ │ ├── integrations.yaml
│ │ └── targets.json
│ ├── modules/
│ │ └── {module_id}/
│ │ ├── module.yaml
│ │ ├── contracts/
│ │ ├── runtime_extensions.yaml
│ │ ├── backend/
│ │ │ ├── handler.py
│ │ │ ├── service.py
│ │ │ ├── repo.py
│ │ │ ├── policy.py
│ │ │ └── schemas.py
│ │ └── ui/
│ ├── ui/
│ │ ├── pages/
│ │ ├── pages/custom/
│ │ ├── route_manifest.json
│ │ └── index.js
│ ├── admin/
│ │ ├── admin_registry.yaml
│ │ ├── index.js ← admin/index.js registers custom page components
│ │ └── pages/ ← admin/pages/ holds custom admin page files
│ ├── brand/
│ └── services/
│ ├── integrations/
│ ├── adapters/
│ ├── security/
│ ├── routes/
│ └── data/
├── workflows/
├── capability_packs/
├── generated/
├── tests/
├── docs/
└── scripts/
Ownership Rules¶
app/modules/owns deterministic app behavior: actions, state, permissions, emitted events, lifecycle transitions, and persistence authority.app/services/owns app service implementations: thin external clients, provider adapters, callback routes, provider-neutral security helpers, long-running workers, and data-contract helpers. Services do not create business actions or own durable app facts.app/config/declares app intent.data.jsonis the unified persistence/data contract.secrets.yamlis names-only.integrations.yamldeclares external capability requirements.targets.jsondeclares runtime/deployment/domain target intent.workflows/at the workspace root owns app-local AI workflows.capability_packs/contains reusable app overlays or hosted-pack client templates. Hosted proprietary engines are not copied into generated apps.generated/is staged output awaiting review or promotion.
Module Contract¶
Modules are the only canonical place for app business behavior.
app/modules/{module_id}/
├── module.yaml
├── contracts/
│ ├── events.yaml
│ ├── reactions.yaml
│ ├── notifications.yaml
│ ├── settings.yaml
│ ├── admin.yaml
│ ├── profile.yaml
│ └── entitlements.yaml
├── runtime_extensions.yaml
└── backend/
├── handler.py
├── service.py
├── repo.py
├── policy.py
└── schemas.py
handler.py is thin dispatch. service.py owns business flow and emits events after state commits. repo.py owns persistence operations through ctx.persistence.collection(module_id, entity_name). policy.py owns scope query helpers. schemas.py owns typed request, response, and document shapes.
Service Contract¶
app/services/ is the canonical support lane:
app/services/integrations/- thin clients for external or hosted APIs.app/services/adapters/- provider-specific mechanics for auth, source control, deployment, DNS, registrar, cloud, storage, secrets, payments, and similar implementation boundaries.app/services/security/- provider-neutral auth and secret resolution policy.app/services/routes/- app-level routes only when a module extension or host contract explicitly requires them.app/services/data/- optional helpers forapp/config/data.json.
If a behavior has product state, permissions, user-facing actions, domain events, or persistence authority, it belongs in a module. A module may call app/services/ code as an implementation detail.
Config Contract¶
app/config/data.jsonis the single data contract for module collections, cross-module aggregate ownership, external existing database mappings, and index/migration metadata.app/config/data_migrations/{migration_id}.jsoncontains additive data migrations when refinement needs an explicit staged migration.app/config/secrets.yamldeclares provider, vault policy, env handles, and secret names only. It must never contain raw secret values.app/config/integrations.yamldeclares external services and hosted capability requirements.app/config/targets.jsondeclares deployment, runtime, domain, DNS, and provider target intent. Provider mechanics live inapp/services/adapters/.
Workflow Contract¶
App-local workflows live beside app/:
workflows/{workflow_name}/
├── orchestrator.yaml
├── agents.yaml
├── handoffs.yaml
├── context_variables.yaml
├── structured_outputs.yaml
├── tools.yaml
├── ui_config.yaml
├── hooks.yaml
├── tools/
└── ui/
Factory workflows live under factory_app/workflows/. Runtime workflow resolution selects one root; generated and hosted product apps use workspace-root workflows/.
Generator Rule¶
AppGenerator emits this canonical structure only. It must not emit app-level support code outside app/services, persistence helpers outside app/services/data, or separate data_contract config files. Those concerns are represented by app/services/ and app/config/data.json.