The recorded rejection of a shared ecosystem client ID was written
against a credential-RELAY hub (one that hands the unlock secret back to
the calling site) and stated too broadly. A hub that stays the SIGNER --
exposing only NIP-07-shaped operations over an origin/nonce-bound
postMessage channel, never releasing the credential -- is not refuted by
that objection. Recorded, with the observation that such a hub is
architecturally a NIP-46 bunker, which BitLogin already supports: the
open question is whether anyone runs a Google-gated one, not whether to
add a protocol. Residual cost restated plainly: the hub's served
JavaScript becomes the security of every user of every integrating site.
Also corrects drive.appdata's classification -- non-sensitive per
Google's current Drive scope documentation, not sensitive as written.
Co-Authored-By: Claude Fable 5 <[email protected]>
Owner decision: static only, and no per-site provider registration --
which every OAuth path requires (and GitHub/Facebook additionally require
a server). Both on-ramp implementations (service handshake, serverless
Google Drive) are removed the day they shipped; centralized-onramps.md
stays on the record as the analysis of why, superseded by
passkey-login.md.
The passkey rail: a WebAuthn PRF-capable passkey (synced for most users
by Google Password Manager or iCloud Keychain) deterministically derives
the login name and password of a standard capsule account -- passkey.ts
holds the derivation behind a FROZEN v1 known-answer contract with the
same standing rule as knownAnswer.test.ts. The ordinary password flows
do everything else; nothing is stored outside the user's authenticator,
and the rail needs no network of its own.
Flow is idempotent by construction: sign-in against a passkey with no
account routes into setup with the derived credential
(AccountNotFoundError); setup against an existing account signs in
(AccountAlreadyExistsError). Fresh identity or §SF10 nsec import at
setup; Tier B2 phrase card retained ('your account stays yours even if
you lose your passkey'); rotation is the graduation step and says so.
Pins updated: passkey row with honest custody label inside the menu,
OAuth actions provably absent, element still network-free. 11 passkey
tests incl. the frozen vectors; menu and screens verified in Chrome.
Co-Authored-By: Claude Fable 5 <[email protected]>
Per-client-ID app-data silo makes the rail per-site; cross-site
portability is the capsule account's job. A host's dead Google project
locks out ungraduated users in practice while every account survives on
relays -- the phrase card is load-bearing. Shared ecosystem client ID
behind a bitlogin.network relay hub considered and rejected: a chokepoint
plus federated credential readability is worse than the silo. Note the
sensitive-scope verification review hosts should expect.
Co-Authored-By: Claude Fable 5 <[email protected]>
Implements centralized-onramps.md §CO3.3, per the owner call that static
only is the way. No server anywhere: the host registers a Google OAuth
client ID (onramp-google-client-id) and the widget does the rest -- a
popup runs the plain implicit grant (no external script, so
script-src 'self' survives), scoped to drive.appdata only, redirecting
back to the widget page itself, which relays the token fragment to its
opener under the same origin+nonce discipline as the service handshake.
Returning users: the credential file is read from the user's app-data
folder and fed into the ordinary password login. First-timers: fresh
identity or §SF10 import, client-side registration, credential uploaded
to their own Drive -- with the no-orphan rule forcing the phrase
ceremony if the upload fails. Rotating the password later IS the
graduation step; the rotate screen says so for managed sessions, and
the dashboard states the custody plainly: your sign-in key is stored
in your own Google Drive.
driveOnramp.ts is pure and unit-tested (13 tests); new pins keep all
network I/O out of the element and constrain Drive fetches to the two
Google API constants. Hosts add https://www.googleapis.com to
connect-src and register the widget page URL as the redirect URI --
documented in the guide, llms.txt, and the design doc (v0.3), including
the recorded dependency on the legacy implicit grant.
Co-Authored-By: Claude Fable 5 <[email protected]>
Implements centralized-onramps.md §CO5. A host opts in via onramp-url /
onramp-name / onramp-providers attributes -- nothing centralized ships
by default. The options menu gains the owner-chosen group heading 'Use
an account you already have' covering extension, remote signer, key
import, and 'Continue with <Provider>' rows whose sub-label IS the
custody statement (§CO7): '<Service> manages your sign-in'.
The handshake (onramp.ts, pure and unit-tested): popup to the
configured URL with a CSPRNG state nonce; exactly one unlock or
register message accepted, only from the configured origin with the
exact nonce. Returning users flow into the ordinary password login.
New users choose a fresh identity or §SF10-import an existing nsec;
registration runs client-side and the password is POSTed to the
service -- if the store fails, the phrase ceremony is forced
immediately rather than deferred, so no account exists that nobody
can unlock. Tier B2 otherwise: the dashboard carries a 'Secure your
account' card into the existing phrase screens while the phrase is
still in session memory, plus a standing '<Service> manages your
sign-in' line.
Policy pins: on-ramp rows render only inside the menu with the custody
label; group heading wording pinned. 12 new tests; verified visually
with a configured element.
Co-Authored-By: Claude Fable 5 <[email protected]>
Proposes 'Sign in with Google/GitHub/…' as a clearly labeled fourth
method family behind the same login surface. Two architectures: an
OAuth-gated NIP-46 bunker (works today, full custody, the proof point)
and the recommended one -- OAuth as the password manager for a REAL
capsule account, where the service holds only the unlock secret, the
identity lives on relays, and service compromise maps to the password-
compromise analysis the spec already has (§14).
Phrase handling is a labeled custody dial (shown at signup / first-
session-only with a 'secure your account' card / escrowed). Graduation
keeps the same npub at every step: save the npub, claim the phrase,
hold your own password, full export -- with §SF10 import as the
already-implemented escape from any custodial tier. Honesty contract:
every centralized option answers who can access the account, what
happens if they vanish, and how to leave. Conforming services must
export and delete on demand and publish their tier machine-readably.
Owner framing on the record: meet users at whatever sign-in they trust,
nudge toward decentralization over time -- 'it's a journey for all of us.'
Co-Authored-By: Claude Fable 5 <[email protected]>