chore(originals): upgrade to 3.0.0-next.0 - #223
Merged
Conversation
@originals/sdk and @originals/auth 3.0.0-next.0, which pulls in the newly
split-out @originals/cel 0.2.0-next.0. Pinned exact rather than caret: npm
writes ^3.0.0-next.0 for a prerelease, which would also match 3.0.0 final and
every later 3.x, and the repo pins these exactly already.
Nothing in our code had to change. `tsc -b --force`, `eslint`, the full bun
suite (151 tests) and `npm run build` are all clean on the new version, and the
import surface is small — src/lib/originals.ts, src/lib/celKeyStore.ts and
convex/migrations/celAssetDids.ts. None of the breaking changes land on it:
- `createAsset` now throws NO_CUSTODY without custody; config.keyStore is
already set (localCelKeyStore), so it has some. 3.0 also fixes
OriginalsSDK.create destructuring keyStore away before it reached the
LifecycleManager, so our custody is now actually honoured downstream.
- Buffer -> Uint8Array on the public surface: no Buffer in our SDK paths.
- Deep imports like @originals/sdk/crypto/* no longer resolve: we had none.
- @originals/auth's root no longer re-exports ./server: we never imported it.
Adds a back-compat regression test, which is the part that could actually have
bitten. 3.0 renames the CEL cryptosuite to originals-cel-ed25519-jcs-v1 and
starts signing the proof configuration; logs sealed before it cannot be
re-signed, so eddsa-jcs-2022 stays accepted on read. Every list envelope already
in the database depends on that promise holding.
The fixture is a genuine envelope minted by SDK 2.1.0 in a throwaway install,
not a hand-written one, and it verifies clean under 3.0 with no warnings. The
test asserts the fixture still carries the pre-3.0 suite, so it cannot quietly
decay into proving nothing. If a later bump drops the legacy suite this fails
here rather than in production, where the symptom would be every existing list
going unverifiable at once.
Note our did:webvh mint path is unaffected by 3.0's normalizeUpdateKey fix for
the didwebvh-ts 2.8 bare-multibase requirement: src/lib/webvh.ts calls
didwebvh-ts directly rather than through the SDK's WebVHManager, so #218's fix
is still what is carrying us there.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SDK 3.0 throws NO_CUSTODY when createAsset is called without a signer, a
keyStore, or an explicit `{ controller: 'ephemeral' }`. celAssetDids.ts mints
with none of them — deliberately, so the server never holds a list's controller
key — so under 3.0 every call throws and the migration cannot run at all.
`ephemeral` is the explicit spelling of exactly what it already did. The header
has always said migrated lists are verifiable but not authorable; 3.0 simply
gives that intent a name and refuses to infer it from an omission.
I claimed in the upgrade commit that NO_CUSTODY missed us because config.keyStore
is set. That holds for src/lib/originals.ts and not for this file, which is the
one place we mint deliberately without custody.
Nothing caught it. Typecheck cannot see a runtime throw, and cel-migration.test
covered only celAssetDidsDb.ts — the database half — so no test had ever called
createAsset on this path. The migration is an internalAction run by hand against
prod, so the first signal would have been a failed run on real rows.
Adds coverage for the minting half: a real mint, a real verification of the
envelope it produces, and an assertion that the genesis resource still commits
to the list's original createdAt rather than the migration's clock. Confirmed
non-vacuous — reverting the fix fails it with NO_CUSTODY.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every list in prod verifies — 28 of 28 have an envelope, checked by diffing list ids against listEnvelopes.listId, no gaps either way. The problem is not verifiability, it is custody: 22 of those 28 were given their genesis by the celAssetDids migration, server-side with an ephemeral controller. Nobody holds their key, so they can never record another CEL event. Only 6 are authorable. copyList mints a new list in the owner's browser, so the key lands in their keyStore, and moves the items across. The copy is honestly new — today's genesis, its own DID, no claim on the original's history — and the source list is left untouched. Details that matter: - Owner-only. A copy mints an identity naming the owner, so a viewer of a shared list must not be able to make one. Enforced server-side; the button only decides whether to offer it. - The free-plan cap is now a shared assertListQuota rather than inline in createList, because a copy is a new list and copying must not be a way around the limit. There is a test for that specifically. - parentId is remapped in a second pass. Copying it verbatim would leave sub-items in the new list pointing at rows in the old one. - vcProofs are dropped. They attest actions taken against the SOURCE asset's DID; carrying them would attribute one asset's provenance to another, which is the exact claim this feature exists to avoid. - Item fields are rest-spread with a four-field drop list, so a column added to items later is carried automatically instead of silently going missing. Also fixes the reason none of this was visible. EnvelopeVerificationRow returned null for the absent case, so a list with no log rendered nothing — indistinguishable from a list whose log just was not shown. Every state now says something, and a retroactive genesis says so rather than claiming a history it does not have. isRetroactiveGenesis compares the genesis proof's `created` against the list's createdAt. That gap is the only signal distinguishing a migrated list from a client-minted one; both commit to the list's real creation date. Validated against prod before relying on it: the split came out 22/6, matching the migration's own run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both of next.1's changes are in surfaces we do not call:
- getAnchoringsForDidCel, so production ord providers can verify
btco-anchored did:cel assets. We do no Bitcoin anchoring — the SDK
references in convex/bitcoinAnchors.ts are commented-out scaffolding.
- addResourceVersion now hosts the new bytes for a published did:webvh
asset. We never call addResourceVersion or publishToWeb.
Verified rather than assumed, since "we don't use it" is exactly the reasoning
that missed NO_CUSTODY in the migration: typecheck, 158 tests, a production
build, and a headless run of the built bundle. Asset creation, fresh and
2.1.0-sealed verification, and tamper-rejection all behave identically to
next.0, with no console errors. The bundle moves +0.8 kB.
@originals/auth has no next.1 and stays on 3.0.0-next.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Publishing wrote a publications row with a hand-built did:webvh and never touched the list's CEL log. The chain that exists to record an asset's history was silent about the most significant thing that happens to one: 10 lists are published in prod and not one of them says so. recordPublishedVersion appends a signed `update` event carrying a content hash of what went public — name and items, via buildListSnapshot. The log then carries a verifiable version history: genesis is v1, the first publish v2, and a later publish of changed content v3. Deliberately NOT publishToWeb, despite that being the SDK's publish surface. publishToWeb migrates the asset did:cel -> did:webvh, which would change the assetDid every row is keyed by and move the public URL to a content-addressed key. boop serves published lists live at a stable path, with POST endpoints for checking items off — an immutable snapshot at a hash URL is a different product. addResourceVersion gets the provenance without touching identity or URLs. Two behaviours worth knowing: Re-publishing unchanged content is a no-op rather than an error. The SDK refuses a version identical to the current one, and a user re-publishing an unchanged list has not done anything wrong; the log simply must not grow. A list whose key was lost to the celAssetDids migration still publishes. It cannot sign the event, so it gets ListNotAuthorableError, and the modal says publishing succeeded but the history could not be recorded and points at the copy action. Failing the publish outright would regress the feature for the 5 published lists in that state. Known gap, not addressed here: checking an item off a published list goes through server-side POST endpoints, and the server holds no key, so live edits after publish are not recorded. Only publishes are. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
@originals/sdkand@originals/authto3.0.0-next.0, which pulls in the newly split-out@originals/cel@0.2.0-next.0.Pinned exact rather than caret. npm writes
^3.0.0-next.0for a prerelease, which also matches3.0.0final and every later3.x; the repo pins these exactly already.Nothing in our code had to change
tsc -b --force,eslint, the full bun suite (151 pass / 0 fail) andnpm run buildare all clean. Our import surface is three files —src/lib/originals.ts,src/lib/celKeyStore.ts,convex/migrations/celAssetDids.ts— and none of the breaking changes land on it:createAssetthrowsNO_CUSTODYwithout custodyconfig.keyStoreis alreadylocalCelKeyStoreBuffer→Uint8Arrayon the public surfaceBufferin our SDK paths@originals/sdk/crypto/*) removed@originals/authroot no longer re-exports./server3.0 also fixes something in our favour:
OriginalsSDK.createused to destructurekeyStoreout of the config before it reached theLifecycleManager, so our custody is now actually honoured downstream.The part that could have bitten
3.0 renames the CEL cryptosuite to
originals-cel-ed25519-jcs-v1and starts signing the proof configuration. Logs sealed before it cannot be re-signed, soeddsa-jcs-2022stays accepted on read — and every list envelope already in the database depends on that promise holding.Verified rather than assumed.
scripts/fixtures/cel-envelope-sdk-2.1.0.jsonis a genuine envelope minted by SDK 2.1.0 in a throwaway install, not hand-written. It verifies clean under 3.0 through our ownverifyListEnvelope, no warnings. The new test also asserts the fixture still carries the pre-3.0 suite, so it cannot quietly decay into proving nothing.If a later bump drops the legacy suite, this fails here rather than in production — where the symptom would be every existing list going unverifiable at once.
Note for the DID path
3.0 adds
normalizeUpdateKey, which stripsdid:key:down to bare multibase and fixes exactly the didwebvh-ts 2.8 breakage diagnosed in #218. It does not cover us:src/lib/webvh.tscalls didwebvh-ts directly rather than through the SDK'sWebVHManager, so #218's own fix is still what carries that path.Follow-up, not in this PR
convex/lib/turnkeyClient.tsandconvex/lib/turnkeySigner.tsare copied out of@originals/auth/server"to avoid extensionless re-exports that break Convex deploy analysis." auth 3.0 ships a curatedexportsmap and every relative import now carries.js, so that workaround should be removable — andturnkeySignByteswould then let a Turnkey key satisfyOriginalsSignerand author CEL events, not just did:webvh logs. Left out here to keep the upgrade minimal ahead of the prerelease, since it touches the Convex module analyzer.🤖 Generated with Claude Code