routes: read the login from Remote-User
Assisted-by: Claude Opus 5.5 via Claude Code
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()