auth: take the default header in dev too
The Caddy site writes the login to Remote-User, the name the app already takes;
a dev override pointing somewhere else is only a 401 it makes.
The task sets nothing now, so one header answers to them all —
the loop and the tailnet read the same name, and the docs make the call.

Assisted-by: Claude Opus 5 via Claude Code
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
change lxquxnzonnrxmkrlplyskvvyouwxywyt
commit 1ca63f24871a5343d6c30c31fd6a568a65d540f4
author Alpha Chen <alpha@kejadlen.dev>
date
parent pvwxnrxz
diff --git a/AGENTS.md b/AGENTS.md
index e5bba4d..f0ca4b7 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -94,11 +94,10 @@ not a fixture; edit a `.vcf` to change what the replay serves.
   an element's block, `it` is the builder, not an enclosing block's value,
   so name the outer block's parameter.
 - Every request needs the identity header or it gets a 401, so a bare
-  `curl` against `rake dev` is refused until you pass
-  `Tailscale-User-Name`, which is what that task sets
-  `PRO_TACTS_IDENTITY_HEADER` to (`Remote-User` is the default everywhere
-  else). The security of that rests on the app being reachable only
-  through a proxy that writes the header itself — never bind it to
+  `curl` against `rake dev` is refused until you pass `Remote-User`
+  (`PRO_TACTS_IDENTITY_HEADER` renames it, and the refusal says which
+  name it read). The security of that rests on the app being reachable
+  only through a proxy that writes the header itself — never bind it to
   anything but localhost.
 - DAV exchanges that went wrong (a status of 400 or more but 401, a
   crash, or a report to Sentry) are written whole to `log/exchange.log`,
diff --git a/Rakefile b/Rakefile
index 0747cd3..aa19bed 100644
--- a/Rakefile
+++ b/Rakefile
@@ -30,13 +30,6 @@ task :dev do
   ENV["PRO_TACTS_EXCHANGE_LOG"] ||= "log/dev.log"
   File.truncate("log/dev.log", 0) if File.exist?("log/dev.log")
 
-  # No proxy in front of a dev server, so the identity header is
-  # whatever a client passes by hand. The name the tailnet deployment's
-  # proxy writes the login to, rather than the Remote-User default, so a
-  # dev session reads the header that deployment reads
-  # (ProTacts::Config#identity_header).
-  ENV["PRO_TACTS_IDENTITY_HEADER"] ||= "Tailscale-User-Name"
-
   Dir.mktmpdir("pro-tacts-dev") do |dir|
     data_dir = Pathname.new(dir)
     ENV["PRO_TACTS_DATA_DIR"] = data_dir.to_s
diff --git a/docs/plans/2026-09-15-identity-from-one-header.md b/docs/plans/2026-09-15-identity-from-one-header.md
index edb5e2a..572142d 100644
--- a/docs/plans/2026-09-15-identity-from-one-header.md
+++ b/docs/plans/2026-09-15-identity-from-one-header.md
@@ -51,13 +51,18 @@ next piece of work, not part of this one.
 `PRO_TACTS_IDENTITY_HEADER` sets it. The default is `Remote-User`, what
 a reverse proxy conventionally writes the authenticated user to; the
 `docs` site in the same Caddyfile already sends it to another backend.
-`rake dev` sets `Tailscale-User-Name`, because a dev session has no
-proxy in front of it and is testing what the tailnet deployment does.
-That name is a misnomer inherited from the Caddy site: its `header_up`
-writes `{http.auth.user.tailscale_user}` there, which is the login, not
-the display name the header is named for. The app reads a header, not a
-meaning, so the misnomer costs nothing but a reader's double take —
-correcting it is a Caddyfile change and a resync, not an app change.
+`rake dev` sets nothing and takes the default, because the deployment
+takes it too: the Caddy site writes `{http.auth.user.tailscale_login}`
+into `Remote-User`, so a dev session and the tailnet read the same
+header name.
+
+That placeholder is the username portion of the login — `alpha`, not
+`alpha@kejadlen.dev` — which is what a `sync:` group is named after. It
+reads the way the display name did without being a display name, and it
+is unique within one domain rather than across the tailnet: two users at
+different domains with the same username would share a book. The
+Caddyfile carries a TODO to move to the whole login once the group name
+is a setting of its own.
 
 The header is trustworthy for the same reason the pair was. `tailscale
 serve` strips `Tailscale-User-Login` from an incoming request before it