theseus/PENDING.md
Theseus Dev f76ca4f9f4 Theseus 0.3.132: layout revert, Nostr per-site identities, Mail as sidebar quicklink, connector token stays persistent
Startpage layout reverted to the pre-0.3.131 shape. 0.3.131's full-page-
height sidebar was a mistake — slots in a short strip under a hero at
the top, cards and slots sharing one row of panels at the same height,
is what matches the design. .page/.main-col/.sidebar structure dropped,
hero back as a body sibling of .homepage, slots-list back to repeat(3,
72px) with minSlots=9. Two of the user's own tweaks baked into the
defaults: .section.chromeless on the left column so slot icons float
on the page background instead of sitting in a boxed panel, and
defaultState().iconSize = "large" (60 px tiles).

Game.X in the SM cards (replaces SilentMode.X in its slot, stub
description + a built-in gamepad brand mark until game.x goes live).
SilentMode.X moved to the quick-links strip as the 5th slot. moveItem
now strips the pinned-slot anchor when a q item moves to sm — a link
dragged onto the cards used to flip back on the next render because
its slot number was still set.

Nostr NIP-07 signer grows per-site identities (0.1.0 → 0.1.1). First
approval modal picks between the Hermes identity (one npub for DMs
and public notes, the one published as the np record on your BNS
name) and a site-specific HKDF-derived npub (unique per origin, so
Nostr clients can't cross-correlate your activity). The chooser shows
both npubs so the user sees what each button reveals. Grants without
a stored profileId (migrated from 0.3.131) default to the Hermes
identity so existing approvals keep behaving the same.
lib/hermes.js:nostrKeyFromRootForOrigin HKDFs with info
silentmode/nostr-origin/v1/<origin> over the existing 32-byte
messengerRoot. main.js _nostrAPI methods take {profileId, origin};
signer cache per profile.

Silent Mode Mail retired as an extension — now a Sidebar quicklink
(id: silentmail). Its 0.1.0 panel iframed mail.silentmode.st, which
sends X-Frame-Options: DENY; the browser rendered the block page and
STILL fired the iframe's load event, so the 5 s "did it embed?"
watchdog saw it as loaded and the panel stayed black. The whole
extension was just a landing page with an Open Mail button — exactly
what a sidebar quicklink does, without taking a panel slot or an
Extensions row. migrateAegisRename auto-retires any existing
extensions/mail/ to addons-backups/mail-migrated-<stamp>/ on first
boot of this Theseus; "mail" added to the first-party reserved-ids
list so a community add-on can't claim the id.

Claude connector 0.2.1 → 0.2.2: the bearer token no longer rotates on
every server stop. The old stop() generated a fresh token every time
Theseus quit, the panel toggled off, or a 0.3.X install replaced the
app — the Connect button wrote the new token to ~/.claude.json but
any already-running Claude Code or Claude Desktop process held the
previous token in memory and only re-reads config on launch, so /mcp
reconnect kept sending the stale bearer and the server rejected it
forever. Infinite loop: install Theseus upgrade, click Connect, MCP
still 401, close Claude, open Claude, install another upgrade a day
later, repeat. The explicit Revoke token button still rotates for
users who want fresh keys; everything else is persistent.

Sidebar panel apps blank to about:blank after 2 min of being closed to
free their render tree (iframes, media, listeners). Fast reopen within
the window still reuses the loaded page.

Light theme palette flip under body.light — the full set of
--ink / --mut / --card / --line tokens flip plus hero + card
translucent panels switch to light-glass, so hero titles, SM card
text, slot labels and the ".bch" tld read correctly on the light
background preset instead of as pale ghosts.
2026-10-30 03:45:32 +02:00

208 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Pending — uncommitted Theseus changes
Working-tree state that hasn't been committed or shipped. Read this before
touching the same files. Update when you land or add work.
Format: each group has a `session:` label so parallel sessions can see
what belongs to whom and coordinate. Naming convention:
`YYYY-MM-DD:short-slug` for date-bounded work, or `parallel:<theme>` for
work owned by a session that isn't the current one.
Last updated: 2026-10-29 (post-0.3.130 batch).
---
Releases are no longer recorded here: the live version and hashes are in
`../site/releases-manifest.json` and `https://dl.silentmode.st/releases-manifest.json`,
and each release commit is `Theseus 0.3.x: ...` in git history.
## Staged for the next release (post-0.3.131)
- **LANDED: Startpage layout reverted to the pre-0.3.131 default,
plus the user's two preferred tweaks baked into the defaults — the
slot column is chromeless (floats on the page background, no panel
box around it) and default `iconSize` is "large" (60 px tiles).**
Changes in [home.html](home.html):
- HTML: `<div class="hero">` and `<div class="homepage">` back as
body siblings (not nested in a `.page` grid with a sidebar aside).
`.homepage` is the 2-col grid `minmax(260, 300) 1fr`, hero padded
`5vh 1rem 0` as the top of the page again.
- CSS: `.sidebar` renamed back to `.left-column`, `.cards-wrap` back
to `.right-column`, the `.page` + `.main-col` grid dropped.
`.section.chromeless` kept (brought back) — the left column uses it
so slots float on the page background like the user had it; the SM
cards keep the soft panel surface.
- `defaultState().iconSize` is `"large"` (60 px slot tiles) —
matches the sizing in the user's screenshot; existing profiles
with their own `iconSize` keep that choice.
- `.slots-list { grid-template-columns: repeat(3, 72px); width:
max-content; margin: 0 auto }` — fixed-cell 232 px grid, centered
in its panel. `layoutSlots` back to `minSlots = 9` (3 × 3 rows,
matches the SM cards grid height). `minSlotsForFill()` and its
`ResizeObserver` are gone.
- `.hero .ver-line` CSS + its `body.light` override removed; the
`<p class="ver-line" id="verLine">` markup gone; the init-script
block that wired `getVersion()` → `navigate("silentmode.st/releases")`
gone. The user asked for "What's new" to be dropped again after I
restored it for 0.3.131; `window.home.getVersion` exposure in
[home-preload.js](home-preload.js) also removed since nothing calls
it any more.
- `moveItem()` now strips the `slot` property when a q item moves to
sm (link → card). Pin-to-slot is q-only, so leaving the anchor on
an sm card re-pinned it back to a slot cell on the next revert.
This fixes the user-reported "moving a link to card doesn't work"
(actually it moved, but the stale slot anchor flipped it back on
the next render).
- **Live user state reset.** Backed up `%APPDATA%\Theseus\home-cards.json`
to `.bak-before-reset`, rewrote it to the restored defaults — SM
cards list contains Game.X (replacing SilentMode.X in its slot),
quicklinks strip holds hello.bch / sia.coin / Pithos.sia /
Bergamot.X / SilentMode.X, plus any user-added extras preserved.
Preserved: `background` (indigo preset) and `iconSize` (large).
User must reload the open home tab (Ctrl+R or close/reopen) to pick
up the new file.
- **LANDED: Claude connector no longer rotates its bearer on every
stop (0.2.2).** The old `stop()` generated a fresh token on every
server stop, so a Theseus quit, a panel toggle-off, or even a 0.3.X
→ 0.3.X+1 install silently invalidated every registered Claude. The
Connect button wrote the new token to `~/.claude.json`, but any
already-running Claude Code or Claude Desktop process holds the
previous token in memory and only re-reads config on launch, so
`/mcp` reconnect kept sending the stale bearer and the server
rejected it. Observed symptom from the connector log: after
installing 0.3.131 and clicking Connect, `.claude.json` was
updated (correct new token written at 01:02:53) but the running
Claude Code session's MCP calls stayed 401 forever. Fix in
[bundled-addons/claude-connector/index.js](bundled-addons/claude-connector/index.js):
`stop()` keeps the stored token; only the panel's "Revoke token"
button rotates. Addon version bumped 0.2.1 → 0.2.2 so
`seedBundledAddons` picks up the fix on the first boot of 0.3.132.
- **LANDED: Nostr per-site identities (0.1.1).** The 0.3.131 Nostr add-on
always signed with Hermes's one `npub`, which gives sites a single
identity they can correlate across the Nostr network. 0.3.132 adds a
per-origin profile picked on first approval:
- `lib/hermes.js` gains `nostrKeyFromRootForOrigin(messengerRoot, origin)`
— HKDF with info `silentmode/nostr-origin/v1/<origin>` over the
existing 32-byte messengerRoot, so every site that chooses "site-
specific identity" sees a different `npub` and clients can't cross-
correlate. Deterministic from the mnemonic, no new vault field
needed.
- [main.js](main.js): `_nostrAPI` methods take `{ profileId, origin }`.
`profileId` is `"hermes"` or `"origin"`; the signer cache keys on
both. Per-origin secret keys are hot-cached under
`nostrOriginSkCache` so the async HKDF only runs once per
(vault, origin). Backward-compatible: callers that omit `profileId`
get the Hermes identity (what 0.3.131 did).
- [bundled-addons/nostr/index.js](bundled-addons/nostr/index.js): grant
schema gains `profileId`. First-time approval modal shows BOTH npubs
side-by-side and two Allow buttons ("Use site-specific identity" is
primary, "Use my Hermes identity" is the opt-in to the shared key).
Subsequent calls reuse the stored profile silently. Pre-upgrade
grants (no `profileId` field) default to `"hermes"` so existing
approvals keep behaving the same.
- [bundled-addons/nostr/panel.html](bundled-addons/nostr/panel.html):
each grant row shows which identity the site sees — "Shared Hermes
identity" or "Site-specific identity" with the derived npub under
it.
- Addon version bumped 0.1.0 → 0.1.1 so `seedBundledAddons` reseeds
the fresh-from-0.3.131 copy on the next Theseus start.
## Staged for the next release (post-0.3.130)
Queue of asks for the next cut. Items marked LANDED are edited in the
working tree waiting for a bump + build. Items marked TODO still need the
code change.
- **LANDED: Nostr NIP-07 signer** — new bundled add-on `bundled-addons/nostr/`
(0.1.0) plus host support. Puts `window.nostr` on https + localhost pages so
any Nostr client (Primal, Coracle, Snort, nostrudel) can sign with the
identity Theseus already holds. **Needs a Theseus release**: it changes
`main.js` and bundles a new add-on, so the extension OTA alone cannot
deliver it. **MISSED 0.3.130** — the folder is untracked (`??` in git
status), so the detached-HEAD build worktree never saw it. For 0.3.131
the ship script must either `git add` the folder first or `cp -r` it
into the build worktree the same way `build/`, `tor/`, `snapshots/`
are copied.
- [lib/nostr-signer.js](lib/nostr-signer.js) — new. Crypto half:
`getPublicKey`, `signEvent` (BIP340 via nostr-tools `finalizeEvent`),
nip04 + nip44 encrypt/decrypt. Recomputes `pubkey`/`id` rather than
trusting the page.
- [main.js](main.js) — `global._nostrAPI` next to `_hermesAPI`.
- Identity is Hermes's key (`nostrKeyFromRoot(messengerRoot)`); vault
with no mnemonic reports no identity instead of failing obscurely.
- Policy per-origin (read vs sign), kinds 0/3/5/10002 re-prompt every
time with no remember option.
- Tests: `scratchpad/signer-test.mjs`, `nostr-policy-test.mjs`,
`nostr-e2e.mjs`.
- **LANDED: Home — SilentMode.X moved to the quick-links strip, Game.X
takes its place in the Silent Mode cards.** [home.html](home.html)
`defaultState().sections.sm` and `.q`. Game.X entry uses a built-in
`game` brand mark (new SVG in `BRAND`); the real description and
favicon will come from the live site once game.x is up (currently a
stub). SilentMode.X moves into the slots strip as the 5th quick-link.
**Still TODO:** update the remote cache at
`https://dl.silentmode.st/home-cards.json` so existing profiles pick
the swap up without resetting (local `sections.sm` isn't reseeded by
`migrateCards` — only the remote fetch rewrites it for installed
users).
- **LANDED: Mail is now a Sidebar quicklink, not an extension.** The
user flagged two things in one: the Mail add-on's 0.1.0 panel was a
black screen (iframe + XFO: DENY), AND Mail conceptually belongs
under Settings → Sidebar (webapp quicklinks) rather than Settings →
Extensions. Both resolved by dropping the extension form entirely:
- `settings.quickLinks` default list in [main.js](main.js) gains
`{ id: "silentmail", url: "https://mail.silentmode.st/", title:
"Silent Mode Mail" }` as the first entry.
- `migrateAegisRename()` grew a mail retirement block: on first boot
of this Theseus, any installed `extensions/mail/` folder moves to
`addons-backups/mail-migrated-<stamp>/`. The one-off `mail/`
directory I added under `bundled-addons/` earlier in this session
is removed again (the quicklink + retire is the right path).
- `"mail"` added to the first-party reserved-ids list so a community
add-on can't claim the id after retirement.
- The quicklink does exactly what the old extension's "Open Mail"
button did (new tab at `mail.silentmode.st`), without taking a
panel slot or an Extensions row.
- **LANDED: Left-side sidebar panels free their render tree after idle.**
[main.js](main.js) `closeLeftPanel`: closed panels used to keep their
loaded page (`leftPanelLoadedId` kept, webContents warm) forever —
great for fast reopen, bad for RAM when the user docks a heavy
app-like panel (Hermes, Pithos, mail) once and never comes back.
Now a 2-minute idle timer blanks the panel to `about:blank` after
close, dropping iframes/media/listeners; reopen inside the window
still reuses the loaded page (`cancelLeftPanelIdleUnload` on open).
`LEFT_PANEL_IDLE_MS = 2 * 60 * 1000`. Does NOT touch the right-side
`sidebar` view (webapp tabs — Telegram, X, WhatsApp, AI chat) — those
are longer-lived by design; follow-up decision whether to apply the
same idle unload there.
- **LANDED: Startpage light theme palette flip under `body.light`.**
[home.html](home.html): the existing `@media (prefers-color-scheme:
light)` block only flipped tokens when the OS preferred light. Picking
the "light" background preset (`body.light`, set by `applyBg()`) left
`--ink` white against a white page, so hero title, SM card titles,
slot labels and the ".bch" extension read as pale ghosts. Added a
full `body.light { --ink; --mut; --card; --line; ... }` block plus
overrides for the hero/card translucent panels (dark-glass → light-
glass on light theme so the dark text inside reads), the ctx-menu
and modal (white surfaces, dark text). Dark theme untouched.
## session: `parallel:collision-policy`
Owner's focus: BCNR ↔ ICANN name collision handling. Not mine — leave for
that session to land or bundle by explicit ask.
- Android / mobile: [../AriadneResolver/mobile/app/AndroidManifest.xml](../AriadneResolver/mobile/app/AndroidManifest.xml),
[BchFetcher.java](../AriadneResolver/mobile/app/src/com/silentmode/ariadne/BchFetcher.java),
[Bns.java](../AriadneResolver/mobile/app/src/com/silentmode/ariadne/Bns.java),
[build.ps1](../AriadneResolver/mobile/build.ps1) — mobile-side collision changes.
- Repo-wide: `.gitignore` gained a `/.keys/` line (harmless).
## Live state
Shipped hashes live at `../site/releases-manifest.json` and
`https://dl.silentmode.st/releases-manifest.json`. On-chain pointer at
`releases.silentmode.bch`. See [GOTCHAS.md](GOTCHAS.md) for the release
gotchas.