Vai al contenuto
Spedizione in 24/48h in tutta Italia
Vai al contenuto
Navigazione documentazione

Questa pagina non è ancora disponibile nella lingua scelta. È mostrata la versione EN.

ADR 0001: Configuration, Settings, Runtime State, And Taxonomy

  • Status: accepted
  • Date: 2026-08-20
  • Decision owners: core architecture and the owning runtime modules

Context

AppSettings began as a convenient tenant key/value store. Over time, tenant preferences, credentials, import cursors, job snapshots, and timestamps acquired similar storage shapes even though they have different ownership, lifecycle, security, and deployment semantics. Separately, several modules use the words "category" or "tag" for concepts that are not interchangeable.

An unrestricted variable bag would preserve this ambiguity. A generic taxonomy schema created from names alone would move module invariants into core without proven consumers.

Decision

Every new value belongs to exactly one of these classes:

Class Owner and lifecycle Writable at request time Examples
Bootstrap configuration deployment; required before the tenant database or container is available no database locator, tenant identity, bootstrap security policy
Environment override deployment secret/configuration provider; process lifetime no DSNs, infrastructure endpoints, keyring material
Deployable application configuration versioned tenant JSON or deployment artifact no queue topology, worker limits, provider selection
Tenant-editable setting declared owner; changed through an authorized application use case yes integration enabled flag, UI preference, theme.selection, default/documentation locales
Runtime state/checkpoint the operation that advances it; migration-backed and retention-aware yes, only through its state repository import jobs, cursors, health/checkpoint timestamps
Credential declared setting or domain credential plus the core secret-store context only through a write-only authorized use case API key, token, TOTP secret
Taxonomy a vocabulary/term/assignment domain with explicit target ownership only if a separate taxonomy decision is accepted none in core today

The executable setting contract lives in src/Application/Settings:

  • SettingDefinition declares owner, stable key, value type, default, executable constraints, scope, sensitivity, editability, and migration/deprecation data;
  • SettingSchemaRegistry rejects duplicate keys and undeclared access;
  • SettingResolver accepts one source per layer and applies only the layers allowed by the definition scope;
  • tenant definitions accept only the tenant layer, deployable definitions accept deployable plus environment override, and bootstrap definitions accept bootstrap plus environment override;
  • TypedTenantSettings is the compatibility boundary over the existing AppSettings representation. It normalizes reads/writes and delegates credentials to the existing AppSettings owner catalog and enc:v2 store.

No resolver includes a runtime-state layer. Runtime state has a domain-specific repository and migration instead of a new generic state bag. Existing durable Psapi/Wpapi run stores remain module-owned. Esapi synchronization state has one owner in esapi_sync_log; Firefly III jobs/checkpoints have one owner in fireflyiiiapi_import_jobs.

OneUptimeApi is the first owner migrated to a complete typed schema. Further owners migrate independently; raw AppSettings remains a compatibility API during that process, while typed access rejects undeclared keys immediately. The theme pair is a specialized early tenant setting: it shares one app_settings row and an opaque revision with module-state writes, is read before the container, and cannot be written through generic AppSettings::set(). i18n.default_locale joins that narrow early reader; Documentation's fallback locale stays module-owned and loads after the container. The tenant JSON database locator and i18n.supported_locales remain deployment policy.

Taxonomy Semantic Review

The reviewed models do not provide two consumers with the same vocabulary, term, assignment, translation, hierarchy, authorization, and deletion rules:

Candidate Actual semantics Decision
Blog and Page Neither native content model currently owns term assignments. Wpapi keeps source WordPress terms in connector bridge tables and does not establish a native Blog/Page taxonomy contract. No consumer pair.
Kit tags Flat, free-form JSON labels embedded in files and links for local filtering; no stable term identity or shared lifecycle. Stay in Kit.
Finance categories/tags Accounting classifications attached to transactions. Categories and colored tags have different relationships and finance-owned deletion behavior. Stay in Finance.
Ticket categories Required bounded string value on each ticket, used for operational filtering; there is no term repository or assignment entity. Stay in Ticket.
Wpapi mappings Source-system identity/provenance for WordPress editorial terms and WooCommerce catalog categories. Connector lifecycle follows remote ids. Stay in Wpapi bridges.
Ecommerce categories Hierarchical, localized, SEO-addressable catalog navigation with active/order/import-lock state and product/search/menu consequences. Stay in Ecommerce.

Core taxonomy tables, target-provider hooks, shared ACL, shared admin UI, and consumer migrations are therefore rejected. They can be reconsidered only when two named active consumers pass the core-promotion review in doc/public/en/architecture/core-promotion.md.

Consequences

  • Similar names no longer justify a shared persistence model.
  • Tenant settings gain executable semantics incrementally without a flag-day rewrite or a second credential cipher.
  • Runtime jobs and timestamps can have retention, concurrency, and audit rules appropriate to their operation.
  • The legacy Firefly III last-sync timestamp is migrated into a synthetic completed job before obsolete runtime keys are removed.
  • A future taxonomy proposal must supply two matching consumers and an explicit migration/export/rollback design; the current decision creates no core table.