feat(core): identity model — entityId becomes a DID, did:key floor (#49)#104
Merged
Conversation
Everywhere entityId means "who" now carries a DID string. Adds did.ts (Ed25519 did:key generation, parsing, signing, verification via Web Crypto only, no dependency) and a new EntityId type alias distinct from RecordId. EntityContent gains a required `did` field — _entity records are now explicitly local profile cards (petnames) about a DID, not the identity itself. Stack.create() gains an ownerProfile option that idempotently creates the owner's own _entity record, closing the gap where nothing previously did. Updates README/spec quickstarts to the generateDidKeypair() + ownerProfile flow, and documents the DID model, challenge-response auth sketch, and deferred key rotation in docs/spec.md's new Identity section. Scoped to what's fully implementable in @haverstack/core: group keypairs and the live server-side auth endpoint are left to follow-up work, per the RFC's own migration notes.
The merge of main into this branch (bringing in #69's timezone/entityId changes) left ConfigContent and StackRecordAdapter with duplicate entityId/timezone members instead of actually reconciling the two sides, and reverted the spec.md quickstart's entityId back to a plain string after generating a DID for it. Neither was caught by the compiler (duplicate members in a type literal aren't an error) — found by manual review and fixed by hand; full build/typecheck/lint/test suite verified clean across all packages afterward.
CI's format:check caught formatting drift introduced across this branch's edits (mostly markdown table/line-width reflow in spec.md). No content changes — pnpm run format applied.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the
@haverstack/core-scoped portion of #49:entityIdbecomes a DID string everywhere identity is meant,did:key(Ed25519) is the mandatory floor method, and_entityrecords become explicit local profile cards (petnames) about a DID rather than the identity itself.packages/core/src/did.ts—did:keygeneration, parsing/validation, sign/verify, and private-key JWK export/import, built entirely on Web Crypto (crypto.subtle) with zero new dependencies, matching core's existing zero-dependency design.EntityIdtype alias (= string, purely additive) applied acrossPermission,GrantContent,ConfigContent,StackRecordAdapter.ownerEntityId,StackTokenStore,access.ts, and the publicStack/ScopedStackAPI — fixes a real type conflation the RFC calls out: an entity was previously typed asRecordId, but it's never actually a record within this stack.EntityContentgains a requireddidfield._entity@1's runtime schema updated in place — no version bump, since there's no install base yet.Stack.create(adapter, { ownerProfile })idempotently creates the owner's own_entityprofile record on first init — previously nothing did this at all, despite the spec assuming it existed.docs/spec.md: new "Identity" section (DID model,did:keyfloor, petname pattern, challenge–response auth sketch, the verified-vs-anonymous 401/403 distinction from the issue's own comment thread, deferred key rotation). Both READMEs' quickstarts updated to thegenerateDidKeypair()+ownerProfileflow; also fixed a pre-existing stale@haverstack/adapter-sqlite/SQLiteAdapterreference inpackages/core/README.md.Scope
Deliberately limited to what's fully implementable and testable within
@haverstack/core, per the RFC's own migration notes:haverstack/serveris a separate repo — this PR only documents the wire-level sketch).Test plan
pnpm -r run build— all 9 workspace packages build cleanpnpm -r run typecheck— no errorspnpm -r run test— 828 tests pass across all packages, no regressionspnpm run lint(core) — cleandid.ts(keypair generation, sign/verify round-trip, tampered-data/wrong-key rejection, malformed-input handling, a known W3C did:key test vector, JWK round-trip)Stack.create({ ownerProfile })(creation, idempotency across reopen, no-overwrite-on-reopen, handle-omitted case, unauthored-record convention)Closes #49 (core-scoped portion).
Generated by Claude Code