docs(hephaestus): add "For AI sessions" section with the misdiagnoses
Someone hitting the deployed instance recently reported the wallet-auth
front-end as broken based on two mistakes:
(a) mistook the 510-byte login shell for a stub (it's an SPA bootstrap
that renders the UI via a JS module client-side), and
(b) tried /static/wallet.js instead of /auth/static/wallet.js, missing
the Caddy path-prefix routing.
Add an explicit section with:
- URL table showing exactly which service serves each path
- curl snippets for smoke-testing the flow without a browser
- Note that /api/v1/user requires an API token (not a session cookie),
so a 401 there does not mean "not signed in"
- Guidance for headless / AI-driven browser sessions
Aims to head off the same misdiagnosis pattern from future sessions.
This commit is contained in:
parent
2cdb0ab47a
commit
3462c00188
1 changed files with 61 additions and 0 deletions
|
|
@ -129,6 +129,67 @@
|
|||
<p class="note"><b>Why no email?</b> Nothing to leak, nothing to phish, no password-reset flow to hijack. The trade: if you lose your recovery phrase there is no support form to send you back in. Back the phrase up. Twice. On paper.</p>
|
||||
</section>
|
||||
|
||||
<section id="ai-sessions">
|
||||
<h2>For AI sessions & programmatic testing <span class="pill">read before debugging</span></h2>
|
||||
<p class="lede">If you're an AI assistant, CI job, or anyone debugging the sign-in without a real browser, read this first. It heads off two misdiagnoses we've already hit.</p>
|
||||
|
||||
<h3>Misdiagnosis #1: "The login shell page is only 510 bytes — it's a broken stub"</h3>
|
||||
<p>It's not a stub — it's a <b>deliberately small bootstrap page</b> that renders the wallet UI client-side via a JS module (standard SPA pattern). Size ≠ brokenness. The full UI (mnemonic generation, passphrase encryption, sign flow) is inside <code>wallet.js</code>, not the shell. Test by requesting <code>wallet.js</code> instead:</p>
|
||||
<pre>curl -sI https://code.silentmode.st/auth/static/wallet.js
|
||||
# expect: HTTP 200, ~10 KB, Content-Type: application/javascript</pre>
|
||||
|
||||
<h3>Misdiagnosis #2: "static/wallet.js returns 404 at every path"</h3>
|
||||
<p>It only 404s if you drop the <code>/auth/</code> prefix. Caddy path-multiplexes: <b><code>/auth/*</code> goes to the auth-proxy, everything else goes to Forgejo</b>. Forgejo has no <code>/static/wallet.js</code>, so that path is expected-404. The real paths are:</p>
|
||||
<table class="spec">
|
||||
<tr><th>URL</th><th>Served by</th><th>Purpose</th></tr>
|
||||
<tr><td>/auth/authorize</td><td>auth-proxy</td><td>Login shell HTML (SPA bootstrap)</td></tr>
|
||||
<tr><td>/auth/static/wallet.js</td><td>auth-proxy</td><td>Wallet UI + signing logic (~10 KB)</td></tr>
|
||||
<tr><td>/auth/static/login.css</td><td>auth-proxy</td><td>Styles for the login card</td></tr>
|
||||
<tr><td>/auth/challenge</td><td>auth-proxy</td><td>POST cashaddr → get nonce + signed message</td></tr>
|
||||
<tr><td>/auth/verify</td><td>auth-proxy</td><td>POST nonce + signature → get OAuth code</td></tr>
|
||||
<tr><td>/auth/.well-known/openid-configuration</td><td>auth-proxy</td><td>OIDC discovery</td></tr>
|
||||
<tr><td>/user/oauth2/hephaestus-wallet</td><td>Forgejo</td><td>Kicks off the OAuth handshake (307 → /auth/authorize)</td></tr>
|
||||
<tr><td>/user/oauth2/hephaestus-wallet/callback</td><td>Forgejo</td><td>Receives the OAuth code, mints session</td></tr>
|
||||
<tr><td>/api/v1/version</td><td>Forgejo</td><td>Public: returns Forgejo version</td></tr>
|
||||
<tr><td>/api/v1/user</td><td>Forgejo</td><td>Requires an API <b>token</b> (not a session cookie) — returning 401 in a browser is not a bug</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Testing the sign-in flow end-to-end without a real browser</h3>
|
||||
<p>The wallet signing is easiest to reproduce in Node/Deno or a headless browser — the crypto is BIP-137 "Bitcoin Signed Message" over BCH secp256k1. But <b>the server-side pieces alone can be smoke-tested with curl</b>:</p>
|
||||
<pre>ISSUER=https://code.silentmode.st/auth
|
||||
|
||||
# 1. discovery document lists all endpoints
|
||||
curl -s "$ISSUER/.well-known/openid-configuration" | jq .
|
||||
|
||||
# 2. request a challenge for any cashaddr (server just holds it 5 min)
|
||||
curl -s -X POST -H 'content-type: application/json' \
|
||||
-d '{"cashaddr":"bitcoincash:qq00000000000000000000000000000000000000q"}' \
|
||||
"$ISSUER/challenge"
|
||||
# → returns {"nonce": "...", "message": "code.silentmode.st wants you to sign in..."}
|
||||
|
||||
# 3. any BCH wallet that speaks "Sign Message" can produce a valid signature
|
||||
# for that exact message. Then POST it:
|
||||
curl -s -X POST -H 'content-type: application/json' \
|
||||
-d '{"nonce":"THE_NONCE","signature":"BASE64_SIG"}' \
|
||||
"$ISSUER/verify"
|
||||
# → returns {"redirect": "https://code.silentmode.st/user/oauth2/hephaestus-wallet/callback?code=..."}</pre>
|
||||
<p>For the JS side (which is what the browser does), inspect <code>auth-proxy/public/wallet.js</code> in the repo — it's ~250 lines and does exactly this: <code>generateBip39Mnemonic → deriveHdPath("m/44'/145'/0'/0/0") → sign(magicHash(message))</code>. Nothing exotic.</p>
|
||||
|
||||
<h3>Common browser-side checks</h3>
|
||||
<p>If you're in a browser (or a browser-driving tool) and want to know if you're actually signed in:</p>
|
||||
<pre>// The URL bar isn't reliable when Forgejo shows Security or Settings pages —
|
||||
// they render for logged-in users but don't have "Dashboard" in the title.
|
||||
// Best check: does the profile menu render, and is there no "Sign in" link?
|
||||
|
||||
const signedIn = !document.querySelector('a[href*="/user/login"]')
|
||||
&& !!document.querySelector('[aria-label*="Profile"], .user-menu, .avatar');
|
||||
|
||||
// The username, if you're on the Dashboard:
|
||||
document.title.match(/^(bch_\w+)/)?.[1];</pre>
|
||||
|
||||
<p class="note warn"><b>Do not test with <code>fetch('/api/v1/user')</code> unless you have an API token.</b> That endpoint requires token auth even when you're signed into the web UI. Session cookies get you into Forgejo pages; they don't get you into the token-scoped API. A 401 there means "no token" — it does not mean "not signed in."</p>
|
||||
</section>
|
||||
|
||||
<section id="git">
|
||||
<h2>Push some code</h2>
|
||||
<p class="lede">Two paths, either works. Pick whichever fits your setup.</p>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue