TAPPaaS configuration schemas¶
This directory holds the JSON-Schema (draft 2020-12) field definitions for every typed configuration object in TAPPaaS. Each type has:
- a schema here in
schemas/(the field definitions + validation rules), - a live location under
~/config/on thetappaas-cicdmothership (the deployed instances), and - an owning manager that performs all create/read/update/delete through verbs.
Admins drive verbs, not JSON (ADR-007). You never hand-edit these files — the owning manager writes validated config and a
reconcilepushes it to the live system. Hand-editing remains possible but is only valid when followed by the manager'sreconcile. Every write is schema- + reference-validated first.
configuration.json (the old monolith) is retired — it was split into site.json + per-environment files + per-module config (this is why there is no configuration.md anymore).
The configuration objects¶
| Object | Schema (schemas/) | Live location (~/config/) | Owning manager — CRUD verbs |
|---|---|---|---|
| Site (singleton) | site-fields.json | site.json | site-manager — site show/modify; node list/add/delete; repository list/add/delete/reconcile; validate; reconcile [--deep] |
| Environment | environment-fields.json | environments/<env>.json | environment-manager — add/modify/delete/list/show/validate; reconcile [--deep] (env + its zone via network; --deep → consuming modules). --dns-mode per-service\|wildcard |
| Organization | organization-fields.json | people/organizations/<name>.json | people-manager — org add/modify/delete/list/show |
| Group | group-fields.json | people/groups/<name>.json | people-manager — group add/modify/delete/list/show |
| Role | role-fields.json | people/roles/<name>.json | people-manager — role add/modify/delete/list/show |
| User | user-fields.json | people/users/<name>.json | people-manager — user add/modify/delete/list/show. People-wide: reconcile (push → Authentik; alias sync), validate |
| Module (deployed) | module-fields.json | <module>.json | module-manager — module add/modify/delete/list/show/validate/reconcile/test/snapshot-vm. add=deploy, modify=redeploy, reconcile=re-apply current config (leaf) |
| Module catalog | module-catalog-fields.json | src/module-catalog.json (in each repo) | site-manager — repository add/delete/list/reconcile (registers/clones the repo that ships the catalog) |
| Zones | zones-fields.json | zones.json | network-manager — add/delete/list/show/exists (the zone keyword is an optional legacy prefix); validate (alias zones-check); init/merge/distribute (aliases zones-init/zones-merge/zones-distribute); reconcile [--apply] [--only <plane>]. (No free-form modify — state + access-to are governed by the lifecycle + init/merge.) |
Service field manifests (service-fields.json)¶
service-fields.json is the odd one out: it does not describe a ~/config/ object at all. It is the schema for services/<service>/fields.json — the per-provider manifest ADR-020 introduces, which lives in a module's SOURCE tree next to the scripts that perform the change.
The two module schemas answer different questions and neither duplicates the other:
| Question | Answered by |
|---|---|
What fields exist, what do they mean, what is the default, and which service uses each one (usedBy)? | module-fields.json |
| For that (field, service) pair — what does changing it cost after install, and how is the change applied? | services/<service>/fields.json |
The change class is keyed by the pair, not the field, which is why the manifest sits with the service rather than in the field schema. module-manager validate lints it: a service that ships a fields.json must classify every field module-fields.json says it owns, using the classes the schema defines. A service with no fields.json has not been migrated to the contract yet and is skipped.
The operator-facing surface it unlocks is documented in the module-manager README: modify <module> --set field=value, which refuses an immutable or recreate field before it writes anything and rejects a mixed --set whole; and the two — only two — ways to authorize a change that needs downtime, modify --force (an operator, now) and the per-module rebootOk (a standing permission honoured only in the scheduled sweep). update-tappaas --force is neither: it means "run the sweep now" and is never forwarded as permission to reboot.
Objects without a schema in this directory¶
| Object | Live location | Owner / how it's written |
|---|---|---|
| Switch / AP topology | switch-configuration-{actual,desired}.json | schema is network/switch-configuration-schema.json; switch-controller / ap-controller (add-controller/add-switch/add-port/interrogate/reconcile), driven by network-manager reconcile --only switch\|ap. |
| TLS cert refids | cert-refids.json | runtime state (no schema) — written by acme-setup.sh, keyed by environment name. An environment's domains.dnsMode (environment-manager --dns-mode) selects whether a wildcard cert refid is stored here. |
| Backup policy | not a file — the .backup block on site.json / environments/<env>.json / <module>.json (cascade: module > env > site) | backup-manager — modify <module> writes the module .backup layer; site/env layers via site/environment modify. list/show/validate/reconcile (resolve cascade → PBS via backup-controller); restore. |
Conventions¶
validate— every config manager exposes avalidateverb (the schema + reference-integrity gate). Writes are validated before they land.reconcile— pushes config → live. Shallow by default;--deepcascades into dependents (site → people + network + environments → modules);--applycommits (default is preview). Reconcile is idempotent.- Managers vs controllers — managers own configuration (these objects) and the verb front door; controllers (opnsense / proxmox / switch / ap / backup / identity) execute against live infrastructure and are driven by their manager.
See docs/design/ADR-007-verb-alignment.md for the full verb/CRUD model and the reconcile cascade.
Attribution: contributor, author, maintainer¶
Three roles, one home each — nothing duplicated (see ADR-015 — Community Governance and Contribution Files for the repo/org-level contribution model):
| Role | Definition | Recorded in |
|---|---|---|
| Contributor | Anyone with an accepted change (the broadest set) | git history — never listed in files |
| Author | The copyright-bearing subset | optional per-module AUTHORS.md — additive lines of <year> @<handle> — <role> (template: src/apps/00-Template/AUTHORS.md) |
| Maintainer | The current responsible party (moves on handover) | maintainer in <module>.json — the one machine-readable governance field, kept there so issues can be reported/routed automatically |
Authorship does not go into <module>.json — it is not needed to run the module. AUTHORS.md is optional. Per-file headers (.nix # Author: lines) are not authoritative and may be dropped when touched; the module-level AUTHORS.md covers every artifact in the module (DRY).
AI assistance: an AI tool is at most a contributor — AI-generated output carries no copyright, so an AI is never an author (it never appears in AUTHORS.md) and never holds maintainer. Per ADR-015 and Codeberg etiquette, a contribution is attributed to the human contributor: do not add an AI Co-Authored-By: trailer to commits or pull requests.