Add a --host flag so serve can bind an interface other than loopback - #345
Draft
mando wants to merge 2 commits into
Draft
Add a --host flag so serve can bind an interface other than loopback#345mando wants to merge 2 commits into
mando wants to merge 2 commits into
Conversation
The listener was hardcoded to 127.0.0.1, so running Argus on a home server, NAS, or inside a container meant an SSH tunnel or a separate proxy to reach the dashboard from another machine. Only the port was configurable. Add a `host` setting resolved through the standard chain (managed > --host > ARGUS_HOST > argus.json > default), plumbed through both `serve` and `run`. The default stays loopback, so nothing changes for anyone who doesn't pass it. A value that isn't a bare address (a URL, a host:port pair, whitespace, a path) warns and resolves back to loopback, so a typo narrows exposure instead of widening it. No `ui` entry: like `readOnly` this is a deployment switch, and it would be the one setting whose remote edit widens the editor's own exposure. Nothing on the port authenticates, so a non-loopback bind says so at startup, naming the address and port, and suggests --read-only when it's off. Both loopback-dependent guards keep their current behavior, which is the part worth stating explicitly: - rejectUnsafeHost stays loopback-only. Adding the configured host to the allowlist would reopen the DNS-rebinding hole for exactly that hostname. So a remote browser reads the dashboard, labels and hides sessions, and cannot write settings, run the connection test, touch stored API keys, or reach /mcp. The startup log says this before the user hits a refusal. - rejectCrossSite keys off Sec-Fetch-Site plus the x-argus-app header, both properties of the request rather than the bind address, so CSRF defense works unchanged from a LAN origin. Now covered by a test. The desktop app passes --host 127.0.0.1 to its sidecar so a widened argus.json or ARGUS_HOST can't expose the tray app's backend port; its front-door proxy was and stays loopback-only. Also stop `argus config set` from writing a rejected value: parse() returning undefined meant the key was silently dropped and reported as `= undefined`. Docs: a new "Opening Argus to your network" section in the CLI reference (what it exposes, the --read-only pairing, what stays local), the setting in the settings reference, a privacy section, and the guard reasoning in the internals configuration and agent-access pages. Verified by binding 0.0.0.0 against an empty temp store: reachable from the LAN address, settings write refused, log lines as described. Closes #344 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #344.
servewas hardcoded to127.0.0.1, so running Argus on a home server, NAS, or in a container meant an SSH tunnel or a separate proxy to reach the dashboard from a laptop. Only the port was configurable.What this adds
hostsetting resolved through the standard chain:managed > --host > ARGUS_HOST > argus.json > default, plumbed through bothserveandrun(one sharedhostArgso the two can't drift).127.0.0.1. Nothing changes for anyone who doesn't pass the flag.uientry. LikereadOnly, this is a deployment switch, and it would be the one setting whose remote edit widens the editor's own exposure.host:port, whitespace, a path) warns and resolves back to loopback, so a typo narrows exposure rather than widening it. A hostname that merely doesn't resolve still gets through and surfaces as a bind failure naming the address that was asked for.--read-onlyas the pairing (only when it's off).The security decisions
Both loopback-dependent guards keep their current behavior, which is the choice the issue asked to settle:
rejectUnsafeHoststays strict. Widening the allowlist to the configured host would reopen the DNS-rebinding hole for exactly that hostname. So a browser on another machine reads the dashboard and can label and hide sessions, but cannot write settings, run the connection test, touch stored API keys, or reach/mcp. The startup log says this before the user hits a refusal, and there's a test asserting each of those routes 403s for a LANHost.rejectCrossSiteis unaffected, since it keys offSec-Fetch-Siteplusx-argus-app, both properties of the request rather than the bind address. Now covered by a test from a LAN origin (same-origin passes, cross-site still 403s).--host 127.0.0.1explicitly, so a widenedargus.json/ARGUS_HOSTcan't expose the tray app's backend port. The front-door proxy was and stays loopback-only. (A managedhoststill wins, by design.)Drive-by
argus config setno longer writes a value the setting rejected:parse()returningundefinedmeant the key was silently dropped and reported as= undefined. It now fails with the invalid value named. Only affects present-but-invalid values; clearing with an empty string still works.Docs
A new "Opening Argus to your network" section in the CLI reference (what it exposes, the
--read-onlypairing, what stays local), the setting in the settings reference, a privacy section, and the guard reasoning in the internals configuration and agent-access pages. The local-only positioning is unchanged: parsing is still local andsyncis still the only thing that uploads.Verification
bun run typecheck,bun test(817 pass),bun run docs:build, andcargo checkon the desktop shell.0.0.0.0against an empty temp store: reachable from the LAN address, the settings write refused, log lines as described.Known rough edge, deliberately left: a remote browser's Settings screen still renders its controls and will surface a 403 when used. Teaching the SPA to hide them would mean reporting per-request loopback status through
/healthz, which felt like a separate change.