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 thewallet.secretKey/wallet.skproperties; - 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().publicKeyorprocess.env.BLOCKLET_APP_PKas the replacement.
SK Signature:
Old code:
const wallet = fromSecretKey(process.env.BLOCKLET_APP_SK);
const signature = wallet.sign();New Code:
const { getWallet } = require('@blocklet/sdk/lib/wallet');
const wallet = await getWallet();
const signature = await wallet.sign();JWT Signature:
Old:
const jwtToken = jwt.sign(payload, process.env.BLOCKLET_APP_SK);New:
const { getWallet } = require('@blocklet/sdk/lib/wallet');
const wallet = await getWallet();
const jwtToken = await wallet.signJWT(payload);SK Verification:
Previous:
const wallet = fromSecretKey(process.env.BLOCKLET_APP_SK);
const isValid = wallet.verify(data);New:
const wallet = fromSecretKey(process.env.BLOCKLET_APP_PK);
const isValid = wallet.verify(data);fromAppDid:
Old:
// 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:
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 beawaited. - If your code relies on
wallet.secretKeyfor signing or derivation, please consult the modifications detailed under 1. Environment Variable Changes.
Example:
Old:
const { getWallet } = require('@blocklet/sdk/lib/wallet');
const wallet = getWallet();
const sig = wallet.sign(data);
console.log(wallet.secretKey) // sk....New:
const { getWallet } = require('@blocklet/sdk/lib/wallet');
const wallet = await getWallet();
const sig = await wallet.sign(data);
console.log(wallet.secretKey) // undefined3. 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:
- 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 requiresawait. - @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:
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)
- From the repository root, run the upgrade command and install dependencies. Create a separate branch.
- Perform a global search-and-replace operation to update import methods, transitioning from CommonJS default exports to named exports.
- A comprehensive search for usages such as
getWallet(),fromSecretKey(), andfromAppDid().
- Usage of private key attributes (secretKey / sk) must be replaced with
getWallet()for signing, or withgetAccessWallet()combined withappSk. - Change all sign() / verify() / getSignData() and similar calls to use await.
- Usage of private key attributes (secretKey / sk) must be replaced with
- For calls spanning components, services, notifications, and WebSockets, ensure to use
getAccessWallet()and passappSkto the signing method. - After modification, run TypeScript/Node builds and unit/integration tests to identify any missing synchronous calls or import errors.
- 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.