diigo-cache: webkey page logs in with the password manager

/cache/ without a fragment is now a login form: petname
(autocomplete=username) and cache id (type=password,
autocomplete=current-password), the shape password managers fill from
the entries the save button makes. Its inputs have no name, so a native
submit (script failed) sends nothing; the key stays in memory, out of
the address bar and history. Home links to it.

Generated-by: Claude Opus 5.5
This commit is contained in:
dckc committed 2026-10-07 04:48:15 -05:00
1 parent 961515d8db
commit efaf74023d
3 files changed
+37 -2

No files matched your search

+25 -2
View File
@@ -5,6 +5,11 @@
* it for a capability (`api.cache(key)`, Cap'n Web over /api, in-band),
* and drives the fill job. Each round trip is its own HTTP batch session:
* calls made before an await travel together.
*
* Without a fragment, the page is a login form a password manager fills
* (petname as username, cache id as password). Its inputs have no
* `name`, so a native submit (script failed) sends nothing; the key
* stays in memory, out of the address bar and history.
*/
export const CACHE_PAGE_HTML = `<!doctype html>
@@ -13,6 +18,11 @@ export const CACHE_PAGE_HTML = `<!doctype html>
<body>
<h1>diigo cache <code id="fingerprint"></code></h1>
<p id="problem" hidden></p>
<form id="login" hidden>
<p><label>petname <input id="login-petname" autocomplete="username" size="24"></label></p>
<p><label>cache id <input id="login-key" type="password" autocomplete="current-password" size="24" required></label></p>
<p><button type="submit">open</button></p>
</form>
<section id="private" hidden>
<h2>private cache</h2>
<p id="private-status">no fill job yet</p>
@@ -29,7 +39,7 @@ export const CACHE_PAGE_HTML = `<!doctype html>
<script type="module">
import { newHttpBatchRpcSession } from "/_capnweb.js";
const key = location.hash.slice(1);
let key = location.hash.slice(1);
const $ = (id) => document.getElementById(id);
const problem = (text) => {
$("problem").textContent = text;
@@ -71,8 +81,21 @@ function report(err) {
problem(String(err));
}
/** no fragment: ask for the key (the password manager fills it in) */
function login() {
const form = $("login");
form.hidden = false;
form.addEventListener("submit", (event) => {
event.preventDefault();
key = $("login-key").value.trim();
if (!key) return;
form.hidden = true;
main().catch(report);
});
}
async function main() {
if (!key) return problem("no key: this page's address should end in #<cache id>");
if (!key) return login();
const c = cap();
const [fingerprint, hasPublic] = await Promise.all([c.fingerprint(), c.hasPublic()]);
$("fingerprint").textContent = fingerprint;
+1
View File
@@ -582,6 +582,7 @@ nobody here opens it. Mint lands you on the wire step, id pre-filled.</li>
<p><button type="submit">wire</button></p>
</form>
<p>the ids this mints are for <b>you</b>: copy them somewhere you control (password manager)</p>
<p><a href="/cache/">open a cache</a> — fill it, watch progress (your password manager can fill in the cache id)</p>
${dumpForm}
${result}
</body></html>`,