Skip to content

Config-driven remote hosts for agentsview sync#600

Open
wesm wants to merge 7 commits into
mainfrom
issue-533
Open

Config-driven remote hosts for agentsview sync#600
wesm wants to merge 7 commits into
mainfrom
issue-533

Conversation

@wesm
Copy link
Copy Markdown
Member

@wesm wesm commented Jun 7, 2026

Closes #533.

Adds a [[remote_hosts]] array to config.toml so agentsview sync can sync
from a small fleet of machines without a per-host cron/launchd job.

[[remote_hosts]]
host = "devbox1"
user = "jesse"   # optional
port = 22        # optional (0/omitted = ssh default)

[[remote_hosts]]
host = "laptop2"

Behavior:

  • agentsview sync with no --host runs the local sync, then syncs each
    configured host over SSH in declared order. --full applies to every sync in
    the run.
  • agentsview sync --host X is unchanged: it ignores remote_hosts and syncs
    only that host, failing fast on error.
  • A configured host that fails at runtime is logged to the debug log and listed
    in a stderr summary; the remaining hosts still run, and the command exits
    non-zero if any configured host failed.
  • Invalid remote_hosts config (empty host, or port outside 0..65535) fails
    before any sync. This semantic check runs only on the no---host path, so a
    bad entry cannot affect sync --host X. Host/user values are trimmed at load
    so the validated value matches what is passed to ssh.

sync --help and the root help document the config block.

RemoteHosts is config-file/CLI only (json:"-"); it is not exposed to the
settings API, so there is no web-UI editing of the host list.

wesm and others added 4 commits June 7, 2026 08:13
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@roborev-ci
Copy link
Copy Markdown

roborev-ci Bot commented Jun 7, 2026

roborev: Combined Review (1121f9f)

Medium/High issues found; no Critical findings.

High

  • internal/config/config.go:182, cmd/agentsview/sync.go:118
    ValidateRemoteHosts accepts any non-empty host/user, and runRemoteSyncOnce passes those values into SSH. Because the SSH argument builder places the target before the -- delimiter, a configured host beginning with an SSH option can be interpreted as an option rather than a hostname. For example, host = "-oProxyCommand=sh -c '...'" could execute an attacker-controlled ProxyCommand locally during agentsview sync.

    Fix: Reject host and user values that can be parsed as SSH options, especially values beginning with - or containing control characters. Move the SSH option delimiter before the target, e.g. ssh [opts] -- target remote-command. Apply the same validation to config-driven hosts and the existing --host path.

Medium

  • cmd/agentsview/sync.go:120
    Configured remotes that share the same host but differ by user or port are synced under the same remote identity. RemoteSync uses Host alone for Machine, session ID prefixes, path rewrites, and remote skip-cache keys, so entries like user=a host=x and user=b host=x can overwrite or merge sessions and skip-cache state.

    Fix: Either reject duplicate remote_hosts entries with the same host in ValidateRemoteHosts, or introduce a distinct stable remote identity that includes user and port, then use it consistently for machine names, ID prefixes, and remote skipped-file cache keys.


Panel: ci_default_security | Synthesis: codex, 12s | Members: codex_default (codex/default, done, 2m52s), codex_security (codex/security, done, 2m34s) | Total: 5m38s

ssh.RemoteSync namespaces sessions (Machine, IDPrefix) and the remote
skip cache by host alone, so two remote_hosts entries sharing a host
collide regardless of user/port: they thrash each other's skip cache
each run and share the host~ session ID namespace. Reject duplicate
host values in ValidateRemoteHosts instead of silently sharing or
overwriting cached state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@roborev-ci
Copy link
Copy Markdown

roborev-ci Bot commented Jun 7, 2026

roborev: Combined Review (10eaf8b)

Summary verdict: one medium issue remains; no high or critical findings were reported.

Medium

  • cmd/agentsview/sync.go:87 - Configured remote hosts only receive cfg.Full, while runLocalSync can trigger an automatic full resync when database.NeedsResync() is true. After a data-version bump, remote sessions preserved from the old DB can remain stale because remote sync loads the existing remote skip cache and skips unchanged remote files.

    Suggested fix: Capture didResync := runLocalSync(...), compute forceFull := cfg.Full || didResync, pass forceFull into runRemoteHosts, and add coverage confirming that an automatic local resync propagates full=true to configured remotes.


Panel: ci_default_security | Synthesis: codex, 7s | Members: codex_default (codex/default, done, 4m55s), codex_security (codex/security, done, 1m0s) | Total: 6m2s

runLocalSync returns whether a full resync ran (--full or an automatic
data-version resync). doSync discarded it and always passed cfg.Full to
the remote fan-out. Propagate it: a local resync now forces every
configured remote full too, so remote sessions are re-parsed rather than
skipped via the remote skip cache.

This is correct-by-construction rather than a live fix: a resync swaps in
a fresh DB whose remote_skipped_files table starts empty, so remotes
already re-process fully today. Propagating the signal keeps that correct
if the remote skip cache is ever preserved across a resync, and matches
how pg_watch/usage/main already branch on NeedsResync.

Extracts syncLocalAndRemotes as an injectable seam and tests that an
automatic resync propagates full=true to every configured host.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@roborev-ci
Copy link
Copy Markdown

roborev-ci Bot commented Jun 8, 2026

roborev: Combined Review (1fe7ca8)

No issues found.


Panel: ci_default_security | Synthesis: codex | Members: codex_default (codex/default, done, 4m30s), codex_security (codex/security, done, 1m25s) | Total: 5m55s

Remote sync shells out to ssh with no options, so an unattended run
could stall on a password/passphrase or host-key prompt, or slow-fail
on an unreachable host -- and config-driven fan-out lets one bad host
drag down the rest. Pass BatchMode=yes (never prompt; require key-based
auth) and ConnectTimeout so runs fail fast with a clear error instead.
Defaults follow any caller sshOpts so an explicit override still wins.

Applies to both sync --host and the configured fan-out; host-key
checking is left to the user's ssh config (unchanged). Document the
key-based (passwordless) SSH requirement in sync and root help.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@roborev-ci
Copy link
Copy Markdown

roborev-ci Bot commented Jun 8, 2026

roborev: Combined Review (dbcbb7f)

No issues found.


Panel: ci_default_security | Synthesis: codex | Members: codex_default (codex/default, done, 6m50s), codex_security (codex/security, done, 3m36s) | Total: 10m26s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: config-driven list of remote hosts for sync

1 participant