feat(ssh): add resolve method — selector to host list without connecting #149

Merged
adamhjk merged 1 commit from ssh-resolve into main 2026-07-28 15:42:33 +00:00
Owner

New resolve method resolves a host selector against the declared fleet and
records the result as a selection resource — pure data work, never spawns
ssh/scp/tailscale. Zero matches is a success (empty host list, count: 0,
count tag "0") so runModel callers and workflows finally get a structured
'matched nothing' answer; a malformed selector still throws, and the
connecting methods keep their throw-on-empty behavior (regression-tested).

  • ResolveArgsSchema takes only the selector (not TargetingSchema — the
    connection knobs are meaningless here) and SelectionSchema carries
    addressing + tags/attrs only, never credential material.
  • Instance resolve- uses a truncated SHA-256 of the normalized
    selector: stable per selector, so repeated resolves version one resource.
  • Writes tagged {fleet, method, count}; selection spec gc 10, infinite.
  • resolve is deliberately excluded from the sshpass-available check's
    appliesTo — that check gates methods that spawn ssh.
  • Model + manifest bumped to 2026.07.28.1 with a no-op upgrades entry;
    manifest Methods table and README document the new method.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

New resolve method resolves a host selector against the declared fleet and records the result as a selection resource — pure data work, never spawns ssh/scp/tailscale. Zero matches is a success (empty host list, count: 0, count tag "0") so runModel callers and workflows finally get a structured 'matched nothing' answer; a malformed selector still throws, and the connecting methods keep their throw-on-empty behavior (regression-tested). - ResolveArgsSchema takes only the selector (not TargetingSchema — the connection knobs are meaningless here) and SelectionSchema carries addressing + tags/attrs only, never credential material. - Instance resolve-<hash> uses a truncated SHA-256 of the normalized selector: stable per selector, so repeated resolves version one resource. - Writes tagged {fleet, method, count}; selection spec gc 10, infinite. - resolve is deliberately excluded from the sshpass-available check's appliesTo — that check gates methods that spawn ssh. - Model + manifest bumped to 2026.07.28.1 with a no-op upgrades entry; manifest Methods table and README document the new method. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(ssh): add resolve method — selector to host list without connecting
All checks were successful
CI / software-factory - test (pull_request) Has been skipped
CI / software-factory - lockfile up to date (pull_request) Has been skipped
CI / container-image - lint (pull_request) Has been skipped
CI / container-image - test (pull_request) Has been skipped
CI / container-image - lockfile up to date (pull_request) Has been skipped
CI / container-image - fmt (pull_request) Has been skipped
CI / container-image - check (pull_request) Has been skipped
CI / model/digitalocean - check (pull_request) Has been skipped
CI / model/hetzner-cloud - check (pull_request) Has been skipped
CI / model/digitalocean - lockfile up to date (pull_request) Has been skipped
CI / model/hetzner-cloud - lockfile up to date (pull_request) Has been skipped
CI / aws models - sample check (pull_request) Has been skipped
CI / aws models - lockfiles up to date (pull_request) Has been skipped
CI / gcp models - lockfiles up to date (pull_request) Has been skipped
CI / cloudflare models - lockfiles up to date (pull_request) Has been skipped
CI / codegen - check (pull_request) Has been skipped
CI / codegen - fmt (pull_request) Has been skipped
CI / gcp models - sample check (pull_request) Has been skipped
CI / cloudflare models - sample check (pull_request) Has been skipped
CI / codegen - lint (pull_request) Has been skipped
CI / vault/1password - check (pull_request) Has been skipped
CI / vault/aws-sm - check (pull_request) Has been skipped
CI / vault/azure-kv - check (pull_request) Has been skipped
CI / vault/1password - fmt (pull_request) Has been skipped
CI / vault/aws-sm - fmt (pull_request) Has been skipped
CI / codegen - lockfile up to date (pull_request) Has been skipped
CI / CI Security Review (pull_request) Has been skipped
CI / Adversarial Code Review (pull_request) Has been skipped
CI / Claude Code Review (pull_request) Successful in 3m12s
CI / Merge Gate (pull_request) Successful in 27s
5690d30d6d
New resolve method resolves a host selector against the declared fleet and
records the result as a selection resource — pure data work, never spawns
ssh/scp/tailscale. Zero matches is a success (empty host list, count: 0,
count tag "0") so runModel callers and workflows finally get a structured
'matched nothing' answer; a malformed selector still throws, and the
connecting methods keep their throw-on-empty behavior (regression-tested).

- ResolveArgsSchema takes only the selector (not TargetingSchema — the
  connection knobs are meaningless here) and SelectionSchema carries
  addressing + tags/attrs only, never credential material.
- Instance resolve-<hash> uses a truncated SHA-256 of the normalized
  selector: stable per selector, so repeated resolves version one resource.
- Writes tagged {fleet, method, count}; selection spec gc 10, infinite.
- resolve is deliberately excluded from the sshpass-available check's
  appliesTo — that check gates methods that spawn ssh.
- Model + manifest bumped to 2026.07.28.1 with a no-op upgrades entry;
  manifest Methods table and README document the new method.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner

Code Review

Blocking Issues

None.

Suggestions

  1. selectorHash collision surface is undocumented. The function truncates SHA-256 to 6 bytes (48 bits, 12 hex chars). For typical usage (tens of distinct selectors per fleet) the collision probability is negligible, but the comment says only "Stability matters" without acknowledging the trade-off. A one-liner noting the shortened length is intentional for compact instance names and acceptable given the small selector cardinality would help future readers avoid second-guessing it.

  2. Credential-leak test uses "auth" as a banned substring. The test at "resolve: selection records carry no credential material" scans JSON.stringify(write.data) for the literal string "auth". This is correct for RESOLVE_FLEET (none of the allowed fields or their values contain that substring), but it would false-positive if a future test fleet introduced a host with a name, tag, or attr value like "authorized" or "auth_group". Using a more targeted probe — e.g. searching for "\"auth\":" (the JSON object key) — would be more robust. Non-blocking because the current test fleet is fine.

  3. runResolveOn helper in the test always asserts out.dataHandles.length === 1. The resolve contract (one selection write per call) is tested implicitly here, but a dedicated test asserting the returned handle matches the written instance name would make the contract explicit and would catch a regression if runResolve were changed to return multiple handles. Very minor — the current coverage is already solid.

## Code Review ### Blocking Issues None. ### Suggestions 1. **`selectorHash` collision surface is undocumented.** The function truncates SHA-256 to 6 bytes (48 bits, 12 hex chars). For typical usage (tens of distinct selectors per fleet) the collision probability is negligible, but the comment says only "Stability matters" without acknowledging the trade-off. A one-liner noting the shortened length is intentional for compact instance names and acceptable given the small selector cardinality would help future readers avoid second-guessing it. 2. **Credential-leak test uses `"auth"` as a banned substring.** The test at `"resolve: selection records carry no credential material"` scans `JSON.stringify(write.data)` for the literal string `"auth"`. This is correct for `RESOLVE_FLEET` (none of the allowed fields or their values contain that substring), but it would false-positive if a future test fleet introduced a host with a name, tag, or attr value like `"authorized"` or `"auth_group"`. Using a more targeted probe — e.g. searching for `"\"auth\":"` (the JSON object key) — would be more robust. Non-blocking because the current test fleet is fine. 3. **`runResolveOn` helper in the test always asserts `out.dataHandles.length === 1`.** The resolve contract (one `selection` write per call) is tested implicitly here, but a dedicated test asserting the returned handle matches the written instance name would make the contract explicit and would catch a regression if `runResolve` were changed to return multiple handles. Very minor — the current coverage is already solid.
adamhjk deleted branch ssh-resolve 2026-07-28 15:42:34 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
2 participants
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
swamp-club/swamp-extensions!149
No description provided.