Skip to content

Commit 56edc88

Browse files
uppertoeclaude
andcommitted
features: retire the /features/app twin; the edge delivers identity at /features
/features/app existed because the edge's public block stripped identity from /features and the only remedy to hand was the landing page's cookie-redirect to a second address. The edge now passes session-cookie holders through forward_auth at /features itself (markbot-server 37c83a4), so the twin forwards home permanently and the app header and schedule links point at /features. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0121j6qgGwUSLGQ48jWZN98F
1 parent d9158f5 commit 56edc88

91 files changed

Lines changed: 128 additions & 109 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

internal/server/contact_submit_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,16 @@ func TestContactFormOffWithoutSwitch(t *testing.T) {
132132
t.Fatal("CONTACT_TO alone should keep the form on")
133133
}
134134
}
135+
136+
// TestFeaturesAppForwardsHome: the retired signed-in twin address forwards
137+
// permanently to /features, where the edge now delivers identity directly.
138+
func TestFeaturesAppForwardsHome(t *testing.T) {
139+
s := newAuthzServer(t)
140+
r := httptest.NewRequest("GET", "/features/app", nil)
141+
r.Host = "app.example.com"
142+
w := httptest.NewRecorder()
143+
s.Handler().ServeHTTP(w, r)
144+
if w.Code != 301 || w.Header().Get("Location") != "/features" {
145+
t.Fatalf("/features/app = %d -> %q, want 301 -> /features", w.Code, w.Header().Get("Location"))
146+
}
147+
}

internal/server/frontend_security_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,8 @@ func TestNoInlineScriptInsideBoostedBody(t *testing.T) {
276276
// unstyled and inert until a manual refresh. The nav already opts out; the hazard is
277277
// that every future content link has to remember, so this enforces it centrally.
278278
func TestLinksIntoAssetPagesAreNotBoosted(t *testing.T) {
279-
// Matches an <a> whose href is exactly "/", "/features" or "/features/app" and that lacks hx-boost.
280-
link := regexp.MustCompile(`<a href="(/|/features|/features/app)"(?:\s[^>]*)?>`)
279+
// Matches an <a> whose href is exactly "/" or "/features" and that lacks hx-boost.
280+
link := regexp.MustCompile(`<a href="(/|/features)"(?:\s[^>]*)?>`)
281281
err := fs.WalkDir(templateFS, ".", func(p string, d fs.DirEntry, err error) error {
282282
if err != nil || d.IsDir() || !strings.HasSuffix(p, ".html") {
283283
return err

internal/server/public.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,8 @@ func (s *Server) features(w http.ResponseWriter, r *http.Request) {
7373
func (s *Server) howRedirect(w http.ResponseWriter, r *http.Request) {
7474
http.Redirect(w, r, "/features", http.StatusMovedPermanently)
7575
}
76+
77+
// featuresAppRedirect retires the signed-in twin address (see the route).
78+
func (s *Server) featuresAppRedirect(w http.ResponseWriter, r *http.Request) {
79+
http.Redirect(w, r, "/features", http.StatusMovedPermanently)
80+
}

internal/server/server.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -420,11 +420,12 @@ func (s *Server) Handler() http.Handler {
420420
s.handle(mux, "/", guardPublic, s.notFound)
421421
s.handle(mux, "GET /security", guardPublic, s.security) // public
422422
s.handle(mux, "GET /features", guardPublic, s.features) // public
423-
// The signed-in twin of /features. The edge's public block strips identity
424-
// on purpose, so /features itself can never know who you are; Caddy
425-
// redirects session-cookie holders here, where the protected catch-all
426-
// delivers full identity and the same handler renders the app chrome.
427-
s.handle(mux, "GET /features/app", guardPublic, s.features)
423+
// /features/app was the signed-in twin of /features while the edge's public
424+
// block stripped identity from /features itself (5–10 Sep 2026). The edge
425+
// now passes session-cookie holders through forward_auth at /features, so
426+
// the twin forwards home; it stays only for the app header and schedule
427+
// links people have in their history.
428+
s.handle(mux, "GET /features/app", guardPublic, s.featuresAppRedirect)
428429
s.handle(mux, "GET /how", guardPublic, s.howRedirect) // public; the page's old address
429430
s.handle(mux, "GET /faq", guardPublic, s.faq) // public
430431
s.handle(mux, "GET /guide/{slug}", guardPublic, s.guide) // public question pages

internal/server/templates/nav.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
the menu — the visitor features go undiscovered, and a control nobody
3535
sees teaches nothing. Full navigation: /how's demo assets are head-only
3636
(see publicheader), so a boosted arrival would render a dead gallery. */}}
37-
<a href="/features/app" hx-boost="false" class="howlink{{if eq .State "how"}} active{{end}}" {{if eq .State "how"}}aria-current="page" {{end}}aria-label="Features">{{template "ic-help"}}<span class="howlink-txt">Features</span></a>
37+
<a href="/features" hx-boost="false" class="howlink{{if eq .State "how"}} active{{end}}" {{if eq .State "how"}}aria-current="page" {{end}}aria-label="Features">{{template "ic-help"}}<span class="howlink-txt">Features</span></a>
3838
<button class="icon-btn" type="button" data-theme-toggle title="Toggle light / dark" aria-label="Toggle theme">
3939
<span class="th-moon">{{template "ic-moon"}}</span><span class="th-sun">{{template "ic-sun"}}</span>
4040
</button>

internal/server/templates/schedule.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ <h2 class="exp-h2">{{template "ic-clock"}} Permits no longer active</h2>
112112
demo assets are head-only (see publicheader). */}}
113113
<p class="share-hint" style="--hue:var(--sec-guests)" x-data="{hide: localStorage.getItem('pstonn-how-hint')==='1'}" x-show="!hide" x-cloak>
114114
{{template "ic-qr"}}
115-
<span>There&rsquo;s more than the roster: visitor QRs, a printed QR that asks first, guest passes. <a href="/features/app" hx-boost="false">See how they work</a>.</span>
115+
<span>There&rsquo;s more than the roster: visitor QRs, a printed QR that asks first, guest passes. <a href="/features" hx-boost="false">See how they work</a>.</span>
116116
<button type="button" class="ghost icon sm" aria-label="Dismiss" title="Dismiss"
117117
@click="hide=true; localStorage.setItem('pstonn-how-hint','1')">{{template "ic-x"}}</button>
118118
</p>

internal/server/testdata/golden/pages/activity-booking-fab.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@
293293
<a href="/" hx-boost="false" class="brand brand-link"><span class="mark"><svg viewBox="0 0 24 24"><path d="M8.5 18.5V5.5h4.6a4 4 0 0 1 0 8H8.5" stroke-width="2.2"/></svg></span> <span class="brand-text"><span class="word">p<span class="dot">.</span>stonn</span> <span class="brand-tag">Visitor Permit Scheduler</span></span></a>
294294
<div class="appbar-r">
295295

296-
<a href="/features/app" hx-boost="false" class="howlink" aria-label="Features"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.2a2.5 2.5 0 0 1 4.9.7c0 1.6-2.4 2-2.4 3.4"/><path d="M12 17h.01"/></svg><span class="howlink-txt">Features</span></a>
296+
<a href="/features" hx-boost="false" class="howlink" aria-label="Features"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.2a2.5 2.5 0 0 1 4.9.7c0 1.6-2.4 2-2.4 3.4"/><path d="M12 17h.01"/></svg><span class="howlink-txt">Features</span></a>
297297
<button class="icon-btn" type="button" data-theme-toggle title="Toggle light / dark" aria-label="Toggle theme">
298298
<span class="th-moon"><svg viewBox="0 0 24 24"><path d="M21 12.8A8 8 0 1 1 11.2 3a6 6 0 0 0 9.8 9.8z"/></svg></span><span class="th-sun"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2M5 5l1.5 1.5M17.5 17.5L19 19M19 5l-1.5 1.5M6.5 17.5L5 19"/></svg></span>
299299
</button>

internal/server/testdata/golden/pages/activity-removal.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@
293293
<a href="/" hx-boost="false" class="brand brand-link"><span class="mark"><svg viewBox="0 0 24 24"><path d="M8.5 18.5V5.5h4.6a4 4 0 0 1 0 8H8.5" stroke-width="2.2"/></svg></span> <span class="brand-text"><span class="word">p<span class="dot">.</span>stonn</span> <span class="brand-tag">Visitor Permit Scheduler</span></span></a>
294294
<div class="appbar-r">
295295

296-
<a href="/features/app" hx-boost="false" class="howlink" aria-label="Features"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.2a2.5 2.5 0 0 1 4.9.7c0 1.6-2.4 2-2.4 3.4"/><path d="M12 17h.01"/></svg><span class="howlink-txt">Features</span></a>
296+
<a href="/features" hx-boost="false" class="howlink" aria-label="Features"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.2a2.5 2.5 0 0 1 4.9.7c0 1.6-2.4 2-2.4 3.4"/><path d="M12 17h.01"/></svg><span class="howlink-txt">Features</span></a>
297297
<button class="icon-btn" type="button" data-theme-toggle title="Toggle light / dark" aria-label="Toggle theme">
298298
<span class="th-moon"><svg viewBox="0 0 24 24"><path d="M21 12.8A8 8 0 1 1 11.2 3a6 6 0 0 0 9.8 9.8z"/></svg></span><span class="th-sun"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2M5 5l1.5 1.5M17.5 17.5L19 19M19 5l-1.5 1.5M6.5 17.5L5 19"/></svg></span>
299299
</button>

internal/server/testdata/golden/pages/activity-showing-all.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@
293293
<a href="/" hx-boost="false" class="brand brand-link"><span class="mark"><svg viewBox="0 0 24 24"><path d="M8.5 18.5V5.5h4.6a4 4 0 0 1 0 8H8.5" stroke-width="2.2"/></svg></span> <span class="brand-text"><span class="word">p<span class="dot">.</span>stonn</span> <span class="brand-tag">Visitor Permit Scheduler</span></span></a>
294294
<div class="appbar-r">
295295

296-
<a href="/features/app" hx-boost="false" class="howlink" aria-label="Features"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.2a2.5 2.5 0 0 1 4.9.7c0 1.6-2.4 2-2.4 3.4"/><path d="M12 17h.01"/></svg><span class="howlink-txt">Features</span></a>
296+
<a href="/features" hx-boost="false" class="howlink" aria-label="Features"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.2a2.5 2.5 0 0 1 4.9.7c0 1.6-2.4 2-2.4 3.4"/><path d="M12 17h.01"/></svg><span class="howlink-txt">Features</span></a>
297297
<button class="icon-btn" type="button" data-theme-toggle title="Toggle light / dark" aria-label="Toggle theme">
298298
<span class="th-moon"><svg viewBox="0 0 24 24"><path d="M21 12.8A8 8 0 1 1 11.2 3a6 6 0 0 0 9.8 9.8z"/></svg></span><span class="th-sun"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2M5 5l1.5 1.5M17.5 17.5L19 19M19 5l-1.5 1.5M6.5 17.5L5 19"/></svg></span>
299299
</button>

internal/server/testdata/golden/pages/activity-source-label.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@
293293
<a href="/" hx-boost="false" class="brand brand-link"><span class="mark"><svg viewBox="0 0 24 24"><path d="M8.5 18.5V5.5h4.6a4 4 0 0 1 0 8H8.5" stroke-width="2.2"/></svg></span> <span class="brand-text"><span class="word">p<span class="dot">.</span>stonn</span> <span class="brand-tag">Visitor Permit Scheduler</span></span></a>
294294
<div class="appbar-r">
295295

296-
<a href="/features/app" hx-boost="false" class="howlink" aria-label="Features"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.2a2.5 2.5 0 0 1 4.9.7c0 1.6-2.4 2-2.4 3.4"/><path d="M12 17h.01"/></svg><span class="howlink-txt">Features</span></a>
296+
<a href="/features" hx-boost="false" class="howlink" aria-label="Features"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.2a2.5 2.5 0 0 1 4.9.7c0 1.6-2.4 2-2.4 3.4"/><path d="M12 17h.01"/></svg><span class="howlink-txt">Features</span></a>
297297
<button class="icon-btn" type="button" data-theme-toggle title="Toggle light / dark" aria-label="Toggle theme">
298298
<span class="th-moon"><svg viewBox="0 0 24 24"><path d="M21 12.8A8 8 0 1 1 11.2 3a6 6 0 0 0 9.8 9.8z"/></svg></span><span class="th-sun"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2M5 5l1.5 1.5M17.5 17.5L19 19M19 5l-1.5 1.5M6.5 17.5L5 19"/></svg></span>
299299
</button>

0 commit comments

Comments
 (0)