All 47 tests pass and lint is clean. Here's a summary of what I implemented.
Multiple SSH keys — backwards-compatible
The ssh-key config value now accepts either a single name (string) or a list of names. Existing single-name configs are unchanged.
ssh-key: my-key # still works exactly as before
ssh-key: # new: provision multiple keys
- alice-laptop
- bob-laptop
Code changes
machine/types.py—Config.ssh_key: str→Config.ssh_keys: list.machine/config.py— new_get_ssh_keys()helper normalizes thessh-keyvalue to a non-empty list, accepting a string or a list. Reuses_require_keyso the "missing key" error message is unchanged; rejects an empty list or an unsupported type with a graceful error.machine/cloud_config.py—get_user_data()resolves every key name and emits onessh-authorized-keysentry per key in the generated cloud-init payload.machine/subcommands/create.py— verifies each configured key exists (extracted to_verify_ssh_keys()to stay under the ruff complexity limit) and passes the full list to the provider.machine/subcommands/check.py— reports a PASS/FAIL line per key.machine/provider.py+providers/{digitalocean,vultr,gcp}.py—create_vm()'sssh_key_nameparameter is nowssh_key_names(a list). DigitalOcean attaches all keys viassh_keys=[...], Vultr viasshkey_id=[...], and GCP validates each name against project metadata.
Tests added
tests/test_config.py—TestGetSshKeys: scalar form, list form, str coercion, and rejection of missing/empty/wrong-type values.tests/test_cloud_config.py(new file) — single-key install, multi-key install in order, generated payload parses as valid YAML, and an unresolvable key aborts cleanly.tests/test_input_validation.py—TestMultipleSshKeys: end-to-end CLI checks that the scalar form still works, the list form parses, and an empty list is rejected gracefully (no traceback).
Docs
README updated: the ssh-key row in all three provider tables now notes list support, plus a new Multiple SSH keys section with examples.
One behavior note worth flagging: on DigitalOcean and Vultr, the listed keys are also attached to the provider's default account user (e.g. root), not only the cloud-init user — that matches the pre-existing single-key behavior. GCP has no per-VM key attachment, so there the keys are only validated against project metadata, as before.