BYO cloud-init with write_files or runcmd silently drops the seed's CA trust and disk grow #113

Closed
opened 2026-09-02 08:15:48 +00:00 by a73x · 1 comment
a73x commented 2026-09-02 08:15:48 +00:00 (Migrated from git.a73x.sh)

[claude 2026-09-02] Found in the software-factory spike. A vm_create with user cloud_init carrying write_files: produced a guest that refused eitri's delegated certificate (forge-1); one carrying only runcmd: booted with a 2.3G root on a 10G disk (forge-2). A guest with neither is fine.

Cause, verified against cloud-init 26.1's own source inside the guest: ConfigMerger._read_cfg merges [user cloud_config, vendor2, vendor] with util.mergemanydict, whose default merger is first-wins per key (dict keys not replaced, lists NOT appended). So any top-level list the user's document carries — write_files, runcmd — replaces the seed's vendor-data list wholesale. internal/agent/seed's vendorDataDoc comment ("applies even when a tenant brings their own cloud-init") is true only for keys the user does not also set.

What is lost: /etc/ssh/eitri_user_ca.pub + the TrustedUserCAKeys drop-in (write_files), the online grow of the root partition (runcmd), the sshd reload and chrony try-restart (runcmd), the clock drop-in (write_files).

Fix direction: cloud-init reads merge_how from the document being merged IN, and vendor-data is merged into the user's. Proven in the guest: vendor merge_how: "list(prepend)+dict(recurse_array,no_replace)+str()" makes vendor lists prepend to the user's (eitri's grow runs before the user's runcmd, on the grown disk) while every scalar the user set still wins (growpart stays user's if they set it, as the seed comment already promises). One line in vendorDataDoc plus a seed test; the real proof is a boot-gate leg that creates a VM with BYO write_files+runcmd and shows CA-cert SSH works and the root is grown.

Docs: docs/mcp.md line ~102 says CA trust is "provisioned through vendor-data" — still true, but vm_create's cloud_init description should say eitri's own write_files/runcmd are prepended, not replaced.


Migrated from git-collab issue 6b0704940e4fa1890bc5e56658c33f4f4938be51.

Original author: a73x dev@a73x.sh.

Close reason: merged in patch aba61eb9

[claude 2026-09-02] Found in the software-factory spike. A vm_create with user cloud_init carrying `write_files:` produced a guest that refused eitri's delegated certificate (forge-1); one carrying only `runcmd:` booted with a 2.3G root on a 10G disk (forge-2). A guest with neither is fine. Cause, verified against cloud-init 26.1's own source inside the guest: ConfigMerger._read_cfg merges [user cloud_config, vendor2, vendor] with util.mergemanydict, whose default merger is first-wins per key (dict keys not replaced, lists NOT appended). So any top-level list the user's document carries — write_files, runcmd — replaces the seed's vendor-data list wholesale. internal/agent/seed's vendorDataDoc comment ("applies even when a tenant brings their own cloud-init") is true only for keys the user does not also set. What is lost: /etc/ssh/eitri_user_ca.pub + the TrustedUserCAKeys drop-in (write_files), the online grow of the root partition (runcmd), the sshd reload and chrony try-restart (runcmd), the clock drop-in (write_files). Fix direction: cloud-init reads `merge_how` from the document being merged IN, and vendor-data is merged into the user's. Proven in the guest: vendor `merge_how: "list(prepend)+dict(recurse_array,no_replace)+str()"` makes vendor lists prepend to the user's (eitri's grow runs before the user's runcmd, on the grown disk) while every scalar the user set still wins (growpart stays user's if they set it, as the seed comment already promises). One line in vendorDataDoc plus a seed test; the real proof is a boot-gate leg that creates a VM with BYO write_files+runcmd and shows CA-cert SSH works and the root is grown. Docs: docs/mcp.md line ~102 says CA trust is "provisioned through vendor-data" — still true, but vm_create's cloud_init description should say eitri's own write_files/runcmd are prepended, not replaced. --- Migrated from git-collab issue `6b0704940e4fa1890bc5e56658c33f4f4938be51`. Original author: a73x <dev@a73x.sh>. Close reason: merged in patch aba61eb9
a73x commented 2026-09-02 08:21:23 +00:00 (Migrated from git.a73x.sh)

[claude 2026-09-02] Picking this up on branch fix/* in a worktree; Opus subagent implements under TDD, boot gate before merge.

[claude 2026-09-02] Picking this up on branch fix/* in a worktree; Opus subagent implements under TDD, boot gate before merge.
Sign in to join this conversation.
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
a73x/eitri#113
No description provided.