> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackra.figentra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture decisions

> Why the repository looks the way it does.

Each ADR records one decision, its context, consequences and the check that enforces it. An accepted ADR is not rewritten: a later ADR supersedes it, in the same commit, and the old page points forward. Format and status vocabulary: [template](/adr/template).

| ADR | Decision | Status |
| - | - | - |
| [0001](/adr/0001-monorepo-layout) | Monorepo layout, catalogs and tooling | Accepted |
| [0002](/adr/0002-docs-on-mintlify) | Documentation on Mintlify in `apps/docs` | Accepted |
| [0003](/adr/0003-tier-dependency-direction) | Tier dependency direction | Accepted |
| [0004](/adr/0004-package-archetypes-and-layers) | Package archetypes and layers | Superseded by 0126 |
| [0005](/adr/0005-di-container-and-legacy-decorators) | DI container and legacy decorators | Accepted, amended by 0143, 0145 |
| [0006](/adr/0006-naming-exports-and-imports) | Naming, exports and imports | Accepted, amended by 0128 |
| [0007](/adr/0007-source-documentation-standard) | Source documentation standard | Accepted, amended by 0128 |
| [0008](/adr/0008-test-placement) | Test placement | Superseded by 0022 |
| [0009](/adr/0009-secrets-two-mechanisms) | Secrets: two mechanisms | Accepted, amended by 0257 |
| [0010](/adr/0010-services-are-independent-workspace-members) | Services are independent workspace members | Accepted |
| [0011](/adr/0011-rejected-and-deferred-technologies) | Rejected and deferred technologies | Accepted |
| [0012](/adr/0012-code-host-and-ci) | Code host and CI | Accepted |
| [0013](/adr/0013-versioning-and-release) | Versioning and release | Superseded by 0025 |
| [0014](/adr/0014-data-ownership-and-orm) | Data ownership and ORM | Accepted |
| [0015](/adr/0015-contract-packages) | Contract packages | Superseded by 0117 |
| [0016](/adr/0016-agents-source-of-truth) | `.agents/` is the source of truth | Accepted |
| [0017](/adr/0017-agent-roster-and-models) | Agent roster and models | Accepted |
| [0018](/adr/0018-framework-design-record) | Framework design record | Proposed |
| [0019](/adr/0019-supply-chain-hardening) | Supply chain hardening | Proposed |
| [0020](/adr/0020-observability-and-crash-reporting) | Observability and crash reporting | Proposed |
| [0021](/adr/0021-ai-engine-and-agent-write-safety) | AI engine and agent write safety | Proposed |
| [0022](/adr/0022-tests-live-in-tests-folders) | Tests live in `__tests__` folders | Accepted |
| [0023](/adr/0023-stackra-is-a-framework) | Stackra is a framework; products consume it | Accepted |
| [0024](/adr/0024-mit-license-with-exclusions) | MIT license, with stated exclusions | Accepted |
| [0025](/adr/0025-packages-publish-to-private-registry) | Packages publish to `npm.figentra.com`; every package builds to ESM dist | Accepted |
| [0026](/adr/0026-turborepo-remote-cache-on-r2) | Turborepo remote cache self-hosted on Cloudflare R2 | Accepted |
| [0027](/adr/0027-cli-tools-pinned-in-mise) | Every CLI the repo needs is pinned in mise | Accepted |
| [0028](/adr/0028-package-categories-appear-with-first-package) | Package categories appear with their first package | Accepted, amended by 0127 |
| [0029](/adr/0029-encore-ts-one-app) | Encore.ts as one application under `services/` | Superseded by 0075 |
| [0030](/adr/0030-encore-cloud-on-owner-aws) | Encore Cloud into the owner's AWS; four environments | Proposed |
| [0031](/adr/0031-coarse-services-split-on-evidence) | Coarse services, split on evidence | Proposed |
| [0032](/adr/0032-rest-api-generated-client-uuidv7-ids) | REST `api()`, generated client, prefixed UUIDv7 ids | Superseded by 0142 |
| [0033](/adr/0033-pubsub-cron-transactional-outbox) | Pub/Sub and cron with a transactional outbox | Superseded by 0078 |
| [0034](/adr/0034-object-storage-malware-scan-limits) | Object storage, GuardDuty malware scan, upload limits | Proposed |
| [0035](/adr/0035-resend-email-with-spf-dkim-dmarc) | Resend email with SPF, DKIM and DMARC | Proposed |
| [0036](/adr/0036-idempotency-cache-and-postgres) | Idempotency in Encore cache for 24h; Postgres for money | Superseded by 0136 |
| [0037](/adr/0037-api-versioning-by-date-header) | API versioning by the `Stackra-Version` date header | Superseded by 0118 |
| [0038](/adr/0038-rate-limits-app-buckets-and-waf) | Rate limits: app token buckets and Cloudflare WAF | Proposed |
| [0039](/adr/0039-openapi-mintlify-and-oauth-guide) | OpenAPI to Mintlify API reference; OAuth developer guide | Proposed |
| [0040](/adr/0040-ddd-feature-modules-in-services) | Services are organised as DDD feature modules | Superseded by 0130 |
| [0041](/adr/0041-decorated-controllers-and-build-time-generator) | Decorated controllers, handlers and crons via a build-time generator | Superseded by 0091 |
| [0042](/adr/0042-di-first-with-one-container-per-service) | DI-first with one container per service | Accepted, amended by 0143, 0147 |
| [0043](/adr/0043-workflow-package-medusa-style) | `@stackra/workflow` is Medusa-style steps and workflows | Superseded by 0102 |
| [0044](/adr/0044-laravel-style-package-config) | Laravel-style package configuration | Superseded by 0146 |
| [0045](/adr/0045-tenancy-package-pool-default-silo-on-demand) | `@stackra/tenancy`: pool by default, silo on demand | Superseded by 0088 |
| [0046](/adr/0046-global-data-invariants-and-per-model-opt-ins) | Global data invariants and per-model opt-ins | Superseded by 0134 |
| [0047](/adr/0047-audit-service-via-events) | Audit service fed by events | Proposed |
| [0048](/adr/0048-point-in-time-recovery-and-quarterly-restore-drill) | Point-in-time recovery and a quarterly restore drill | Proposed |
| [0049](/adr/0049-search-driver-interface) | `@stackra/search`: a driver interface, Postgres first | Superseded by 0089 |
| [0050](/adr/0050-ai-package-on-ai-sdk-bedrock-default) | `@stackra/ai` on AI SDK core, Bedrock by default | Superseded by 0100 |
| [0051](/adr/0051-better-auth-identity-service) | Better Auth identity service | Superseded by 0087 |
| [0052](/adr/0052-console-is-a-vite-react-spa) | The console is a Vite + React single-page app | Proposed |
| [0053](/adr/0053-decorator-route-classes-compiled-to-router-adapters) | Routes are decorator classes compiled to router adapters; no file-based routing | Proposed |
| [0054](/adr/0054-tanstack-query-and-zustand-for-client-state) | TanStack Query for server state, Zustand for client state | Superseded by 0079 |
| [0055](/adr/0055-forms-react-hook-form-zod-heroui-adapters) | Forms use react-hook-form and Zod with thin HeroUI field adapters | Proposed |
| [0056](/adr/0056-heroui-v3-pro-on-web-share-everything-but-components) | HeroUI v3 and Pro on web; share logic and tokens, not components | Proposed |
| [0057](/adr/0057-landing-is-react-router-framework-mode-on-workers) | The landing site is React Router 7 framework mode on Cloudflare Workers | Proposed |
| [0058](/adr/0058-desktop-is-electron) | The desktop app is Electron | Proposed |
| [0059](/adr/0059-offline-encrypted-query-cache-and-mutation-queue) | Mobile offline: encrypted persisted Query cache and a persisted mutation queue | Proposed |
| [0060](/adr/0060-i18n-logical-css-light-and-dark) | i18n keys and logical CSS from day one; English launch; light and dark | Superseded by 0080 |
| [0061](/adr/0061-mobile-keeps-react-navigation-and-native-tabs) | Mobile keeps React Navigation 7 and native tabs; one app | Proposed |
| [0062](/adr/0062-mobile-release-push-and-crash-reporting) | Mobile: EAS Update, Expo push and Sentry from day one | Proposed |
| [0063](/adr/0063-cloudflare-hosts-web-surfaces) | Cloudflare hosts the web surfaces | Proposed |
| 0064 | Unused: the number was never assigned | — |
| [0065](/adr/0065-linear-gate-is-checked-offline) | The Linear gate is checked offline | Accepted |
| [0066](/adr/0066-delegation-to-opencode-by-routing-matrix) | Claude leads; OpenCode executes routed tasks in worktrees | Accepted |
| [0067](/adr/0067-spec-kit-is-the-sdlc) | spec-kit is the SDLC, with a mandatory clarify gate | Accepted |
| [0068](/adr/0068-runway-cicd-built-inside-stackra) | runway: CI/CD built inside Stackra | Proposed |
| [0069](/adr/0069-testing-strategy-and-coverage) | Testing strategy and coverage floors | Proposed |
| [0070](/adr/0070-quality-budgets) | Quality budgets: accessibility, Web Vitals, bundle, mobile startup | Proposed |
| [0071](/adr/0071-privacy-obligations) | Privacy: export, erasure, platform declarations and templates | Proposed |
| [0072](/adr/0072-soc2-groundwork) | SOC 2 groundwork | Proposed |
| [0073](/adr/0073-observability-better-stack-sentry-encore) | Observability: Better Stack, Sentry and Encore tracing | Proposed |
| [0074](/adr/0074-open-core-licensing) | Open core: `@stackra-pro` on the private registry | Superseded by 0077 |
| [0075](/adr/0075-stackra-ships-packages-figentra-os-ships-services) | Stackra ships packages; Figentra OS ships the product services | Proposed |
| [0076](/adr/0076-every-package-runs-saas-on-prem-and-air-gapped) | Every package runs in SaaS, on-prem and air-gapped deployments | Proposed |
| [0077](/adr/0077-open-core-free-and-pro-lists) | Open core: the free and Pro package lists | Proposed |
| [0078](/adr/0078-messaging-layer-eight-packages) | The messaging layer: eight packages, one question each | Accepted, amended by 0141, 0148 |
| [0079](/adr/0079-client-state-is-stackra-state) | Client state is @stackra/state (ported Figentra signals); Query owns optimism and the offline queue | Proposed |
| [0080](/adr/0080-i18n-is-the-figentra-i18n-port) | i18n is the ported Figentra i18n package; i18next is dropped | Proposed |
| [0081](/adr/0081-gate-chain-configurable-per-app) | The gate chain is fully configurable per app, with a warning when tenancy or iam is omitted | Superseded by 0138 |
| [0082](/adr/0082-public-ids-and-api-areas) | Public ids are a prefix plus base32 of one UUIDv7; four API areas | Accepted, amended by 0135 |
| [0083](/adr/0083-npm-scopes-and-cli-names) | npm scopes: @stackra for packages, @figentra for this repository's apps and services; @stackra/console is the CLI framework | Proposed |
| [0084](/adr/0084-figentra-os-package-port-plan) | Figentra OS package port plan | Proposed |
| [0085](/adr/0085-logger-reporters) | Logger reporters: CloudWatch kept; Encore log, Sentry and Better Stack added | Superseded by 0147 |
| [0086](/adr/0086-notifications-full-engine-all-channels-free) | @stackra/notifications is a full engine, with every channel free | Proposed |
| [0087](/adr/0087-identity-and-iam-are-driver-packages) | Identity and IAM are driver packages; enterprise features plug in as Pro | Proposed |
| [0088](/adr/0088-tenancy-free-pool-pro-silo-and-scope) | @stackra/tenancy: pool and RLS are free; DB-per-tenant and the scope tree are Pro | Proposed |
| [0089](/adr/0089-search-fresh-with-pro-engine-drivers) | @stackra/search is written fresh; Meilisearch and OpenSearch are Pro drivers | Proposed |
| [0090](/adr/0090-codegen-discovery-by-root-module) | Codegen discovers decorators through each member's root module | Accepted, amended by 0144 |
| [0091](/adr/0091-generated-encore-app-in-stackra-folder) | The Encore app is generated into .stackra/encore; services hold no Encore files | Accepted, amended by 0158 |
| [0092](/adr/0092-ir-ported-definition-package-dropped) | The IR is ported with decorator-derived kinds; the definition package is dropped | Accepted |
| [0093](/adr/0093-better-auth-integration) | Better Auth integration: container hooks, iam-only authorization, an owned MikroORM adapter, global auth tables | Proposed |
| [0094](/adr/0094-decorators-only-authoring) | Decorators are the only authoring style; workflows are declarative @Step graphs | Accepted, amended by 0145, 0152 |
| [0095](/adr/0095-zod-inside-field-decorators) | @stackra/validation: Laravel-style decorators compiled to one Zod schema, plus an output serializer | Accepted, amended by 0133 |
| [0096](/adr/0096-trait-naming-grammar) | Trait naming grammar and the trait list | Accepted, amended by 0133 |
| [0097](/adr/0097-model-and-database-packages) | @stackra/model and @stackra/database; gen emits MikroORM EntitySchema | Accepted, amended by 0133 |
| [0098](/adr/0098-actions-are-the-unit) | Actions are the unit of behaviour, in @stackra/actions, with one pipeline and a typed actor | Accepted, amended by 0136, 0137, 0139, 0155 |
| [0099](/adr/0099-risk-scale-l0-to-l4) | One action risk scale, L0 to L4, with derived MCP hints and effect tags; change risk is R0 to R3 | Accepted |
| [0100](/adr/0100-ai-model-and-ownership) | One free @stackra/ai: agents, tools, skills, MCP and A2A on AI SDK, every tool call an action | Accepted, amended by 0155, 0156 |
| [0101](/adr/0101-action-first-workflow-behind-when-needed) | Every operation is an action; a workflow sits behind it only when the criteria hit | Accepted |
| [0102](/adr/0102-workflow-engine-in-process-journal) | The workflow engine: in-process steps with a Postgres journal, outbox staging and hooks, ported from Medusa's MIT code | Accepted, amended by 0151, 0152 |
| [0103](/adr/0103-locking-package) | @stackra/locking: server-side named locks behind a driver | Accepted, amended by 0149 |
| [0104](/adr/0104-links-and-query-graph) | Cross-module relations are @Link declarations, read through query.graph | Accepted |
| [0105](/adr/0105-crud-generates-service-methods) | @Crud generates a model's service methods and opt-in actions | Accepted |
| [0106](/adr/0106-infisical-agent-vault-per-environment) | Infisical Agent Vault, split by environment; the model never sees a secret | Accepted |
| [0107](/adr/0107-connections-store-and-three-key-homes) | Three homes for keys; tenant credentials live in @stackra/connections | Accepted |
| [0108](/adr/0108-scoped-settings) | Scoped settings: each service owns its values; the registry owns the schema | Accepted |
| [0109](/adr/0109-registry-manifests-published-by-ci) | The registry: build manifests published by CI to a Postgres-backed registry service | Accepted |
| [0110](/adr/0110-experience-is-the-sdui-service-at-l2) | The experience service is server-driven UI at level L2: trees of registered coded components, no code over the wire | Accepted, amended by 0139, 0154 |
| [0111](/adr/0111-module-is-the-unit-of-contribution) | The module is the unit of contribution; services and apps only host modules | Accepted |
| [0112](/adr/0112-experience-packages-and-renderer-kit) | Experience packages, @stackra/ui parity and the renderer kit | Accepted |
| [0113](/adr/0113-heroui-pro-distribution) | HeroUI Pro reaches Stackra Pro customers as a private mirror bundle, with a bring-your-own-key option | Accepted |
| [0114](/adr/0114-page-builder-for-every-page) | The page builder builds every page, including our own marketing site; MDX and Sanity are dropped | Accepted |
| [0115](/adr/0115-form-builder) | Built forms are documents of kind form; they submit to an action; the editors are Pro | Accepted |
| [0116](/adr/0116-content-types-and-custom-fields) | @stackra/content: content types are models with a trait bundle; custom fields are a free, bounded trait | Accepted |
| [0117](/adr/0117-contracts-placement-and-decorator-ownership) | Shared primitives live in @stackra/contracts, each package has a types-only ./contracts entry, and each package owns its decorators | Accepted, amended by 0140, 0141, 0144 |
| [0118](/adr/0118-api-versioning-dormant-package) | @stackra/versioning is built now and stays dormant: date versions pinned per API key or OAuth app | Accepted, amended by 0136 |
| [0119](/adr/0119-merge-policy-and-release-authority) | Merge policy: Quality Gate is the only required check; Claude merges only through pnpm pr:merge | Accepted |
| [0120](/adr/0120-sdlc-spec-location-lanes-and-change-risk) | The SDLC: specs in .agents/specs, four lanes, and change risk R0 to R3 | Accepted |
| [0121](/adr/0121-sdlc-tooling-roles-and-linear) | SDLC tooling: sdlc retired, spec-kit pinned, a local Linear extension, the ported roles and who runs what | Accepted |
| [0122](/adr/0122-workflow-studio-three-layers) | The workflow studio: coded workflows, configurable knobs, and tenant automations as data that references code | Accepted, amended by 0153, 0154 |
| [0123](/adr/0123-delivery-order-follows-the-dependency-graph) | Delivery order follows the dependency graph: package-disjoint waves, spikes first, assumed defaults pending the owner | Proposed |
| [0124](/adr/0124-encore-is-the-generated-runtime-target) | Encore is the generated runtime target; service source stays plain TypeScript | Accepted, amended by 0158 |
| [0125](/adr/0125-constitution-amended-to-match-accepted-adrs) | The constitution is amended to match Accepted ADRs and locked owner decisions | Accepted |
| [0126](/adr/0126-member-shapes-and-scaffold-templates) | Member shapes and scaffold templates: service skeleton, entries-first packages, kind folders, four app kinds | Accepted |
| [0127](/adr/0127-package-categories-including-infrastructure) | Package categories, including infrastructure | Accepted |
| [0128](/adr/0128-code-conventions-enums-classes-components-and-kind-folders) | Code conventions: enums, classes, components and kind folders | Accepted |
| [0129](/adr/0129-house-lint-plugin-and-coding-standard) | Code standards enforced by a house lint plugin, shipped as @stackra/coding-standard | Accepted |
| [0130](/adr/0130-module-layout-by-semantic-kind) | Module layout by semantic kind; no controllers | Accepted |
| [0131](/adr/0131-module-key-version-and-deprecation) | Module key, version and deprecation | Accepted |
| [0132](/adr/0132-permissions-catalogue-and-policy-providers) | Permissions are a module catalogue; policies are providers | Accepted |
| [0133](/adr/0133-field-semantics-model-keys-rename-safe-fields-and-lifecycle-events) | Field semantics, model keys, rename-safe fields and lifecycle events | Accepted |
| [0134](/adr/0134-data-invariants-classification-and-delete-lifecycle) | Data invariants, field classification and the delete lifecycle | Accepted |
| [0135](/adr/0135-public-id-format) | Public id format: prefix plus Crockford base32 of one UUIDv7 | Accepted |
| [0136](/adr/0136-action-runtime-contract) | The action runtime contract: handler context, transaction, idempotency and versions | Accepted |
| [0137](/adr/0137-declared-action-options-and-refusal-codes) | Declared action options and refusal reasons | Accepted |
| [0138](/adr/0138-pipeline-order-is-fixed) | The pipeline order is fixed; apps toggle and parameterise gates | Accepted |
| [0139](/adr/0139-actions-declare-their-user-experience) | Actions declare their user experience | Accepted |
| [0140](/adr/0140-error-envelope-and-error-kinds) | The error envelope and error kinds | Accepted |
| [0141](/adr/0141-integration-event-names-and-envelope) | Integration event names and the envelope | Accepted |
| [0142](/adr/0142-cross-service-transport-through-drivers) | Cross-service transport goes through drivers | Accepted |
| [0143](/adr/0143-container-scopes-registration-lifecycle-and-testing) | Container scopes, module registration, lifecycle and testing | Accepted |
| [0144](/adr/0144-metadata-registry-and-explorers) | A typed MetadataRegistry, and every decorator package ships an Explorer | Accepted |
| [0145](/adr/0145-tokens-are-define-token-values-and-global-helpers-are-banned) | Tokens are defineToken values; container-backed global helpers are banned | Accepted |
| [0146](/adr/0146-package-configuration-typescript-layers-and-secret-references) | Package configuration: TypeScript files, layered overrides and secret references | Accepted |
| [0147](/adr/0147-logger-and-runtime-telemetry-defaults) | Logger and runtime telemetry defaults | Accepted |
| [0148](/adr/0148-queue-and-scheduler-full-job-features-fair-queuing-and-tenant-schedules) | Queue and scheduler: full job features, fair queuing and tenant schedules | Accepted |
| [0149](/adr/0149-locking-fencing-tokens-auto-renew-and-with-lock) | Locking: fencing tokens, auto-renew and @WithLock | Accepted |
| [0150](/adr/0150-cache-package) | @stackra/cache: memory, Valkey, Encore and Postgres drivers with tenant-namespaced keys | Accepted |
| [0151](/adr/0151-workflow-engine-is-a-port-with-native-and-temporal-drivers) | The workflow engine is a port with a native driver and a Temporal driver | Accepted |
| [0152](/adr/0152-workflow-semantics-timeouts-waits-pinned-versions-and-one-graph-format) | Workflow semantics: timeouts, waits, pinned versions, operations and one graph format | Accepted |
| [0153](/adr/0153-automations-run-as-principals-and-every-package-exposes-building-blocks) | Automations run as principals; every package exposes its capabilities as building blocks | Accepted |
| [0154](/adr/0154-tenant-code-runs-only-in-a-sandboxed-code-step) | Tenant code runs only in a sandboxed code step | Accepted |
| [0155](/adr/0155-queries-are-mcp-tools-and-all-ai-code-is-free) | AI-native rule: queries are MCP tools, and all AI code is free | Accepted |
| [0156](/adr/0156-ai-runtime-provider-drivers-memory-and-guardrails) | The AI runtime: provider drivers, a managed-runtime driver, memory and guardrails | Accepted |
| [0157](/adr/0157-stackra-inspect-and-stackra-diff) | stackra inspect and stackra diff | Accepted |
| [0158](/adr/0158-gen-artefacts-openapi-manifest-and-client) | Gen artefacts: committed OpenAPI and manifest, a CI-published client, deploy-mode targets | Accepted |
| [0230](/adr/0230-reviewer-identity-separate-from-builder) | Reviews come from a separate reviewer identity, never the author | Accepted |
| [0257](/adr/0257-secret-manager-agent-and-the-secrets-inbox) | Secret-manager agent and the secrets inbox: values written blind, nobody reads them | Proposed |

Copy the [template](/adr/template), give it the next number, and add it to this table and to `docs.json`.
Cite Stackra ADRs by path, never by a number from another repository.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.