feat(blueprints): add declarative blueprints #10

Merged
ryan.mcgee merged 31 commits from feat/blueprints into main 2026-10-15 18:07:39 +00:00
Owner

Summary

  • Declarative YAML blueprints: describe resources, targets, headers, access settings and backend TLS in one document, preview it, and apply it all or nothing. Web page and API.
  • Stacked on #9 (feat/audit-log); the base branch is the audit log because blueprint changes are audited.

Changes

  • Blueprints.plan/2, execute/2, export/1. Resources are matched by domain (HTTP) or protocol and port (TCP/UDP). Matches are updated, others created, nothing is ever deleted. A section left out of a resource is untouched; a section present is declared in full.
  • Everything goes through the existing context functions in one transaction, so validation, authorization and audit events are unchanged. A plan is the same run, rolled back. Applying the same blueprint twice changes and logs nothing.
  • Strict parser (Blueprints.Parser): errors carry blueprint paths (resources[1].targets[0].host). It refuses anchors, aliases, merge keys, tags, tabs, duplicate keys, unknown keys and secret keys (passwords, PINs, share links), limits size (256 KB), resources (200), children (100) and nesting, creates no atoms, and runs in a separate process with a heap and time limit. The library resolves several of these silently (duplicate keys take the first, aliases expand), which is why the parser is stricter than it.
  • Deterministic export (Blueprints.Yaml), without secrets.
  • Broadcasts holds PubSub messages until the transaction commits. Audit.write joins an outer transaction. Saving an unchanged node list is no longer logged as a change.
  • /orgs/:org/blueprints (admins): preview, apply, load current configuration. GET /api/v1/blueprint (any key), POST /api/v1/blueprints/plan and /apply (write keys), documented in the OpenAPI spec; problems are 422 keyed by path.
  • New dependency yaml_elixir 2.12.2 (approved). Maintenance note: it is current, but its parser yamerl last released in 2022.

Verification

  • mix format --check-formatted, mix compile --warnings-as-errors, mix credo --strict (no issues), mix dialyzer (0 errors).
  • mix test: 837 passed on SQLite and on PostgreSQL. No migration in this change.
  • Live smoke on the compose stack: API plan, rejected blueprint (422 with path), apply, and Traefik serving the new route with the blueprint's header; re-apply reports unchanged; export contains the whole configuration. In the browser: load current, edit, preview (1 create, 1 update, 4 unchanged), apply, Traefik serving the result; audit log shows the events for both the user and the API key. The dev database was restored afterwards.
  • The README example was planned against a scratch database.
## Summary - Declarative YAML blueprints: describe resources, targets, headers, access settings and backend TLS in one document, preview it, and apply it all or nothing. Web page and API. - Stacked on #9 (`feat/audit-log`); the base branch is the audit log because blueprint changes are audited. ## Changes - `Blueprints.plan/2`, `execute/2`, `export/1`. Resources are matched by domain (HTTP) or protocol and port (TCP/UDP). Matches are updated, others created, nothing is ever deleted. A section left out of a resource is untouched; a section present is declared in full. - Everything goes through the existing context functions in one transaction, so validation, authorization and audit events are unchanged. A plan is the same run, rolled back. Applying the same blueprint twice changes and logs nothing. - Strict parser (`Blueprints.Parser`): errors carry blueprint paths (`resources[1].targets[0].host`). It refuses anchors, aliases, merge keys, tags, tabs, duplicate keys, unknown keys and secret keys (passwords, PINs, share links), limits size (256 KB), resources (200), children (100) and nesting, creates no atoms, and runs in a separate process with a heap and time limit. The library resolves several of these silently (duplicate keys take the first, aliases expand), which is why the parser is stricter than it. - Deterministic export (`Blueprints.Yaml`), without secrets. - `Broadcasts` holds PubSub messages until the transaction commits. `Audit.write` joins an outer transaction. Saving an unchanged node list is no longer logged as a change. - `/orgs/:org/blueprints` (admins): preview, apply, load current configuration. `GET /api/v1/blueprint` (any key), `POST /api/v1/blueprints/plan` and `/apply` (write keys), documented in the OpenAPI spec; problems are `422` keyed by path. - New dependency `yaml_elixir` 2.12.2 (approved). Maintenance note: it is current, but its parser `yamerl` last released in 2022. ## Verification - `mix format --check-formatted`, `mix compile --warnings-as-errors`, `mix credo --strict` (no issues), `mix dialyzer` (0 errors). - `mix test`: 837 passed on SQLite and on PostgreSQL. No migration in this change. - Live smoke on the compose stack: API plan, rejected blueprint (422 with path), apply, and Traefik serving the new route with the blueprint's header; re-apply reports unchanged; export contains the whole configuration. In the browser: load current, edit, preview (1 create, 1 update, 4 unchanged), apply, Traefik serving the result; audit log shows the events for both the user and the API key. The dev database was restored afterwards. - The README example was planned against a scratch database.
- strict YAML parser with error paths, size and depth limits, run in
  a guarded process; refuses anchors, aliases, tags, duplicate keys,
  unknown keys and secret keys
- plan/execute/export in one transaction through the existing context
  functions, so validation and audit events are unchanged; a plan is
  the same run rolled back
- Broadcasts holds PubSub messages until a transaction commits
- Audit.write joins an outer transaction; unchanged node lists are
  not recorded as changes
- add yaml_elixir
- /orgs/:org/blueprints: preview, apply and load current (admins)
- GET /api/v1/blueprint, POST /api/v1/blueprints/plan and apply
  with OpenAPI entries and 422 errors keyed by blueprint path
- README: blueprints section, API table and status
ryan.mcgee changed target branch from feat/audit-log to main 2026-10-15 18:07:30 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
White_Ghost_Cyber_Systems/traefik-control!10
No description provided.