Security posture — auth boundary and accepted risk¶
Confirms and documents the read-auth boundary raised as the "unauth-precedent
context note" finding (ARGUS-814, batch 2026-07-19-argus, artifact #1108).
This is a contract doc like API conventions — it
records a deliberate decision, not a TODO.
The gap¶
GET /tasks.json (and the rest of the hub's *.json read surface —
/runs.json, /artifacts.json, /projects.json, /models.json, and
similar) returns 200 with the full payload to an unauthenticated caller.
Confirmed live post-ARGUS-810 (the /ui/* auth-bypass fix): an authed and
an unauthed GET /tasks.json return identical 200 bodies, while
unauthenticated /ui/* now correctly 404s. Authorization: Bearer
<ARGUS_CAPTURE_TOKEN> is checked on mutations and a short list of
non-public reads (see Auth in the API
conventions) but is decorative on the general *.json surface — every
client that sends it (the app, watchers, monitors) does so voluntarily, not
because the server demands it.
Decision: accepted, not a bug¶
The hub's ingress is Tailscale-only (homelab-infra convention — no public
exposure; Funnel is reserved for external webhooks). Anything that can reach
argus.aaronbrazier.com is already a device on Aaron's tailnet, the same
trust boundary every other homelab exporter (/metrics, unauthenticated
Prometheus scrape) relies on. Within that boundary:
- The
*.jsonread surface carries task/run/artifact/config state, not secrets — no credentials, no tokens, no PII cross aGETon this surface. - Every one of these routes is already commented
Public read like the rest of the *.json surfaceat its registration ininternal/hub/server.go; this has been the consistent, intentional shape of the API since the phase-3 app surfaces multiplied.jsonendpoints (ARGUS-263), not an oversightARGUS-810missed. - Requiring a bearer on every read would not remove a real attacker capability inside the tailnet (an unauthenticated process on the tailnet can already reach the Postgres-backed state other ways), but it would add a hard failure mode to every current and future read client for no corresponding safety gain.
Aaron's ruling (2026-07-19, in response to ARGUS-814): accept the
open-read-on-tailnet stance for the *.json surface. Mutations, the
human-only approve group, and the report/events bearer group stay gated as
documented in Auth — this decision only covers
reads that were already public in practice.
When this changes¶
Revisit this decision (open a new ADR, don't just edit this file) if any of the following becomes true:
- Tailscale-only ingress is relaxed (a Funnel route added for something other than webhooks, or the tailnet gains an untrusted member class).
- A
*.jsonread surface starts carrying a secret, credential, or personally identifying payload. - The read surface needs per-caller authorization (not just "on the tailnet
or not") — e.g. multi-tenant use, which
USER.md's single-user assumption currently rules out.