Skip to content

feat(adapter-local): add openOrInitialize() for honest first-run UX - #165

Merged
cuibonobo merged 1 commit into
mainfrom
claude/issue-143-p3peit
Aug 14, 2026
Merged

feat(adapter-local): add openOrInitialize() for honest first-run UX#165
cuibonobo merged 1 commit into
mainfrom
claude/issue-143-p3peit

Conversation

@cuibonobo

Copy link
Copy Markdown
Member

Summary

The README quick start's happy path called generateDidKeypair() then LocalAdapter.initialize() — which throws on a second run (file exists), and as written mints a new identity every run while dropping privateKey on the floor. The real first-run choreography (if db exists, open; else generate keypair, persist it, initialize) was left to every adopter to write by hand.

  • LocalAdapter.openOrInitialize(opts) — branches on existsSync(path). initialize()/open() are unchanged, for callers who want the explicit distinction. entityId accepts either a plain DID string or a lazy () => string | Promise<string>, so keypair generation only runs on the actually-new path — the provider is never invoked when the db already exists. A plain-string entityId is asserted against the existing stack's owner on the open path (catches silent config divergence); a lazy provider is not checked there, since evaluating it just to compare would defeat the point of laziness.
  • Quick start rewrite (root README.md and packages/core/README.md) — uses openOrInitialize() and actually persists the generated keypair via exportDidPrivateKeyJwk().
  • Key-custody section — where to put the JWK on Node/server, desktop, and browser (including storing the CryptoKey itself in IndexedDB instead of exporting), and the asymmetry: losing the key never breaks anything local, but permanently forecloses authenticating as that identity to a server.

Closes #143

Spec

No docs/spec/ changes. openOrInitialize() is an additive LocalAdapter convenience static, like the existing initialize()/open() — it isn't part of the StackAdapter interface or any wire/permission contract.

Verification

pnpm run format:check && pnpm run lint && pnpm test && pnpm run build && pnpm run typecheck

All green. Added test coverage in packages/adapter-local/tests/local.test.ts for: initializing when the db is absent, opening (not re-initializing) when present, the lazy provider never being invoked on the open path, the lazy provider being invoked (sync or async) on the initialize path, the mismatch error when a plain-string entityId disagrees with the existing owner, and timezone only applying on the initialize path.

Notes for reviewers

The issue's open question — should entityId be lazy — is resolved here: it's string | (() => string | Promise<string>), with the lazy form intentionally exempt from the open-path match check (documented in the option's JSDoc rather than silently ignored).


Generated by Claude Code

LocalAdapter.openOrInitialize() does the existence branch (open if the
db is there, otherwise generate/initialize) that every adopter was
previously left to write by hand. entityId accepts a lazy
`() => string | Promise<string>` so keypair generation only runs on the
actually-new path, or a plain string, which is asserted to match the
existing owner on the open path to catch silent config divergence.

Rewrite both READMEs' quick starts to use it and actually persist the
generated keypair (via exportDidPrivateKeyJwk), and add a Key custody
section covering where to put the JWK on Node/desktop/browser and the
consequence of losing it.

Closes #143

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FSzQUk58M7KYnmuZXKxpsS
@cuibonobo
cuibonobo merged commit 14aaee4 into main Aug 14, 2026
5 checks passed
@cuibonobo
cuibonobo deleted the claude/issue-143-p3peit branch August 14, 2026 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

First-run UX: openOrInitialize(), an honest quick start, key-custody guidance

2 participants