Skip to main content

1.17.x Upgrade and Migration Guide

Overview

Overview

With the forthcoming Blocklet Server v1.17.x release, we have implemented optimizations across various security scenarios for Blocklet Server. However, these enhancements also bring with them some breaking changes:

  • Removed some Blocklet environment variables (BLOCKLET_APP_SK, BLOCKLET_APP_PSK).
  • @blocklet/sdk's getWallet() returned object removed the wallet.secretKey / wallet.sk properties;
  • Most signing and verification functions in @blocklet/sdk and @ocap/wallet have transitioned from synchronous to asynchronous.
  • The export methods for several packages have shifted from CommonJS default exports to ES Module named exports.
  • In the following scenarios, the signing/verification private key has been changed from the original BLOCKLET_APP_SK to BLOCKLET_APP_ASK: cross-Component calls, Service API calls, Server API calls, Notifications, or WebSocket communications.
  • Some environment variables have been added:
    • BLOCKLET_APP_ASK: A new environment variable, designed for signing in the following scenarios: Cross-Component calls / Service API or Server API invocations / Notification or WebSocket communications
    • BLOCKLET_APP_PPK: The original BLOCKLET_APP_PSK's public key

This guide breaks down these changes into distinct migration steps, illustrative replacement examples, and critical regression checks, streamlining gradual modifications and comprehensive manual verification.

Before making modifications, please ensure that all related dependencies have been upgraded to the latest version (version: 1.17.1).

This document can be copied to AI for migration and modification.

List of Major Breaking Changes

1. Environment Variable Changes

Changes: BLOCKLET_APP_SK and BLOCKLET_APP_PSK have been removed. Any code relying on these private keys must now sign via the wallet interface of @blocklet/sdk or export the public key for verification.

Migration Highlights:

  • If your code directly signs using a private key, refactor it to use getWallet() to retrieve the wallet and utilize its asynchronous signing method (refer to the example).
  • To derive a sub-wallet using a private key, use await getWallet().deriveWallet().
  • For verification purposes only, use the public key getWallet().publicKey or process.env.BLOCKLET_APP_PK as the replacement.

SK Signature:

Old code:

javascript
const wallet = fromSecretKey(process.env.BLOCKLET_APP_SK);
const signature = wallet.sign();

New Code:

javascript
const { getWallet } = require('@blocklet/sdk/lib/wallet');
const wallet = await getWallet();
const signature = await wallet.sign();

JWT Signature:

Old:

javascript
const jwtToken = jwt.sign(payload, process.env.BLOCKLET_APP_SK);

New:

javascript
const { getWallet } = require('@blocklet/sdk/lib/wallet');
const wallet = await getWallet();
const jwtToken = await wallet.signJWT(payload);

SK Verification:

 Previous:

javascript
const wallet = fromSecretKey(process.env.BLOCKLET_APP_SK);
const isValid = wallet.verify(data);

New:

javascript
const wallet = fromSecretKey(process.env.BLOCKLET_APP_PK);
const isValid = wallet.verify(data);

fromAppDid:

Old:

javascript
// BLOCKLET_APP_SK
const wallet = fromAppDid(address, process.env.BLOCKLET_APP_SK, { role: types.RoleType.ROLE_ACCOUNT }, x)

// BLOCKLET_APP_PSK
const wallet = fromAppDid(address, process.env.BLOCKLET_APP_PSK, { role: types.RoleType.ROLE_ACCOUNT }, x)

New:

javascript
  const { deriveWallet } = require('@blocklet/sdk/lib/wallet');

  // BLOCKLET_APP_SK
  const wallet = await deriveWallet(address, { role: types.RoleType.ROLE_ACCOUNT }, x);

  // BLOCKLET_APP_PSK
  const wallet = await deriveWallet(address, { role: types.RoleType.ROLE_ACCOUNT }, x, 'psk');

2. Changes to the getWallet() return object

Changes: Removed properties such as wallet.secretKey / wallet.sk; all signing/verification methods are now asynchronous.

Key Migration Points:

  • All calls to wallet.sign(), wallet.verify(), etc., must be awaited.
  • If your code relies on wallet.secretKey for signing or derivation, please consult the modifications detailed under 1. Environment Variable Changes.

Example:

Old:

javascript
const { getWallet } = require('@blocklet/sdk/lib/wallet');

const wallet = getWallet();
const sig = wallet.sign(data);

console.log(wallet.secretKey) // sk....

New:

javascript
const { getWallet } = require('@blocklet/sdk/lib/wallet');

const wallet = await getWallet();
const sig = await wallet.sign(data);

console.log(wallet.secretKey) // undefined

3. Multiple Exports Are Now Named (ESM) Exports

Changes: Some packages have been changed from default or CommonJS-style exports to named exports. The import syntax requires adjustment.

Common Replacement Patterns:

javascript
- import parse from '@blocklet/meta/lib/parse';
+ import { parse } from '@blocklet/meta/lib/parse';

- const env = require('@blocklet/sdk/lib/env');
+ const { env } = require('@blocklet/sdk/lib/env');

Affected Packages and Common Exports (Please search and replace globally in the codebase):

  • @blocklet/sdk: BlockletAuthenticator, createConnectHandlers, Database, authMiddleware, fallback, sessionMiddleware, sitemap, userMiddleware, WalletAuthenticator, WalletHandlers, getWallet, getPkWallet, createRemoteWallet, deriveWallet, getPermanentWallet, getEthereumWallet, getAccessWallet, env, BlockletService, etc.
  • @blocklet/meta:toBlockletDid, validateBlockletEntry, getComponentProcessId, hasReservedKey, getBlockletInfo, parseNavigation, parse, urlPathFriendly, verifyMultiSig, getApplicationWallet, checkBlockletEnvironment, env.
  • @blocklet/env: Previously, env was directly default-exported; it now requires destructuring from the blockletEnv or env named exports.

It is recommended to use your IDE's project-wide grep/replace functionality or make file-by-file adjustments, ensuring that import statements and their exported names remain consistent.

4. Synchronous -> Asynchronous: await and async are required at the call site.

Changes: The following functions/methods are now asynchronous (only frequently used ones are listed). Please add await to the call sites and mark the functions containing these calls as async.

4.1 @blocklet/sdk

  • verify() / sign() / getSignData()(lib/util/verify-sign.ts)
  • signResponse() / verifyResponse() (in lib/security)
  • getDelegation() (lib/connect/shared.ts)
  • verifyBlockletSig()(lib/middlewares/blocklet.ts)
  • subscribe() / unsubscribe() (lib/service/eventbus.ts)
  • ensureClient() / on()(lib/service/notification.ts)

4.2 @ocap/wallet

All methods of wallet instances generated by the library (or returned by getWallet()) are now asynchronous:

  • wallet.sign()
  • wallet.verify()
  • wallet.ethSign()
  • wallet.ethVerify()

4.3 Other Libraries (Common)

  • @arcblock/jwt:jwt.sign(), jwt.verify(), jwt.signV2() now require await.
  • @blocklet/meta:signResponse(), verifyResponse() now require await.
  • @asset/vc:create(), verify(), verifyPresentation(), createCredentialList(), verifyCredentialList() – all return awaitable Promises.
  • @blocklet/js-sdk:verifyResponse() -> await.
  • @blocklet/store / @blocklet/cli: sign() now requires await.
  • @blocklet/server-js: _getAuthHeaders(), getAuthHeaders(), signWithAccessKey() -> await.

5. Key Considerations for Signing and Invocation Scenarios

In the following scenarios (cross-component calls, service API invocations, notifications, or WebSockets), the signing private key, previously process.env.BLOCKLET_APP_SK, should now be the secretKey from the access wallet.

Migration Key Points:

  • To use @blocklet/sdk's getAccessWallet(), obtain the access wallet (which includes the signing key). The secretKey should then be explicitly passed within the options parameter of sign(), verify(), or getSignData(). Example:
javascript
import { sign, verify, getSignData } from '@blocklet/sdk/lib/util/verify-sign';
import { getAccessWallet } from '@blocklet/sdk/lib/wallet';

const accessWallet = await getAccessWallet();

// sign
const result = await axios({
  url: '/service/api',
  data: json,
  headers: {
    'x-app-id': accessWallet.address,
    'x-app-pk': accessWallet.publicKey,
    'x-component-sig': await sign(json, { appSk: accessWallet.secretKey }),
    'x-component-did': process.env.BLOCKLET_COMPONENT_DID
  },
});

// verify
const isValid = await verify(data, signature, { appSk: accessWallet.secretKey });

// getSignData
const signData = await getSignData(data, { appSk: accessWallet.secretKey });

Code Migration Checklist (Prioritized)

  1. From the repository root, run the upgrade command and install dependencies. Create a separate branch.
  2. Perform a global search-and-replace operation to update import methods, transitioning from CommonJS default exports to named exports.
  3. A comprehensive search for usages such as getWallet(), fromSecretKey(), and fromAppDid().
    • Usage of private key attributes (secretKey / sk) must be replaced with getWallet() for signing, or with getAccessWallet() combined with appSk.
    • Change all sign() / verify() / getSignData() and similar calls to use await.
  1. For calls spanning components, services, notifications, and WebSockets, ensure to use getAccessWallet() and pass appSk to the signing method.
  2. After modification, run TypeScript/Node builds and unit/integration tests to identify any missing synchronous calls or import errors.
  3. Manual file-by-file review: Pay special attention to code paths related to middleware, service, eventbus, notification, and connect.

Frequently Asked Questions and Troubleshooting Tips

  • Bad secret key size
    • Reason: The dependencies were not upgraded to the latest version.
  • Various verification failures
    • Confirm that the signature utilizes the access wallet's secretKey.
  • Other components failed to start
    • Please upgrade all components in your application to the latest version, then perform a complete shutdown and restart.