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.

Tenant configuration boundary

Tenant JSON is a bootstrap and deployment document, not the mutable settings database. TenantConfigInventory is the machine-readable allowlist used by tenant:config:audit; every leaf path must have an explicit classification and rationale. Unknown paths fail the command.

Backoffice-editable settings owned by Psapi, Wpapi, Esapi, and Google are tenant-scoped AppSettings. Their typed owner metadata is declared by AppSettingOwnerCatalog; runtime readers are DB-only and code defaults apply only where the owner defines one. Tenant JSON is not a fallback.

Use:

php bin/console tenant:config:audit --format=json
php bin/console tenant:config:migrate-editable --dry-run
php bin/console tenant:config:migrate-editable

The migration preflights all conflicts before writing, redacts credential values, encrypts migrated owner credentials at rest with AES-256-GCM derived from JWT_SECRET, writes atomically, and records _migration.tenant_json.{owner}.ready. Existing plaintext credentials for these owners are upgraded idempotently by the same command. Use --overwrite only after reviewing the dry-run conflict report. Preserve a copy of the tenant JSON as the operator recovery artifact before removing the migrated owner object; secrets must subsequently be rotated through their credential-owning admin surface.

Theme selection and the two editable locale values have separate exact-path migrations. For an existing rendering tenant with an allowed theme wrapper, first stop any long-lived workers, pause backoffice theme/module/locale writes, and retain a database snapshot and a copy of the canonical JSON. Run the following commands once per canonical tenant (TENANT_ENV names its canonical host; aliases share its DB):

php bin/console k0smos:modules:sync-theme-selection
php bin/console tenant:config:migrate-theme-selection --dry-run
php bin/console tenant:config:migrate-theme-selection
php bin/console tenant:config:migrate-locales --dry-run
php bin/console tenant:config:migrate-locales
php bin/console tenant:theme:diagnose

Read back theme.selection, i18n.default_locale, and, for tenants with the Documentation module active, i18n.documentation_locale from app_settings:

SELECT setting_key, setting_value FROM app_settings
WHERE setting_key IN ('theme.selection', 'i18n.default_locale', 'i18n.documentation_locale');

Confirm the selected wrappers and deployed manifests with the diagnostic command. Remove each legacy JSON field only after its corresponding DB value has been validated. For an inactive Documentation module, a legacy value of en can be omitted after proving the module's English compatibility fallback; retain any non-default value until activation and DB readback. Check a new request after editing JSON, including when config compilation is enabled, then resume writes and restart workers. The compiled JSON cache uses a source-content digest as well as mtime, so a same-second edit invalidates it. Backfills insert only absent keys and preserve subsequent backoffice edits on rerun. A fresh install may omit the four fields: it synchronizes the default wrapper and seeds the default theme pair. The only legacy fallback is a missing selection row; a malformed present row is a configuration error. Normal HTTP returns 503 if the DB or required schema is unavailable.

The two bundled lite integration tenants have no theme wrapper in their deployment module allowlists. Their explicit default/default JSON pair was omitted as equivalent to the code fallback; do not run theme selection backfill for them or create a DB aggregate whose wrapper check would fail. Their inactive Documentation locale was also the en compatibility default and was omitted. Their i18n.default_locale was backfilled and read back before that JSON field was removed. The five configured rendering tenants likewise had their DB theme/default/Documentation values read back before JSON cleanup. A private, consistent SQLite/JSON backup was checked for integrity and restore, and no workers were active during this cutover. On other deployments, follow the per-tenant sequence above before removing any legacy field.

The DB theme pair is one theme.selection record with a revision token. The admin page /admin/settings/themes changes front and admin together. Choices need an already active wrapper/dependencies and a deployed asset manifest; theme switching does not enable modules or run content fixtures. Module and theme forms return 409 for stale revisions. To recover a malformed selection after a failed deployment, explicitly prepare the target modules if needed, then replace the row:

php bin/console k0smos:modules:sync-theme-selection --front=default --admin=sober
php bin/console tenant:theme:recover --front=default --admin=sober

The two locale pages live under Settings and validate values against their respective deployment-supported languages and installed Documentation catalogs. i18n.supported_locales remains a deployment policy used before routing and privacy classification. The legacy JSON shape during migration is:

"i18n": {
    "default_locale": "it",
    "documentation_locale": "en",
    "supported_locales": ["it", "en"]
}

Documentation first tries the navigation locale, then the configured Documentation fallback catalog. A present invalid DB value fails validation; only an absent DB key uses the legacy JSON value or its code default.

The effective runtime snapshot is assembled before modules and the container. It combines raw JSON/database locator with the narrow DB theme and default locale settings. The resolved JSON disk cache never contains the DB overlay. Long-lived queue workers must be gracefully restarted after a theme or default locale switch. For a code-release rollback to a version that only reads JSON, restore the latest DB choices to deployment JSON before sending traffic to the older release. See field disposition.

For an isolated browser check after building Default and Sober assets, run node tools/benchmarks/tenant-theme-smoke.mjs. It creates and removes a disposable SQLite tenant, switches the admin theme through the real form, and writes screenshots under reports/tenant-theme-smoke/.

Independent site deployment locators

The optional Site Publish module classifies five deployment keys: site.root (independent checkout), site.public_url (plain HTTPS canonical origin), the optional site.profile (content contract), site.runtime (one executable basename), and site.timeout_seconds (30–3,600 seconds). It also accepts an optional nested site.database target for the public PostgreSQL/Supabase projection. Neither the site checkout nor that projection replaces the root database block. Locales, editorial revisions and publication state live in module tables.

"site": {
  "root": "site/dm3",
  "public_url": "https://k0smos.example.com",
  "runtime": "bun",
  "timeout_seconds": 600
}

The runtime defaults to bun and the timeout to 600 seconds. The runtime is a validated executable name, not a path or editable command; arguments remain fixed by Site Publish. Site tooling writes every structured result to the private digest-checked receipt path supplied by k0smos, while stdout/stderr remain bounded progress output only.

SQLite remains the canonical public generation and needs no database locator. To mirror each finalized generation to Supabase/PostgreSQL, configure the separate target with environment placeholders rather than literal credentials:

"site": {
  "root": "site/dm3",
  "public_url": "https://k0smos.example.com",
  "database": {
    "driver": "supabase",
    "url": "${K0SMOS_SITE_SUPABASE_DATABASE_URL}"
  }
}

site.database accepts a PostgreSQL URL or explicit host plus dbname/database, with optional port, user/username, password, charset, sslmode and serverVersion. Remote Supabase connections always use sslmode=require; an explicit attempt to disable it is rejected. Missing, unresolved or unknown configuration fails publication with a bounded code. Credentials stay on the server and are excluded from AppSettings, manifests, receipts, logs and browser assets. Apply the module's versioned PostgreSQL schema explicitly before selecting this target; publication never creates or alters remote tables. The SQLite-only path remains valid when the block is absent.

The declaration alone identifies the site: the checkout folder's name is the site code, and the content profile defaults to that same code. Both accept lowercase letters, digits and hyphens, 2 to 40 characters. Set site.profile only when the folder name differs from the contract it publishes, for example a showroom checkout carrying the dm3 profile. The site code and profile are frozen in the module's target row at first import: renaming the folder or changing the profile afterwards resolves to a different site or fails closed, so treat it as a migration rather than a configuration edit.

Identity is read from configuration only, so operators keep editing content while a checkout is unmounted or being replaced; publication refuses until it resolves, and the editor and site:status name the exact reason.

By default the checkout must be strictly below the repository's site/ folder. An operator can supply an absolute parent-directory allowlist through K0SMOS_SITE_ALLOWED_ROOTS (platform path separator). The target must have real app/, tooling/ and contracts/ directories; source/tenant-database/public root overlap, traversal and mutable-input symlinks fail closed. An opaque source binding prevents two tenants from publishing into the same checkout. Media originals and credentials remain owned by k0smos. See Site Publish for backup, import preview and activation.