Skip to content

feat(sdk/js): align with Rust SDK and guest-agent proto#690

Open
Leechael wants to merge 13 commits into
feat/sdk-release-workflowsfrom
feat/sdk-js-api-parity
Open

feat(sdk/js): align with Rust SDK and guest-agent proto#690
Leechael wants to merge 13 commits into
feat/sdk-release-workflowsfrom
feat/sdk-js-api-parity

Conversation

@Leechael
Copy link
Copy Markdown
Collaborator

Summary

Aligns the JS SDK surface with the Rust SDK and the agent_rpc.proto definitions in guest-agent/.

  • getKey(path?, ...)path now defaults to '' so callers can omit it, matching Rust (Option<String>) and Python (str | None).
  • InfoResponse now surfaces cloud_vendor and cloud_product (optional). Proto AppInfo gained these in dstack 0.5.7 (63f30ce7).
  • TlsKeyOptions now accepts notBefore, notAfter, withAppInfo. Proto GetTlsKeyArgs gained these in dstack 0.5.7 (029f167f, c6d1d1ba).

Version compatibility

  • Receiving (cloud_vendor / cloud_product): typed as optional. Older guest-agent versions simply don't send these fields, so the deserialized object has undefined — no runtime check needed.
  • Sending (notBefore / notAfter / withAppInfo): when a caller sets any of these, getTlsKey first probes the guest-agent with Version() (reusing the same gating pattern as ensureAlgorithmSupported). On dstack OS < 0.5.7 the Version RPC is unavailable, so the client throws a clear error instead of silently dropping the options.

Stacked PR

Base is feat/sdk-release-workflows (PR #686) — that branch still has the JS SDK beta version bump and the trusted-publisher release workflows.

Test plan

  • npx tsc -p tsconfig.node.json --noEmit passes
  • npx tsc -p tsconfig.browser.json --noEmit passes
  • npm test against a running dstack simulator
  • Verify notBefore/notAfter/withAppInfo are accepted by guest-agent 0.5.7+
  • Verify version-gate throws cleanly on a pre-0.5.7 simulator (or mock)

Leechael added 6 commits May 19, 2026 18:44
align with Rust SDK (Option<String>) and Python SDK (str | None) where
path defaults to empty string when not provided.
proto AppInfo gained these fields in dstack 0.5.7 (commit 63f30ce). Mark
them optional so callers on older guest-agent versions still parse cleanly.
proto GetTlsKeyArgs gained these fields in dstack 0.5.7 (commits 029f167,
c6d1d1b). When the caller sets any of them, probe the guest-agent with
Version() first (reusing the algorithm-gating pattern) so older OS versions
fail loudly instead of silently dropping the options.
replace crypto.createHash('sha384'|'sha256') calls with @noble/hashes
sha384/sha256, which work identically in node and browsers without
needing a polyfill. @noble/hashes is already a peer dependency.

this prepares for removing crypto-browserify, whose upstream chain
(elliptic, create-ecdh, browserify-sign) carries unpatched advisories.
remove the crypto-browserify dep and the package.json browser field that
aliased node's crypto to it. the only consumers of node crypto in this
SDK already moved to @noble/hashes in the previous commit.

this removes the elliptic / create-ecdh / browserify-sign / bn.js (4.x)
transitive chain whose advisories have no upstream fix.
bun.lockb pinned bn.js@5.2.2, which is below the patched 5.2.3 for
advisory GHSA-378v-28hj-76wf (infinite loop). @solana/web3.js@1.98.4
declares `bn.js: ^5.2.1`, so a fresh resolution picks 5.2.3 naturally.

incidentally migrates to bun's text-format `bun.lock` (default since
bun 1.2), replacing the binary `bun.lockb`. The text format is diffable
in git.
@Leechael Leechael force-pushed the feat/sdk-js-api-parity branch from 7fe8c3d to 2ed5015 Compare May 19, 2026 12:42
move @solana/web3.js, viem, and @noble/curves out of
optionalDependencies into peerDependencies (with optional meta). they
were auto-installed by npm despite the "optional" label, dragging ~70
transitive packages into every downstream install. now a bare
`npm install @phala/dstack-sdk` pulls only @noble/hashes.

consumers that import /solana, /viem, /encrypt-env-vars, or
/verify-env-encrypt-public-key submodules must install the matching
peer explicitly.
@Leechael Leechael force-pushed the feat/sdk-js-api-parity branch from 4794930 to a485ec8 Compare May 19, 2026 13:48
@Leechael Leechael marked this pull request as ready for review May 19, 2026 14:02
Leechael added 6 commits May 19, 2026 22:22
add a \"files\" allowlist so npm pack ships only README, LICENSE,
package.json, and dist/. drops .npmignore (replaced by the allowlist).

before: 60 files / 230 KB unpacked / 57 KB tarball, including bun.lock,
src/*.ts, test-outputs.js, tsconfig*.json, and .claude/settings.local.json.

after: 43 files / 133 KB unpacked / 30 KB tarball — ~47% smaller, no
extraneous files leaking out of the repo.
reasons for rewrite:
- npm install instructions did not mention peer deps (consumers now
  need @noble/hashes explicitly, plus per-submodule peers after the
  0.5.8 optionalDependencies → peerDependencies change)
- getKey signature was stale (missing algorithm arg, path shown as
  required, key return type listed as hex string instead of Uint8Array)
- sign/verify section said "not yet released" though shipped in 0.5.7
- blockchain helpers used the deprecated toViemAccount / toKeypair
  variants instead of toViemAccountSecure / toKeypairSecure
- no coverage of attest, version, info.cloud_vendor / cloud_product,
  getTlsKey notBefore / notAfter / withAppInfo, secp256k1_prehashed,
  AppCompose type, or the verify+encrypt env-var flow
- migration content for the deprecated TappdClient surfaced ahead of
  the actual current API

structure: one section per capability area (keys, attestation, sign &
verify, diagnostics, blockchain, compose hash, encrypted env vars),
single example per method, compatibility table mapping each feature to
the minimum guest agent version, migration section moved to the bottom.

length drops from 508 to 291 lines with broader and more accurate
coverage. no AI-slop "Use Cases" / "Key Characteristics" filler.
after moving to @noble/hashes and @noble/curves the .browser.ts variants
were 95% identical to the node ones — only Buffer vs manual byte helpers
and \"import crypto\" vs globalThis.crypto. rewrite both subpaths to use
the universal pattern and drop the duplicates in the next commit.

- encrypt-env-vars.ts: drop \`import crypto from 'crypto'\` and use the
  global Web Crypto (works on Node 18+ and browsers)
- verify-env-encrypt-public-key.ts: drop Buffer (use manual hex / bigint
  helpers); make the function bodies stay synchronous like the previous
  node version, so the public signature does not change

both still return the same types and behave identically.
replace the two tsc invocations (node + browser) with a single tsup
build that emits both formats from one entry list.

- format: cjs (.js) and esm (.mjs); types as .d.ts and .d.mts
- splitting/sourcemap off; treeshake on
- exports use explicit \"types\"/\"import\"/\"require\" conditions per
  subpath so TypeScript picks .d.mts for ESM consumers and .d.ts for
  CommonJS, and runtime picks .mjs vs .js accordingly
- drop tsconfig.node.json and tsconfig.browser.json — tsup carries its
  own config, the root tsconfig.json is still used for typecheck
- drop .js.map / .d.ts.map outputs; the previous maps pointed at
  src/*.ts paths that the package did not ship, so they were dead weight

published package shape vs current 0.5.8-beta.1 on npm:

  files   : 60 → 27
  unpacked: 230 KB → 107 KB   (-54%)
  tarball :  57 KB →  23 KB   (-60%)

now also ships ESM (.mjs) — bundlers get tree-shaking, modern Node can
\`import\` natively; legacy \`require()\` still works through the .js
files.
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.

1 participant