App-password display: chunked + click-to-copy, never expire
closedProblem
App-password secrets are long, high-entropy, server-generated strings
shown once in a plain <code> block with no copy affordance
(account.templ secret banner), forcing desktop → phone hand-transcription
character by character.
Constraints (decided)
- Secrets are generated by Stalwart (
x:AppPassword/set, onlyttlis ours) and returned exactly once — length/entropy must NOT be weakened. - Passwords never expire: keep the
ttl = 0default; no expiry feature.
Proposal
- Add a Copy button to the one-time secret banner (reuse the generic
data-copyhelper instatic/app.js; the raw secret goes in the attribute). - Render the displayed secret chunked (e.g. 4-char groups) for manual transcription while copying the raw value; keep the “copy now, never shown again” warning.
autocomplete=offon the banner region.
Files
internal/appview/ui/account.templ(+ small chunk helper inview.goor templ),static/app.jsonly if the helper needs work- Tests:
ui_test.gorender coverage for the banner
Acceptance
- One click copies the exact secret; displayed form is chunked.
- Non-goal: entropy reduction, QR handoff (iPhone Mail can’t consume it), OAuth login (separate future).
Context
Flow being optimized: desktop browser → hand-type into phone mail app.
1 Comment
Progress: chunked display implemented (group size 5, dual copy)
Secret format confirmed:
app_aaaaaaqz9x2p1hacftrl91qtpftnlbev9j7ais 40 chars, so groups of 5 divide evenly and keep_mid-group (app_a) instead of at a group edge (app_).Changes (uncommitted, in working copy)
internal/appview/ui/hx.go: newchunkSecrethelper (5-char groups, display-only; raw secret untouched; entropy unchanged).internal/appview/ui/fragments.templ(+ regeneratedfragments_templ.go):appPasswordSecretnow renders chunks as adjacent<span>s in a flex-with-gap<code>— no whitespace text nodes, so drag-select pastes the exact secret. Styling:font-mono font-bold text-base sm:text-lg whitespace-nowrap,overflow-x-autowrapper (one line even on mobile). Raw secret indata-copyon both the<code>(click secret to copy) and an explicitCopybutton. Banner hasautocomplete="off" spellcheck="false". Warning copy + OK dismiss kept.internal/appview/ui/static/app.js:data-copyfeedback changed fromtextContentswap (which would flatten chunk spans) to a transientCopiedbadge inserted after the element.TestChunkSecretGroupsOfFive(hx_test.go),TestAppPasswordSecretChunkedCopiesRaw(fragments_test.go, incl. adjacency lock>app_a</span><span).Verification
go build ./...clean;gofmt/go vetclean.go test ./internal/appview/...— all pass (incl. both new tests, watched fail RED before GREEN).<span>app_a</span><span>aaaaa</span>…with zero bytes between spans.static/app.jshas no JS runtime in this env to syntax-check (node absent); change uses only standard DOM APIs already in the file. Recommend a browser click-through: click secret, click Copy, drag-select across groups, paste each into an editor and compare to raw.