feat(add): provider-neutral remo add/remo remove for SSH-reachable hosts (014)#77
Merged
Conversation
Feature 014 `remo add` — register a single SSH-reachable environment
(new registry type `ssh`, access_mode=direct) plus `remo remove`, so a
user with only SSH access (no hypervisor/cloud API) can `remo shell`/`cp`
into a box. Companion to 013's filtered sync.
Adds the full design set for the 014 branch:
- spec.md: 4 clarifications integrated (verify fail-closed; persisted
--identity; reject un-bracketed IPv6; `remo remove` command)
- plan.md + research.md: reuse KnownHost positional serialization for the
ssh type (port in instance_id, identity in region) — no format change,
backward-compatible (SC-007); type-gated ssh_port/ssh_identity; wire
port+identity into build_ssh_opts (shared by shell AND cp); skip the
tools version-check for unmanaged hosts (FR-011); whole-registry
collision scan (FR-010)
- data-model.md, contracts/{add,remove}-command.md, quickstart.md
- tasks.md: 24 TDD tasks, MVP = Phases 1-3; analyze remediations applied
(FR-006 picker coverage, --verify moved to US3, cp coverage note)
Point .specify/feature.json at specs/014-register-ssh-host.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HAPB5GA4NVUgKFsVFVJDxN
…e hosts Feature 014. Register a single SSH-reachable environment with only SSH access (no hypervisor/cloud API) as a new registry type `ssh` (access_mode=direct), then `remo shell`/`remo cp` into it like any other host. Storage (no registry format change): the ssh type reuses KnownHost positional fields — instance_id=port, region=identity — via type-gated `ssh_port`/ `ssh_identity` accessors. build_ssh_opts (shared by shell AND cp) emits `-o Port=`/identity only for type=ssh, so every provider's argv is unchanged (a proxmox vmid in instance_id is never read as a port). Commands: - `remo add NAME [user@]host[:port] [--user --port --identity --verify --yes]` create or in-place update (no dup); whole-registry collision check refuses to overwrite OR shadow a provider entry, including incus/proxmox node/container short-names (FR-010); un-bracketed IPv6 rejected (FR-013); `--verify` is a fail-closed reachability probe that writes nothing on failure (FR-014). - `remo remove NAME [--yes]` local-only deregister, no remote call (FR-008); refuses provider-managed hosts (FR-009). Also: - `remo shell` skips the tools/version check for ssh hosts -> plain login shell for unmanaged boxes (FR-011). - FR-012 guard: shared guard_not_added_ssh_host() makes provider destroy/ snapshot/resize against an ssh host fail with a clear message (20 call sites). - README documents add/remove; full test coverage per conditional branch (Constitution II). 105 new tests; full suite green (1266 passed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAPB5GA4NVUgKFsVFVJDxN
…check Code-review fixes for feature 014: - Registry line injection/corruption (FR-013): a ':' in --user (or user@) shifted every later field on reload, and a newline in user/host/identity could inject a forged registry line. Only the identity's ':' was checked. Add _reject_unsafe_field() rejecting ':' and control chars in user/host, and extend the identity check to control chars. Nothing is written on rejection. - --verify fail-closed on unknown host keys: BatchMode=yes could not accept a new host key, so a reachable never-before-seen host failed "Host key verification failed" and was reported unreachable. The probe now adds StrictHostKeyChecking=no + UserKnownHostsFile=/dev/null (reachability/auth check only; does not pre-seed ~/.ssh/known_hosts). - Remove dead PROVIDER_TYPES constant. +6 regression tests. Full suite: 1272 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HAPB5GA4NVUgKFsVFVJDxN
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Feature 014-register-ssh-host. Adds a provider-neutral
remo addthat registers a single SSH-reachable environment into the registry with only SSH access — no hypervisor host access, cloud credentials, or API token — plusremo removeto deregister it. Companion to 013's filtered sync:sync= bulk provider discovery;add= single manual registration.Added hosts are a new registry
type = "ssh"(access_mode = direct), soremo shellandremo cpconnect through the existing direct-SSH path with no user-visible special-casing.Design highlights
sshtype reusesKnownHostpositional fields —instance_id= port,region= identity — via type-gatedssh_port/ssh_identityaccessors. Existing 4/6/7-field provider lines parse unchanged.build_ssh_optsemits-o Port=/identity only fortype=ssh, soremo shellandremo cpboth honor them (FR-005) while every provider's SSH argv stays byte-identical (a Proxmox numeric vmid ininstance_idis never read as a port).node/containershort-names thatremo shell <name>would resolve to. (This shadow case was caught by end-to-end validation and fixed with a regression test.)remo shellskips the tools/version check forsshhosts → plain login shell instead of a bogus "Update tools?" prompt.guard_not_added_ssh_host()makes providerdestroy/snapshot/resize against an added host fail with a clear message (20 call sites) rather than an opaque error.Commands
--yes), never duplicating.--verifyis a fail-closed SSH reachability probe: on failure it surfaces the error, writes nothing, exits non-zero. Without it, no network round-trip.[::1]:22deferred).remo removeis local-only (no remote connection/change) and refuses provider-managed hosts.Testing
mypy+ruffclean.shell/cphandshake to a real host) is the one manual spot-check left for a reviewer with a reachable box.Spec-kit artifacts (spec/plan/research/data-model/contracts/quickstart/tasks) are included under
specs/014-register-ssh-host/.🤖 Generated with Claude Code