serve: add --bind ADDR for VPS / remote-control exposure
ober
bfacc320eb82478b9d46ffccfdf5faf9a831b92e
--- a/src/jcode/ui/cli.ss +++ b/src/jcode/ui/cli.ss @@ -166,7 +166,10 @@ COMMANDS: session resume Resume a previous session config Show or edit configuration serve Start JSONL server (stdio mode) - serve --port N Start TCP server on 127.0.0.1:N + serve --port N Start TCP server (default bind: 127.0.0.1) + serve --port N --bind ADDR + Bind to a specific address (e.g. 0.0.0.0 for + VPS / remote control). Token still required. serve --rotate-token Generate new auth token and exit serve --show-token Display current auth token and exit @@ -175,6 +178,7 @@ EXAMPLES: jcode \"Read main.ss\" One-shot query jcode session list List sessions jcode serve --port 8321 Start TCP server for Android app + jcode serve --port 8321 --bind 0.0.0.0 Expose on VPS (front with TLS) ")) (def (interactive-mode opts) --- a/src/jcode/ui/serve.ss +++ b/src/jcode/ui/serve.ss @@ -369,12 +369,18 @@ ;; ── TCP server loop ─────────────────────────────────────────────────── -(def (tcp-serve-loop port-num) - "Listen on 127.0.0.1:port, accept one client at a time, auth + event loop." +(def (tcp-serve-loop bind-addr port-num) + "Listen on bind-addr:port, accept one client at a time, auth + event loop." (let ((token (ensure-token!)) - (srv (tcp-listen "127.0.0.1" port-num))) - (fprintf (current-error-port) "[INFO] listening on 127.0.0.1:~a~n" - (tcp-server-port srv)) + (srv (tcp-listen bind-addr port-num))) + (fprintf (current-error-port) "[INFO] listening on ~a:~a~n" + bind-addr (tcp-server-port srv)) + (unless (or (equal? bind-addr "127.0.0.1") (equal? bind-addr "::1")) + (fprintf (current-error-port) + "[WARN] non-loopback bind (~a): token traverses cleartext TCP.~n" + bind-addr) + (fprintf (current-error-port) + " Front with TLS (stunnel/caddy) or SSH/WireGuard for public exposure.~n")) (flush-output-port (current-error-port)) (let accept-loop () (fprintf (current-error-port) "[INFO] waiting for client...~n") @@ -414,6 +420,8 @@ (begin (fprintf (current-error-port) "[ERROR] invalid port: ~a~n" (cadr args)) (exit 1))))) + ((and (equal? (car args) "--bind") (pair? (cdr args))) + (loop (cddr args) (cons (cons 'bind (cadr args)) opts))) ((equal? (car args) "--rotate-token") (loop (cdr args) (cons '(rotate-token . #t) opts))) ((equal? (car args) "--show-token") @@ -448,12 +456,14 @@ (thread-join! t-mcp) (thread-join! t-lsp)) ;; Dispatch to TCP or stdio mode - (let ((port-opt (assoc 'port opts))) + (let ((port-opt (assoc 'port opts)) + (bind-opt (assoc 'bind opts))) (if port-opt ;; TCP mode - (begin - (log-info logger "serve-start" `((mode . "tcp") (port . ,(cdr port-opt)))) - (tcp-serve-loop (cdr port-opt))) + (let ((bind-addr (if bind-opt (cdr bind-opt) "127.0.0.1"))) + (log-info logger "serve-start" + `((mode . "tcp") (bind . ,bind-addr) (port . ,(cdr port-opt)))) + (tcp-serve-loop bind-addr (cdr port-opt))) ;; stdio mode (backward compat) (begin (log-info logger "serve-start" '((mode . "stdio")))