routes: read the login from Remote-User
Assisted-by: Claude Opus 5.5 via Claude Code
change slwluuyxvmqsxvwomtmoqovmszwqtnzm
commit 23fcf1d03e944e0cf97a6dc03e1d768a69899f87
author Alpha Chen <alpha@kejadlen.dev>
date
parent vlxvzsmu
diff --git a/docs/DESIGN.md b/docs/DESIGN.md
index d3d555c..93d3d65 100644
--- a/docs/DESIGN.md
+++ b/docs/DESIGN.md
@@ -20,7 +20,7 @@ A small self-hosted webapp for two people to jointly manage household email acco
 
 - Two app users.
 - Access is via Tailscale, which is the whole of authentication: anyone the tailnet lets reach Docket is a user, added on their first request. Docket keeps no list of people.
-- The app binds to localhost behind Caddy with [caddy-tailscale](https://github.com/tailscale/caddy-tailscale), which sets two headers from the tailnet identity: `Tailscale-User-Login` (the full login, `{http.auth.user.id}`) identifies the user, and `X-User-Slug` carries a short name the Caddyfile maps from the login. Requests without a login are rejected.
+- The app binds to localhost behind Caddy with [caddy-tailscale](https://github.com/tailscale/caddy-tailscale), which sets two headers from the tailnet identity: `Remote-User` (the full login, `{http.auth.user.id}`) identifies the user, and `X-User-Slug` carries a short name the Caddyfile maps from the login. Requests without a login are rejected.
 - A user's slug is what Docket shows for them. It follows the latest `X-User-Slug`, falls back to the part of the login before the `@`, and need not be unique.
 
 ## Credentials and accounts
diff --git a/migrations/0001_init.sql b/migrations/0001_init.sql
index ac7c1e5..047a329 100644
--- a/migrations/0001_init.sql
+++ b/migrations/0001_init.sql
@@ -4,7 +4,7 @@
 
 -- Everyone Tailscale lets in, added on their first request.
 CREATE TABLE users (
-    -- The Tailscale-User-Login value that identifies this user.
+    -- The Remote-User value that identifies this user.
     login TEXT PRIMARY KEY,
     -- The short name shown for them, from X-User-Slug; not unique.
     slug TEXT NOT NULL
diff --git a/src/model.rs b/src/model.rs
index 603aeaf..3c6e93a 100644
--- a/src/model.rs
+++ b/src/model.rs
@@ -67,7 +67,7 @@ impl fmt::Display for State {
 /// anyone who reaches Docket is a user.
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct User {
-    /// The `Tailscale-User-Login` value that identifies this user.
+    /// The `Remote-User` value that identifies this user.
     pub login: String,
     /// The short name shown for them. Not unique: two logins can share one.
     pub slug: String,
diff --git a/src/routes.rs b/src/routes.rs
index aa41338..e10ddc6 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -19,7 +19,7 @@ use crate::views::{self, Page, ThreadView};
 
 // caddy-tailscale sets both from the tailnet identity, overwriting whatever
 // the client sent.
-const IDENTITY_HEADER: &str = "Tailscale-User-Login";
+const IDENTITY_HEADER: &str = "Remote-User";
 const SLUG_HEADER: &str = "X-User-Slug";
 const DEV_COOKIE: &str = "docket_dev_user";
 
diff --git a/tests/api.rs b/tests/api.rs
index 14d5fd1..603ed07 100644
--- a/tests/api.rs
+++ b/tests/api.rs
@@ -31,7 +31,7 @@ fn client() -> Client {
 async fn get(addr: SocketAddr, login: &str, path: &str) -> (StatusCode, String) {
     let res = client()
         .get(format!("http://{addr}{path}"))
-        .header("Tailscale-User-Login", login)
+        .header("Remote-User", login)
         .send()
         .await
         .unwrap();
@@ -46,7 +46,7 @@ async fn post(
 ) -> reqwest::Response {
     client()
         .post(format!("http://{addr}{path}"))
-        .header("Tailscale-User-Login", login)
+        .header("Remote-User", login)
         .form(form)
         .send()
         .await
@@ -84,7 +84,7 @@ async fn anyone_tailscale_lets_in_is_a_user() {
     // The proxy names them with X-User-Slug, and the name follows it.
     let body = client()
         .get(format!("http://{addr}/?m=4"))
-        .header("Tailscale-User-Login", "eve@example.com")
+        .header("Remote-User", "eve@example.com")
         .header("X-User-Slug", "Evie")
         .send()
         .await
@@ -320,7 +320,7 @@ async fn cross_site_posts_are_refused() {
     let send = |origin: &'static str, path: &'static str| {
         client()
             .post(format!("http://{addr}{path}"))
-            .header("Tailscale-User-Login", SAM)
+            .header("Remote-User", SAM)
             .header("Origin", origin)
             .form(&[("state", "do"), ("user", ALEX), ("back", "/")])
             .send()