fix(sys-front-inj-design): let only the destination's owner write the CDN stylesheet

The role renders a stylesheet per host context and copied it with a bare copy: to css_app_dst, a path carrying no network and no host dimension - one style.css serves the clearnet and the onion CDN vhost alike, while the URLs baked into it depend on whether the rendering host sits in svc-net-tor.

What carries a worker's render onto the manager is sys-stk-full/tasks/main.yml, which includes the front-proxy chain with delegate_to the openresty host and run_once. delegate_to changes where a task executes, not whose variables it uses, so the variable context stays the play's first host while execution lands on the manager: a clearnet render is written onto the manager's own disk. No shared storage is involved - /var/www is a plain volume and svc-storage-nfs-client mounts only /var/lib/infinito.

That is also why the sibling survived. stack_host_template reads IS_STACK_HOST from the delegating host's vars, so webmail-design.inc.php.j2 skipped with 'this host does not own the destination' and kept the manager's version, while the bare copy: overwrote it. Same delegation, same absolute URL, opposite outcome, and the gate is the only difference.

After this change all four writers in the role are owner-gated and none is not: 00_core.yml ships layer.css, default.css and bootstrap.css through stack_host_template, assets.yml ships the branding through design_assets, which subclasses stack_host_copy, and these two were the stragglers. If the gate could starve the CDN, those sheets and every logo would already be missing.

The app_style_present fact is deliberately left ungated: head_sub.j2 reads it to decide whether to emit the link element, so gating it would silently drop the stylesheet reference. Only the two filesystem mutations are gated. A secondary effect is that the file and the link can no longer disagree, since both now skip or both run in the same invocation.

IS_STACK_HOST sits second in the removal task's when list on purpose: test_no_repeated_when_runs compares the leading element, and putting it first would have formed a run with the neighbouring task.

Nothing was executed - no lint run, no deploy. The blast radius is 20 call sites and 64 roles shipping a per-app stylesheet, with compose and manager contexts unchanged by construction. test_stack_host_copy_migration.py fires only on directories.instance, so the CDN root is uncovered, which is why this survived.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-10-15 16:19:55 +02:00
1 parent 62e4d1e363
commit 8187db557c
1 file changed
+4 -2
+4 -2
View File
@@ -18,13 +18,15 @@
- name: "🚀 Deploy per-app '{{ app_style_src }}' to '{{ css_app_dst }}'"
when: app_style_present | bool
copy:
stack_host_copy:
content: "{{ app_style_css }}"
dest: "{{ css_app_dst }}"
mode: "0644"
- name: "🧹 Remove the per-app stylesheet '{{ css_app_dst }}' a role no longer ships"
when: not (app_style_present | bool)
when:
- not (app_style_present | bool)
- IS_STACK_HOST | bool
ansible.builtin.file:
path: "{{ css_app_dst }}"
state: absent