# sic Run a command on a remote host — or inside a container several hops deep — without a shell re-parsing its arguments. `ssh host touch 'a b'` creates two files: `ssh` joins its arguments with spaces and hands the result to the remote login shell, which splits it again. `sic host touch 'a b'` creates one file named `a b`. `sic` sends the argument vector to a daemon on the host as length-prefixed fields, and the daemon calls `execvp` on it. No shell parses the arguments. ## Components - **`sic`** — client. Takes argv on the command line, frames it as netstrings, pipes the frame over `ssh` to `sicd`. - **`sicd`** — daemon on the target host. Reads netstrings from stdin and runs the command. Both are single static Go binaries (`cmd/sic`, `cmd/sicd`). A Python reference implementation of the daemon is in `gateway/sicd`. ## Wire format Each field is a netstring: `:,`. A frame is a mode field, zero or more argument fields, and an empty terminator: ``` * 0:, ``` `argv = ["touch", "a b"]` is `4:exec,5:touch,3:a b,0:,`. The length prefix means no byte needs escaping and no delimiter can collide with the payload. | mode | selector | behaviour | |------|----------|-----------| | `exec` (default) | `4:exec,` | `execvp(argv)`, no shell. Arguments are passed through unchanged. | | `sh` | `2:sh,` | one field passed to `sh -c`. Pipes, redirects, and globs work. | ## Usage ``` sic [arg ...] sic --sh '' sic -- [arg ...] # explicit separator ``` ``` sic host1 echo hello world sic host1 touch 'a b' # one file named "a b" sic host1 echo '$HOME' # literal, no expansion sic --sh host1 'echo hi | wc -c' ``` ## Nesting The frame survives being wrapped again, so `sic` reaches *into* containers with no shell anywhere in the path, at any depth. A target is a host followed by container hops separated by `/`: ``` sic host1/app cat /etc/os-release # into the 'app' guest on host1 sic host2/ct/svc touch 'a b' # host2 -> ct -> svc, ONE file, three layers deep ``` Each host's hop runtimes come from `/etc/sic/hosts.toml`, one stanza per host: ```toml [host1] nest = ["incus"] # host1/app -> incus exec app -- [host2] nest = ["pct", "docker"] # host2/ct/svc -> pct exec ct -- docker exec svc -- ``` This is what separates `sic` from a shell helper script. The `ssh` equivalent — ``` ssh host2 "pct exec ct -- docker exec svc -- touch \"a b\"" ``` nests quotes inside quotes inside quotes, and every layer re-splits the arguments; a body containing a space, `$()`, a pipe, or a newline breaks a different layer each time. `sic` frames the argv once and re-frames it at each hop, so `touch 'a b'` is one file whether it runs on the host or three containers deep. No escaping, at any level. ## Build ``` go build -o bin/sic ./cmd/sic go build -o bin/sicd ./cmd/sicd ``` ## Install Client: ``` cp bin/sic ~/bin/sic ``` Daemon on each target host. `sicd` runs what it is sent, so it must only be reachable over an authenticated transport. Pin it to a dedicated ssh key so the target can run nothing else with that key: ``` scp bin/sicd host:/tmp/sicd ssh host 'sudo install -m755 /tmp/sicd /usr/local/bin/sicd' # ~/.ssh/authorized_keys on the target: # command="sicd",no-port-forwarding,no-pty ssh-ed25519 AAAA... ``` Do not listen on a TCP or message-bus port. ## Test ``` printf '4:exec,2:id,0:,' | ssh host sicd sic host echo hello world ``` ## See also - `docs/design.md` — wire format, exec modes, transport and security, prior art. - `docs/design-nested.md` — how nested container hops resolve. - `demos/quoting-hell.py` — six cases run both ways, plain `ssh` versus `sic`. - `SKILL.md`, `SKILL-bg.md` — agent skill definitions (foreground and background).