new docs, plugins, deps bump#1715
Open
Iamknownasfesal wants to merge 25 commits into
Open
Conversation
- Upgrade @mysten/sui v2, @mysten/codegen 0.10, @noble/curves v2, @noble/hashes v2, TypeScript 6, vitest 4, @types/node 25 - Export grouped Move-module namespaces (ika, ikaCommon, ikaDwallet2pcMpc, ikaSystem) plus error and validation helpers from the public API - Add test/testnet/e2e.test.ts: parameterized sweep across every curve x valid (sigAlgo, hash) combo x dWallet kind x share source, plus future-sign, sign-during-DKG, transfer, and IkaClient surface - Add test/unit/coverage-gaps.test.ts for chain-agnostic surface - Fix getOwnedDWalletCaps to request object content under the v2 RPC - Migrate integration tests + unit utils mocks to v2 RPC shape and namespace imports - Add tsconfig types: ["node"] for TS6 with @types/node 25
New plugin layer that wraps the core SDK with a typed
source/destination/publisher composition model:
new IkaClient()
.use(suiSource({ signer, ... }))
.use(eth())
.use(ethPublisher({ url, chain }))
.use(btc())
.use(bitcoinPublisher({ apiBaseUrl }))
.use(solana())
.use(solanaPublisher({ url }))
.use(sui())
.use(suiPublisher({ suiClient }));
Highlights
* Sui source plugin: shared / zero-trust / imported-key DKG, presign
(per-dWallet + global), sign, message-compose, multi-op transaction
builder, USEK helpers, partial-DKG recovery error.
* Sui signer abstraction: accepts either an Ed25519Keypair (server flow)
or a SuiWalletSigner ({ address, signAndExecuteTransaction }) for
dApp Kit / browser wallets. `signerAddress` override for sponsored-tx
patterns.
* `ika.sui.withSigner(signer)` rebinds the source surface to a different
signer (shared IkaClient, init state, USEK cache, decoration). Pairs
with `capRecipient` on DKG inputs to support "backend funds DKG,
user signs" flows.
* Ethereum destination: EIP-1559 / 2930 / legacy tx signing, EIP-191
personal_sign, EIP-712 typed data. Fixes a double-hash bug where the
destination pre-keccak'd the message and asked the network to keccak
again; now sends the pre-hash bytes for every mode.
* Bitcoin destination: all four spending modes (p2pkh, p2wpkh,
p2sh-p2wpkh, p2tr-script), BIP-143 / BIP-341 / legacy preimage
builders exported for reuse, PSBT-mode and preimage-mode signing,
Esplora publisher with custom-broadcast override.
* Solana destination: VersionedTransaction signing, personal-message
signing, publisher with skipPreflight + blockhash-aware confirmation
loop.
* Sui destination: TransactionData / PersonalMessage signing across
ed25519 / secp256k1 / secp256r1 with the correct intent prefix +
blake2b digest.
Localnet test stack
* docker-compose with bitcoin / anvil / solana / sui + an in-process
Ika MPC swarm (Dockerfile.ika). The ika service depends on Sui being
healthy and publishes ika_config.json to a bind-mount the host can
read.
* `sui-source.localnet.test.ts`: full DKG -> presign -> sign -> broadcast
e2e against the real MPC swarm for ethereum, all four bitcoin modes
(batched presigns), solana, and sui destinations.
Move workspace fixes uncovered by the localnet rig
* `Swarm::build()` now reads SUI_FAUCET_URL from the env (was hardcoded
to 127.0.0.1:9123/gas; unusable inside a container).
* `ika-node` no longer enables `enforce-minimum-cpu` by default so
sub-16-core dev machines and CI workers can run a local swarm.
Validator builds enable it explicitly.
Examples + integrations
* multisig-bitcoin frontend rewritten on top of the plugin's
bip341/checkSig/p2tr helpers + bitcoinPublisher (946 -> 415 lines).
* keyspring backend/frontend retargeted at the new plugin surface and
config layout.
prepareSign / assembleSign on every destination
-----------------------------------------------
Splits the existing one-shot `sign()` into reusable phases so callers
whose sign requests don't flow through `ctx.source.signMessage`
directly (multisig contracts, future-sign, sponsored relays, ...) can
plug in their own gating mechanism:
const { prep, preimage, plan } = await dWallet.bitcoin.prepareSign({
kind: 'psbt', psbt, inputIndex, mode, network,
});
// hand `preimage` + `plan` to whatever Move flow gates the sign
// ...later, when the network signature lands...
const signed = await dWallet.bitcoin.assembleSign(prep, signature);
`sign()` becomes a thin composition (`prepareSign` → source
`signMessage` → `assembleSign`). Existing callers are unchanged.
The prep object carries ONLY what `assembleSign` reads — the preimage
and plan live in the prepareSign result shape, not the prep. That lets
callers reconstruct a prep at execute-time from persisted state (PSBT,
dWallet pubkey, mode, network) without needing the original preimage.
Same shape on all four destinations:
bitcoin prep = { kind, mode, network, sender, psbt, inputIndex,
hashType, compressedPubkey, p2trBundle } (psbt)
{ kind, mode } (preimage)
ethereum prep = { digest, sender, input }
solana prep = { sender, input }
sui prep = { bytes, sender, curve, publicKey }
Source-side prepareSign
-----------------------
`ika.sui.prepareSign({ dWallet, message, ...plan, presign })` computes
the user-side centralized-party sign message without submitting
anything. Shared/imported-shared read the user share publicly; zero-
trust/imported-key decrypt via USEK. The output bytes plug into any
Move flow that ultimately calls `request_sign`.
Future-sign (Phase 1 + Phase 2)
-------------------------------
`ika.sui.requestFutureSign(...)` submits a `request_future_sign` PTB,
transfers the unverified cap to `capRecipient` (default: signer), and
waits for NetworkVerificationCompleted. Returns
`{ partialSignatureId, capId }`.
`ika.sui.completeFutureSign(...)` consumes the validated cap plus a
fresh message approval and triggers the MPC sign. Returns the sign id;
fetch the signature and finalize via the destination's `assembleSign`.
Auto-detects dWallet kind (shared / zero-trust / imported-key /
imported-key-shared) and routes to the matching SDK method.
Also exposes compose-mode variants
(`ika.sui.compose.requestFutureSign` /
`ika.sui.compose.completeFutureSign`) for composing inside
`ika.sui.transaction(...)`.
multisig-bitcoin: drop manual PSBT finalize
-------------------------------------------
`MultisigBitcoinWallet.finalizeTransaction` now delegates to the
bitcoin destination plugin's `assembleSign`. The assemble prep is
reconstructed locally from the cached p2tr bundle — no placeholder
preimage workaround.
- @ika.xyz/plugins: add build-package script, ESM tsconfig, subpath shims (bitcoin/ethereum/solana/sui), flat exports with types conditions, files entry; re-export Bitcoin network/payload types from the publisher subpath - SuiTxExecutionResult: expose digest (optional) and switch events to a mutable Array so consumers can pass it straight into their parsers - keyspring backend: rewrite ExecEvent + parsers to match the new plugin event shape (eventType, bcs as bytes via .parse) and tolerate missing digest - multisig-bitcoin frontend: declare @ika.xyz/plugins workspace dep, bump tsconfig target to ES2020 for BigInt literals - pnpm workspace: include multisig-bitcoin/frontend, exclude generated plugin subpath dirs
- @ika.xyz/plugins: add typecheck/lint/test scripts, .prettierignore,
vitest config aliasing workspace sources, test tsconfig
- Move all plugin tests out of sdk/typescript into sdk/plugins/test:
unit/ (bitcoin-{plugin,preimage}, ethereum-plugin, plugin-client,
plugins-runtime), testnet/plugin-e2e, full localnet/ stack
- Update sdk/typescript: drop plugin aliases from vitest.config and
test scripts, remove obsolete tsconfig.test.json
- Rewrite Buffer/require usage in bitcoin destination + tests to
satisfy the workspace no-restricted-globals / no-require-imports
rules; bitcoinjs-lib v7 accepts Uint8Array natively
- Fix ethereum unit test mock: source must hash the preimage with
keccak256 before signing (matches what real MPC does internally)
- IkaClient constructor: replace `const self = this` with arrow
helpers to satisfy no-this-alias
- ts-ci.yaml: matrix over sdk/typescript + sdk/plugins; run install
at repo root, build sdk/typescript first for plugins, add
typecheck + unit-test steps for plugins
- README.md: architecture, install, subpath imports, prepareSign /
assembleSign, future-sign, directory layout, localnet usage
publish-typescript-sdk.yml - Trigger on both sdk/typescript-* and sdk/plugins-* tags - Tag→package.json version check routes by tag prefix - Manual version override updates plugins/package.json too - Add get/check/build/publish steps + summary row for plugins ts-ci.yaml - Run pnpm install at workspace root once, not per-package - Build sdk/typescript ahead of plugins (workspace dep) - Typecheck + unit-test steps gated to sdk/plugins only Also remove sdk/plugins/PRD.md (specification snapshot no longer in sync with the implemented prepareSign / assembleSign API).
CI failed because `pnpm typecheck` ran before `pnpm build`, so the `@ika.xyz/plugins/*` subpath imports in test files could not resolve (the package.json `exports` field points into `dist/`). Cascade: every unresolvable module became `any`, surfacing implicit-any errors in callback parameters that depended on those module types. Add `paths` mappings in `test/tsconfig.json` so test typecheck resolves to the in-tree source files instead of the built declarations. The build step is no longer a prerequisite for typechecking; build state and typecheck are now independent.
Restore crates/ika-node/Cargo.toml and crates/ika-swarm/src/memory/swarm.rs to their state before d8f0999. These changes were unrelated to the plugins work and the user wants them dropped. Net effect: - ika-node default features include `enforce-minimum-cpu` again - ika-swarm faucet URL is hardcoded to LOCAL_DEFAULT_SUI_FAUCET_URL
destinations
- Sui PersonalMessage: BCS-wrap the message (bcs.vector(bcs.u8))
before messageWithIntent, matching @mysten/sui Signer.signPersonalMessage
so verifyPersonalMessage accepts the signature (H1)
- Sui assembleSign: assert signature.length === 64 (M2)
- Ethereum: serializeTransaction for legacy txs requires `v`, not
`yParity`. Build a legacy-shape sig triple `{r, s, v: 27n+yParity}`
so viem's EIP-155 v-rewriting works (H2)
- Ethereum EIP-712: when types omits EIP712Domain, derive from domain
fields via viem's getTypesForEIP712Domain (instead of `[]`) so the
preimage matches hashTypedData (H3)
- Ethereum low-S normalization: post-Homestead nodes reject high-S
txs; OpenZeppelin's ECDSA.recover rejects high-S. Re-normalize the
64-byte (r||s) into low-S before assembly. yParity recovery handles
the parity flip automatically (H4)
- Bitcoin DER encoding: re-normalize to low-S before BIP-66 encoding
so BIP-146 / standard-relay nodes propagate the tx (M1)
- Bitcoin p2tr-script applySignature: assert 64-byte schnorr sig (L6)
- Solana assembleSign: assert 64-byte Ed25519 sig (L1)
source
- findEvent: match by canonical inner-module path
`::coordinator_inner::<Type>>` rather than bare struct name —
prevents a malicious SuiWalletSigner from injecting fabricated
events whose type happens to contain the bare struct name (L2)
- keyspring backend: same hardening on its local event parsers (L3)
- withSigner: pass-through `userShareEncryptionKeys` option, with
jsdoc spelling out the inheritance footgun for multi-tenant
backends (M3)
plugin host
- decorate: validate dWalletExtend returns an object, walk keys via
Reflect.ownKeys, reject non-string keys (L4, L5)
tests
- new sui-destination.test.ts: verifyPersonalMessage end-to-end +
length check
- ethereum-plugin.test.ts: EIP-712 with omitted EIP712Domain, legacy
transaction signing, high-S → low-S normalization
other
- docker-compose.yml: update Dockerfile path (now under sdk/plugins)
docker-compose
- ika service no longer depends on the sui service. mysten/sui-tools
is amd64-only; on arm64 hosts Rosetta-emulated genesis can take
30+ min. Document running `sui start --with-faucet` natively on
the host instead, with env-overridable RPC URLs.
- add `host.docker.internal:host-gateway` to ika so the container
can reach the host-run sui localnet.
bitcoin.localnet.test
- switch the two inline credentialed `fetch(...)` calls to
`chain.walletRpc(...)`. Node 20+ rejects credentialed URLs in
Request; the helper already uses Basic Auth header.
solana.localnet.test
- replace `conn.confirmTransaction({blockhash, lastValidBlockHeight,
signature}, ...)` for the airdrop with a `getSignatureStatuses`
poll. The websocket signature subscription hangs against the
older validator under Rosetta, even when the airdrop confirms.
- bump per-test timeout to 180s for slow rosetta paths.
.gitignore
- track the new sdk/plugins/test/localnet/ika-state/ path; drop
the stale sdk/typescript/ entry.
Source and destination plugins both targeted `ika.sui.prepareSign`, so registering them together (the canonical destination pattern) threw `plugin collision: 'sui.prepareSign' already registered` at .use() time. Caught by sui-source.localnet.test.ts. Source's prepareSign returned the *user-side centralized-party sign message*; destination's prepareSign returns the chain-specific preimage. Different things — both legitimately "prepare sign". Rename the source's to its semantically correct name (`prepareSignMessage`) and update the public export + interface. Destination's `prepareSign` (and `assembleSign`) stay as-is.
Replace `conn.confirmTransaction({blockhash, lastValidBlockHeight,
signature}, ...)` for the airdrop with a getSignatureStatuses
poll. Older test-validator + rosetta emulation hangs the websocket
signature subscription even when the airdrop has confirmed; the
lastValidBlockHeight strategy then fires "block height exceeded".
Also bump the publisher confirm timeout (60s → 180s) for the
Solana e2e. The publisher itself caps at the user-supplied value;
60s isn't enough for a rosetta-emulated validator.
regression in H2 (legacy tx detection)
- Use viem's getTransactionType(input.tx) instead of a string check
on `tx.type`. viem's serializer infers legacy from gasPrice
presence even when the caller omits `tx.type`; the previous
string check missed that path and let `{r, s, yParity}` flow into
serializeTransactionLegacy, which then crashed reading
signature.v. Add a regression test for the implicit-legacy shape.
stale doc in sdk/plugins/src/sui/index.ts
- The aggregator comment still claimed `prepareSign` could be
imported from the source subpath; that export was renamed to
`prepareSignMessage`. Update the example imports.
Running `pnpm prettier:fix` from the workspace root reformatted 46 files outside sdk/plugins (workflows, READMEs, docs, skills, examples). Double→single quotes and 4-space→tab indentation per the root prettier.config.js — no semantic changes.
Top-level reorganization
- meta.json: get-started, learn, build, solana-integration, operate,
reference, ai-skills
- core-concepts -> learn (renamed; reordered; trust-model split out;
zero-trust-and-decentralization folded in)
- sdk -> build/sdk; ika-client/ and ika-transaction/ subtrees
collapsed to single pages
- move-integration -> build/move-integration (subtree preserved;
internal links updated)
- cli -> operate/cli
- skills -> ai-skills (4 per-skill landing pages added)
- new sections: get-started/, build/{plugins,recipes,architecture},
operate/{validator-setup,validator-operations,networks},
reference/{curve-signature-hash-matrix,events,network-configs,
move-modules}
- removed: code-examples/ and operators/ placeholder stubs
Content
- Authored against the 2025/297 protocol paper, the 2024/253 abstraction
paper, and the live Rust + TypeScript implementation (audited via
parallel agents). Class-groups TAHE only; no Paillier in any
current code path. Hash applied inside the MPC. Bitcoin Taproot is
script-path only.
- get-started/index.mdx: 5-to-10 minute Bitcoin signing tutorial.
- learn/: what-is-ika, dwallets (four kinds + lifecycle), trust-model
(1+(t-of-n), forgery, liveness, what survives which compromise),
2pc-mpc, cryptography, multi-chain-vs-cross-chain, whitepaper.
- build/: architecture, sdk/* with concrete API surface,
plugins/* covering source/destination/publisher composition,
prepareSign/assembleSign, future-sign Phase 1+2, four destinations,
publishers; recipes/* with five end-to-end runnable patterns;
move-integration relocated with internal links updated.
- operate/: cli (existing), validator-setup, validator-operations,
networks (mainnet/testnet/localnet with arm64 Rosetta workaround).
- reference/: curve+sig+hash matrix, events with canonical
::coordinator_inner:: paths, network configs, Move modules.
- ai-skills/: four skill landing pages plus an updated index.
solana-integration
- Labeled "(pre-alpha)" in the section title and nav.
- Disambiguation callout: this is Solana-as-coordination-chain, not
the Solana destination plugin (which is in build/plugins).
skills/
- ika-sdk: SKILL.md rewritten to lead with the plugin layer.
api-reference.md appends a Plugin Layer section covering source,
destinations, publishers, prepareSign/assembleSign, future-sign,
withSigner, capRecipient. flows.md appends plugin-layer flows
including the canonical "backend funds DKG, user signs" pattern,
hash-application invariant, ECDSA low-S note, Taproot constraint.
- ika-move: typescript-integration.md adds a pointer note to the
plugin layer for typical app code paths.
- ika-cli, ika-operator: independent of the plugin layer; no
changes needed.
redirects
- docs/public/_redirects (Cloudflare Pages format) preserves
/docs/core-concepts, /docs/sdk, /docs/cli, /docs/move-integration,
/docs/skills, /docs/operators, /docs/code-examples deep links.
other
- Stripped em/en dashes site-wide per style.
- Final build green; static export emits _redirects to out/.
Build error fix - Next static export needs a param entry for the empty-slug case of /docs/[[...slug]]. The new docs/content/docs/index.mdx provides the root landing page so source.generateParams() emits a slug for it, and /docs renders as a static HTML. Homepage - Replace feature cards to reflect the new top-level sections: Get Started, Learn, Build (SDK + plugins), Plugins, Move integration, Solana Integration (pre-alpha), Operate, AI Skills. - Lift the Solana callout copy and add a Solana link to the footer. - Hero CTA now points to /docs/get-started. - Install snippet updated to include @ika.xyz/plugins. - Second CTA on the install card links to the trust model so the honest read of guarantees is one click from the front page.
Lead Solana ahead of Sui across the hero subtitle, the programmable value-prop, the install card, the feature grid, and the footer links. Sui stays present and production-tagged, but Solana takes the prime spot in every place where order matters.
Sidebar - Remove root:true from nested meta.json (build/sdk, build/plugins, build/recipes, build/move-integration, operate/cli). Those are subsections, not roots; the flag was making them appear at the top level of the sidebar dropdown. - Now only the seven actual top-level sections are roots: get-started, learn, build, solana-integration, operate, reference, ai-skills. Tab icons - Rewrite layout.tsx tabConfig with one entry per current top-level section. Stale keys (sdk, cli, move-integration, core-concepts, skills) replaced. - Solana Integration uses the actual Solana brand SVG instead of a generic globe, with Solana brand colors. - Per-section descriptions tightened to a one-line accurate summary. Banner - Banner copy aligned with the rest of the docs voice.
Plugin API
- SourceSurface gains an optional `createDWallet(input)` method so
destinations can forward a chain-led `createDWallet` call to the
active source. The Sui source implements it by delegating to its
existing `ika.sui.createDWallet`.
- Each chain-specific destination (btc, eth, solana) exposes a new
`ika.<chain>.createDWallet({ kind, importedKey?, capRecipient?,
... })` that picks the right curve and returns an
already-decorated dWallet. Sui destination intentionally skips it
to avoid colliding with the source's existing `ika.sui.createDWallet`.
- The resulting dWallet is decorated via the host's `ctx.client.decorate`
before returning, so per-chain namespaces are usable without a
separate call.
Mental-model note for callers
- A dWallet is bound to one curve, not one chain. `ika.bitcoin.createDWallet`
produces a SECP256K1 key that also signs for Ethereum via the same
handle. The docs page calls this out so users do not think they
have created a Bitcoin-only key.
- `ika.solana.createDWallet` is single-chain in practice because
Ed25519 is not compatible with the SECP256K1 chains.
Docs
- All chain-specific recipes, plugin pages, get-started, the
homepage, and the SDK skill now lead with `ika.<chain>.createDWallet`
and document the equivalent source-level call.
- New page `build/plugins/writing-your-own.mdx` covers the plugin
contract: how to write a source, destination, or publisher. Linked
from the plugins index.
.gitignore
- The blanket `build` rule was matching `docs/content/docs/build/`,
which silently dropped most of the new plugins / recipes / SDK
pages from previous commits. Tighten to
`contracts/**/build/` and `deployed_contracts/**/build/` so only
Move build artifacts are ignored. Re-add the docs/content/docs/build
files that were never committed: plugins/* (8 pages), recipes/* (6
pages), architecture, index, sdk/ika-client, sdk/ika-transaction,
sdk/low-level, sdk/setup.
Sidebar - Remove root:true from every top-level section meta.json. Sections now appear in a single tree instead of as separate tabs, so when a user lands on Get Started (one page) the sidebar still shows Learn, Build, Operate, etc. Solves the "looks empty" issue. - Remove the tabConfig + transform logic from docs/layout.tsx since there are no tabs to transform anymore. Icons - Each top-level meta.json now declares `icon`. The loader in lib/source.ts maps the string to a lucide-react icon and renders it next to each folder in the sidebar tree. Notes on the dep upgrade ask - Attempted fumadocs 14 -> 16 + next 15 -> 16. Reverted because the upgrade pulls Tailwind v4 (createPreset removed) and a substantial styling refactor. That belongs in a dedicated PR; staying on 14/15 for this commit.
Bucket pages with a bulleted list of children are gone. Each section
landing is now a card hub with icons, descriptions, and a logical
grouping (start here / core / advanced / etc).
IA consolidation
- Drop Get Started as a top-level tab. Its single tutorial page moves
to learn/quickstart.mdx and is the second item in Learn (after the
index hub).
- Five top-level tabs now: Learn, Build, Solana, Operate, Reference,
AI Skills (six counting AI Skills). The empty Get Started tab is
gone.
- learn/meta.json adds quickstart and a "Concepts" separator before
the conceptual pages.
- _redirects gets /docs/get-started -> /docs/learn/quickstart.
Card-hub landings
- learn/index: start here (quickstart, what-is-ika), core concepts
(dwallets, trust-model, multi-chain), cryptography (2pc-mpc,
primitives, whitepaper).
- build/index: start here (architecture, plugins), lower-level (sdk,
move-integration), patterns (recipes, writing-your-own).
- build/sdk/index: setup, core API (client, transaction, keys),
lower-level (helpers, low-level builders).
- build/plugins/index: keeps the architecture prose, but converts the
trailing "where to go next" bullet list into three card groups
(two-phase + future-sign, per-chain destinations, publishing +
custom plugins).
- build/recipes/index: one card per recipe (shared bitcoin, zero-trust
ethereum, imported-key, future-sign multisig, sponsored DKG).
- operate/index: tools (CLI, networks), validators (setup, ops).
- reference/index: four lookup cards (matrix, events, network configs,
Move modules).
- ai-skills/index: replace the markdown table at top with a card grid;
drop the trailing per-skill duplicate list at the bottom.
Cross-references
- index.mdx, learn/what-is-ika.mdx, build/recipes/{index,shared-bitcoin}
all updated from /docs/get-started -> /docs/learn/quickstart.
- Home page (3 CTAs) updated; "Get Started" cta still labeled
"Get Started" but points to /docs/learn/quickstart.
…ion) The previous rewrite dropped the tabConfig+transform pattern, so the sidebar tab selector lost its icon badges and the description line under each tab title. Restoring both for the current IA. Each tab gets: - a colored icon badge (rounded-lg, color + bgColor pair) - a description rendered below the title Mappings: - learn -> BookOpen, rose, "Concepts, trust model, and cryptography" - build -> Code2, pink, "SDK, plugins, Move integration, recipes" - solana-integration -> Globe, purple, "Solana coordination chain (pre-alpha)" - operate -> Terminal, emerald, "CLI, validators, and network configs" - reference -> Library, fuchsia, "Curves, events, configs, Move modules" - ai-skills -> Bot, amber, "AI skills for coding agents" The meta.json `icon` field stays for the in-tree folder icons (that is a separate render path through the loader transformer in lib/source.ts).
The ---Concepts--- separator rendered as a non-clickable label styled the same as the page links above and below it. Visually it looked like a broken link. The card hub on learn/index.mdx already groups the pages (Start here / Core concepts / Cryptography), so the sidebar separator was redundant.
…ages
Sidebar separators previously rendered as plain medium-weight text
the same size as page links, which made non-clickable labels look
broken. Two changes:
1. Custom Separator component
- sidebar-config.tsx: a client-side wrapper around DocsLayout that
passes `sidebar.components.Separator` to fumadocs. The component
renders the label as small uppercase tracked text in muted color,
followed by a thin horizontal rule that fills the rest of the row.
- Visually obvious that it's a section header, not a link.
- Extracted into a client component because functions cannot cross
the server/client boundary in app router. The outer layout stays
a server component so the page-tree loader (fs access) still
works; the inner DocsLayoutClient owns the function refs.
2. Page groupings via separators
- Learn: Concepts / Cryptography
- Build: Layers / Patterns
- Operate: Tools / Validators
- Solana, Reference, AI Skills: no separators (small enough)
Bug - next.config.mjs had output:'export' without trailingSlash. Next then emitted files like out/docs/learn.html (file-style, no directory). - The browser served those at URLs without a trailing slash, e.g. /docs/learn. Relative links on that page like ./quickstart resolved against the file URL, so they pointed at /docs/quickstart (wrong) instead of /docs/learn/quickstart. - Every section landing's card hub used relative ./xxx links to its children, so the entire Learn / Build / Operate / Reference sidebars appeared "broken" from the landing pages. Fix - trailingSlash:true makes the export emit directory-style paths: out/docs/learn/index.html, out/docs/learn/quickstart/index.html etc. - The browser URL becomes /docs/learn/ (with the slash), so ./quickstart resolves to /docs/learn/quickstart/ as intended. The static link audit script (markdown + JSX href + anchors across 73 pages) reports zero broken targets; this commit only addresses the runtime resolution issue caused by the export shape.
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.
Description
Introduces
@ika.xyz/plugins— a new first-party SDK package that wraps@ika.xyz/sdkwith chain-aware sources, destinations, and publishers — plusthe host plumbing on the SDK side (
@ika.xyz/sdk/plugin) that lets themcompose onto a single
IkaClientinstance. Adds aprepareSign/assembleSigntwo-phase signing API to every destination, a future-signflow on the Sui source, and the CI / publish wiring needed to ship the new
package.
What's in the box
@ika.xyz/plugins— new packagesuiSource— handles DKG, encryption-key registration,presigns, signs. Accepts either an
Ed25519Keypair(backend) or aSuiWalletSigner(dApp Kit) via the newSuiSignerabstraction.btc,eth,solana,sui. Each one exposesprepareSign/assembleSignso custom Move flows (multisig,sponsored, future-sign) can gate between message preparation and
signature assembly without re-implementing chain-specific encoding.
bitcoinPublisher,ethPublisher,solanaDevnet/solanaMainnet,suiPublisher. Singleika.publish({ chain, payload })call.
Subpath imports (
@ika.xyz/plugins/bitcoin/destination, etc.) keepper-chain peer deps (
bitcoinjs-lib,viem,@solana/web3.js)optional and tree-shakable.
@ika.xyz/sdk/plugin— plugin hostIkaClient(plugin variant) — orchestrates source/destination/publisherlifecycle, decoration of dWallet handles, and the live
IkaContextpassed to plugins on install.
DWallet,IkaContext,BaseSignResult,SignMessageInput,source / destination / publisher interfaces.
Two-phase signing
Every destination splits signing into:
prepareSign(input)→{ prep, preimage, plan }.prepis the minimalshape
assembleSignreads;preimageis what gets passed to the source;planis{ curve, signatureAlgorithm, hash }.assembleSign(prep, signature)→ chain-specific signed payload.dWallet.<chain>.sign(input)composes both. The split lets callers runarbitrary gating logic (multisig approval, future-sign, sponsored tx) in
between, instead of being locked into the default "submit immediately"
path.
Future-sign on Sui
Phase 1 issues a
PartialUserSignatureCap; Phase 2 redeems it. Supportedfor all four dWallet kinds.
Cross-account flows
capRecipienton every DKG-creating method — backend funds DKG, caplands at the end user's address.
withSigner(other)— rebind execution to a different signer withoutreinstalling the source. Same
IkaClient, same caches.CI + publishing
.github/workflows/ts-ci.yaml— matrix oversdk/typescript+sdk/plugins. Single workspacepnpm install, build typescript firstas the plugins workspace dep, then per-package prettier / typecheck /
build / eslint / unit tests.
.github/workflows/publish-typescript-sdk.yml— addssdk/plugins-*tag trigger alongside the existing
sdk/typescript-*trigger. Tag →package.json version check routes by prefix; manual version override
updates all three packages (ika-wasm + typescript + plugins) at once.
Test layout
All plugin tests live in
sdk/plugins/test/:unit/— bitcoin-plugin, bitcoin-preimage, ethereum-plugin,plugin-client, plugins-runtime
testnet/plugin-e2e.test.ts— multi-chain e2e against Sui testnetlocalnet/— full docker stack (Ika validators + Anvil + Bitcoin Coreregtest + Solana test-validator) with one test per destination
The plugin tests previously lived in
sdk/typescript/test/. Moving theminto the package they cover keeps the SDK tests focused on protocol /
client behavior and makes plugin coverage independent of SDK changes.
Examples + downstream fixes
sdk/plugins/examples/— 10 numbered runnable scripts (DKG, sign perchain, compose multi-op, multisig approval, partial-DKG recovery).
examples/keyspring/backend— rewritten to use the source/publisherplugins and the new event shape (
eventType+ bytesbcs).examples/multisig-bitcoin/frontend— switched tobtcPrepareSign/btcAssembleSignfor its custom Move flow;tsconfig target bumped to
ES2020for BigInt literals.README
sdk/plugins/README.mdcovers architecture, install, subpath imports,prepareSign/assembleSign, future-sign, the directory layout, andlocal development.
Test plan
pnpm install && pnpm -F @ika.xyz/sdk build && pnpm -F @ika.xyz/plugins build— clean local buildpnpm -F @ika.xyz/plugins typecheck—src+test, source-resolved paths (no dist dependency)pnpm -F @ika.xyz/plugins lint— eslint + prettierpnpm -F @ika.xyz/plugins test:unit— 83/83 passingpnpm -F @ika.xyz/sdk test:unit— 148/148 passingpnpm -F @ika.xyz/sdk build && pnpm -F @ika.xyz/sdk lintnpx tsc --noEmitinexamples/keyspring/backend— cleannpx tsc --noEmitinexamples/multisig-bitcoin/frontend— cleanpnpm -F @ika.xyz/plugins test:localnet— full docker stack (skipped this round, needs reviewer to run)pnpm -F @ika.xyz/plugins test:testnet— needsIKA_TESTNET_PRIVATE_KEYfrom a funded testnet accountNotes for reviewers
examples. Validator / Move contract code is unchanged.
@ika.xyz/pluginsis unpublished today. Once this lands, pushsdk/plugins-0.1.0to trigger Trusted Publishing. The package willresolve
@ika.xyz/sdkat whichever version is currently published(pnpm rewrites
workspace:*at publish time).Bufferis banned in plugin source by the workspace eslint rule.bitcoinjs-lib v7 accepts
Uint8Arraynatively; existingBuffer.from(...)wrappers in the Bitcoin destination were removed.This is documented in the new README and is a permanent design
constraint, not a roadmap item.