The issue was recognized when a friend reported `scp` and `rsync` not working. I found the culprit to be not hooking up the session's/channel's I/O to spawned process, otherwise the new process assumed the null devices as its std{in,out,err}.
In other words, the newly created process was not reading/writing to/from the client's shell, rather from, e.g., secondary tty device (pair of the pty device). Thus the I/O was not channeled from the client to the process, rather to the PTY session.
The PR hooks the client's I/O directly as the newly spawned process' std{in,out}. This means when issuing a command which expects to read bytes from stdin, the stdin of the remote process is fed from the local client, not the remote tty. Now `scp` et. al. can work.
Onto item 2..
Issue 2:
If you're on macOS, go look for your user in `/etc/passwd`. You will not find it. macOS uses Open Directory to manage the users, so the users' {meta,}data are stored in the DirectoryService(8) database.
In #golang, implementation of user lookup in `os/user` pkg uses the native function `getpwnam_r` w/ cgo; but only parses `/etc/passwd` otherwise, except for lookups of current user which goes through syscall. Lookups return empty results for other users.
To avoid cgo and still maintain robust and comprehensive offering, I had to shell out to `dscl` on macOS to obtain full user details (TODO: swap with `dscacheutil`) and parse /etc/passwd on other *nix platforms.
There's another area in caddy-ssh where I use `os/user.Lookup`, but for that I'll use the github.com/tweekmonster/l… module there because I don't need the user's shell in that area.
I left the PR open to simmer while I dwell a bit more on its implementation, tweak few more things, and do final touches while I close other life items. It was another wild ride through PTY and how avoiding C while interacting with the OS brings in unforeseen complexity.