Motivation
The current safety guard for non-local write targets (main.go:113-119 — warnIfNonLocal) just prints a warning and sleeps 10 seconds before auto-proceeding. In practice that's easy to miss:
- The warning scrolls past in a busy terminal.
chcopy keeps going on its own, so an unattended invocation will happily TRUNCATE + INSERT against whatever CHCOPY_LOCAL_HOST points to — including a remote/prod cluster if someone fat-fingers the env.
- The current message doesn't actually show which host is about to be written to, so even an attentive operator can't sanity-check the target without re-reading their shell env.
We should fail closed: when the target doesn't look local, require explicit confirmation that names the host.
Proposed behavior
When CHCOPY_LOCAL_HOST does not resolve to localhost or a private / docker-bridge range, prompt before running anything:
WARNING: target ClickHouse "ch-prod-eu-1.example.com" does not look local.
You are about to write to this server. Type 'yes' to proceed:
- Must include the resolved hostname (the value of
CHCOPY_LOCAL_HOST as configured) so the user can verify the target.
- Only the literal answer
yes proceeds. Anything else (including empty line, no, EOF, Ctrl-C) aborts with a non-zero exit code.
- Local targets: no prompt, behavior unchanged.
--dry-run: no prompt (nothing is written).
Non-interactive / CI escape hatch
If stdin is not a TTY and the target is non-local, abort with a clear error unless the user opts in:
- New flag
--yes (or --force) to skip the prompt.
- Without
--yes on a non-TTY non-local run, exit with something like:
error: target "X" is non-local and stdin is not a TTY; pass --yes to confirm non-interactively.
This keeps scripted/CI usage explicit rather than silently auto-confirming after 10s.
Acceptance criteria
Out of scope
- Per-table or per-host allowlists (could be a follow-up).
- Changing the local/private-range heuristic itself (
isLocal stays as-is).
Motivation
The current safety guard for non-local write targets (
main.go:113-119—warnIfNonLocal) just prints a warning and sleeps 10 seconds before auto-proceeding. In practice that's easy to miss:chcopykeeps going on its own, so an unattended invocation will happilyTRUNCATE+INSERTagainst whateverCHCOPY_LOCAL_HOSTpoints to — including a remote/prod cluster if someone fat-fingers the env.We should fail closed: when the target doesn't look local, require explicit confirmation that names the host.
Proposed behavior
When
CHCOPY_LOCAL_HOSTdoes not resolve to localhost or a private / docker-bridge range, prompt before running anything:CHCOPY_LOCAL_HOSTas configured) so the user can verify the target.yesproceeds. Anything else (including empty line,no, EOF, Ctrl-C) aborts with a non-zero exit code.--dry-run: no prompt (nothing is written).Non-interactive / CI escape hatch
If stdin is not a TTY and the target is non-local, abort with a clear error unless the user opts in:
--yes(or--force) to skip the prompt.--yeson a non-TTY non-local run, exit with something like:error: target "X" is non-local and stdin is not a TTY; pass --yes to confirm non-interactively.This keeps scripted/CI usage explicit rather than silently auto-confirming after 10s.
Acceptance criteria
yes, aborts on anything else.--yes→ abort with clear error.--yes→ proceed without prompting.--dry-runalways skips the prompt.--yespath and the abort-without-TTY path.Out of scope
isLocalstays as-is).