aplanesdk

中文文档
0.47.0发布于 2026/11/03MITTypeScriptESM + CJS构建来源已验证

通过apsigner签署Algorand事务的TypeScript SDK

中文使用文档
文档目录25 节
  1. 版本控制
  2. 安装
  3. 从本地Tarball安装
  4. 故障排除
  5. 快速入门
  6. 的连接方法
  7. 通过SSH远程连接
  8. 环境相关连接
  9. 身份验证
  10. API参考
  11. SignerClient
  12. AlgoKit Utils适配器
  13. 支持的密钥类型
  14. 共同签署人和保密账户
  15. 有限的联名人账户
  16. 错误处理
  17. 签署例外
  18. 提交异常
  19. 示例:完整的工作流
  20. LogicSig资源规划
  21. 如何工作(服务器端)
  22. 示例:Falcon-1024 Key
  23. 费用的影响
  24. 许可证
  25. 项目

APlane TypeScript SDK

通过apsigner签署Algorand事务的TypeScript SDK。

版本控制

只有当SDK发生变化时才会发布SDK包。SDK版本跟踪 兼容APlane发布标签,并可能跳过产品发布号。

原生猎鹰SDK发布线之后需要一个APlane签名者发布 v0.35.0;看到 docs/COMPATIBILITY.md.

安装

npm install aplanesdk algosdk

对于可选的AlgoKit适配器示例/客户端助手,安装当前的 AlgoKit Utils TypeScript v10测试版。适配器在10.0.0-beta.4上进行了测试:

npm install "@algorandfoundation/algokit-utils@^10.0.0-beta.4"

或者使用yarn/pnpm:

yarn add aplanesdk algosdk
pnpm add aplanesdk algosdk

从本地Tarball安装

从本地安装时 .tgz 首先构建并打包SDK,然后将生成的tarball安装到您的项目中:

# From typescript/
npm install
npm pack

# In your consuming project
npm init -y   # if needed
npm install ../path/to/aplanesdk-<version>.tgz algosdk

故障排除

同伴依赖冲突如果看到对等依赖错误,请尝试:

npm install aplanesdk algosdk --legacy-peer-deps

快速入门

import { SignerClient, sendRawTransaction } from "aplanesdk";
import algosdk from "algosdk";

// Connect to signer
const client = await SignerClient.fromEnv();

// Build transaction with algosdk
const algodClient = new algosdk.Algodv2("", "https://testnet-api.4160.nodely.dev", "");
const params = await algodClient.getTransactionParams().do();

const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
  sender: "SENDER_ADDRESS",
  receiver: "RECEIVER_ADDRESS",
  amount: 1000000, // 1 ALGO
  suggestedParams: params,
});

// Sign via apsigner (waits for operator approval)
const signed = await client.signTransaction(txn);

// Submit to network (signed is ready to use, no processing needed)
const txid = await sendRawTransaction(algodClient, signed);
console.log(`Submitted: ${txid}`);

的连接方法

所有SDK连接都使用配置的支持ssh的签名者路径。SDK模式不支持本地HTTP直接连接。

通过SSH远程连接

通过SSH隧道连接到远程机器上的指定程序,并进行身份验证 通过注册的客户端密钥:

const client = await SignerClient.connectSsh(
  "signer.example.com",
  "~/aplane/apclient/.ssh/id_ed25519",
  {
    sshPort: 1127,           // default: 1127
    timeout: 30000,          // optional explicit shorter request timeout
    sshSetupTimeout: 60000,  // TCP dial + SSH authentication only
    knownHostsPath: "~/aplane/apclient/.ssh/known_hosts",
  }
);

SSH设置使用自己的60秒截止日期,独立于HTTP请求和 操作员批准超时。通过 signal 取消安装。注册了 同样的控制 setupTimeout 和 signal.

注意: SSH使用固定的非保密用户名 aplane 和公共密钥 身份验证;注册的密钥是客户端的唯一凭证。一个关键 未注册的签名者握手失败。完成后记得关闭:

await client.close();

环境相关连接

从数据目录加载配置。该目录是必需的- pass dataDir 或者设置 APCLIENT_DATA 环境变量:

// Set environment variable
// export APCLIENT_DATA=~/aplane/apclient

const client = await SignerClient.fromEnv();

// Or pass directly
const client = await SignerClient.fromEnv({ dataDir: "~/aplane/apclient" });

数据目录结构(安装程序默认: ~/aplane/apclient):

<data_dir>/
  config.yaml          # Non-routing client settings
  endpoints.yaml       # Signer and cosigner routing
  .ssh/
    id_ed25519         # SSH key: the client's credential
    known_hosts        # Trusted signer host keys

例子 endpoints.yaml (通过SSH远程):

schema_version: 2
default: primary
endpoints:
  primary:
    role: signer
    url: ssh://signer.example.com:1127
    identity_file: .ssh/id_ed25519
    known_hosts_path: .ssh/known_hosts

通过 trustOnFirstUse: true 来 SignerClient.fromEnv 只有当呼叫可能 信任并保存未知主机密钥。使用 endpoint: "cosigner.qa" 选择 命名的端点。

身份验证

客户端的SSH密钥是它的凭据。新密钥只注册一次 请求排队等待操作人员稍后批准 apadmin (pending: true(正常结果)或者钥匙已经注册了,但什么都没有 存储在客户端。操作员批准后连接:

import { requestEnrollmentFromEnv } from "aplanesdk";

const { fingerprint, pending } = await requestEnrollmentFromEnv({ label: "ci-runner" });
if (pending) console.log(`key ${fingerprint} queued; have the operator approve it in apadmin`);

requestEnrollment(host, sshKeyPath, { knownHostsPath, label }) 是 显式的形式。同样的注册可以从 apshell 与 enroll.

API参考

SignerClient

health(): Promise<boolean>

检查签名者是否可以联系到。

if (await client.health()) {
  console.log("Signer is online");
}

getStatus(): Promise<StatusResponse>

获取已验证的签名者状态。这在签名者被锁定时有效。

const status = await client.getStatus();
console.log(status.state, status.keysetRevision, status.warnings);

keysetRevision 流程是本地的,对于决定何时刷新有用吗 listKeys(true);它在重新启动指定程序时不是持久的。 approvalWaitSeconds 是由SDK使用的大小 /sign 最后期限。显示 非空 warnings 运营商;他们报告了持续的健康状况 这需要关注。

listKeys(refresh?: boolean): Promise<KeyInfo[]>

列出可用的签名密钥。

const keys = await client.listKeys();
for (const key of keys) {
  console.log(`${key.address} [${key.keyType}]`);
}

返回的列表 KeyInfo:

  • address: Algorand地址
  • keyType: “ed25519”, “aplane.falcon1024. ”v1”、“aplane.htlc。v1”等。
  • logicSigResources:独立的程序字节,参数字节,和 按授权路径划分的最大操作码成本需求。
  • isGenericLsig:如果不需要加密签名,则为True
  • signingArgs:名单 SigningArg 对于LogicSigs

SDK只公开有限的库存和普通支出签名。事实并非如此 构建、部分签署或完成合同管理密钥交易;使用 APlane aprekey 这些操作的工作流。

发现通用logicsig所需的参数:

const keyInfo = await client.getKeyInfo(hashlockAddress);
if (keyInfo?.signingArgs) {
  for (const arg of keyInfo.signingArgs) {
    console.log(`${arg.name}: ${arg.type} - ${arg.description}`);
  }
}

signTransaction(txn, authAddress?, lsigArgs?): Promise<string>

签署单个事务。返回准备提交的base64编码字符串。

服务器通过根据需要添加虚拟事务来自动处理大型logicsig(例如Falcon-1024)的费用池。

// Basic signing (uses txn.sender as authAddress)
const signed = await client.signTransaction(txn);

// Rekeyed account (different auth key)
const signed = await client.signTransaction(txn, "SIGNER_KEY_ADDRESS");

// Generic LogicSig with runtime args (e.g., HTLC)
const signed = await client.signTransaction(
  txn,
  "HASHLOCK_ADDRESS",
  { preimage: new Uint8Array([/* secret value */]) }
);

// Submit directly (no processing needed)
const txid = await sendRawTransaction(algodClient, signed);

signTransactions(txns, authAddresses?, lsigArgsMap?): Promise<string>

将多个事务作为一个组进行签名。返回一个base64编码的串接签名事务字符串,准备提交。

重要的:不预先分配组id。服务器在为大型logicsig添加所需的虚拟事务后计算组ID。

// Build transactions (do NOT call assignGroupId)
const txn1 = algosdk.makePaymentTxnWithSuggestedParamsFromObject({...});
const txn2 = algosdk.makePaymentTxnWithSuggestedParamsFromObject({...});

// Sign group (server handles grouping and dummies)
const signed = await client.signTransactions([txn1, txn2]);

// Submit directly (no processing needed)
const response = await algodClient.sendRawTransaction(Buffer.from(signed, "base64")).do();

signTransactionsList(txns, authAddresses?, lsigArgsMap?): Promise<string[]>

就像 signTransactions() 但是返回单个base64编码的事务,而不是串联的事务。当您需要单独检查事务时非常有用。

const signedList = await client.signTransactionsList([txn1, txn2]);
// signedList is string[], each element is a base64-encoded signed transaction

signRequests(requests, options?): Promise<GroupSignResponse>

发送一个或多个raw /sign 请求条目。在集成时使用这个 已经拥有事务编码,并希望APlane的本地响应形状。

const response = await client.signRequests(
  [{
    txn_bytes_hex: "5458...",
    auth_address: "SIGNER_KEY_ADDRESS",
    txn_sender: "SENDER_ADDRESS", // advisory display hint only
  }],
  { requestId: "app-owned-request-id" },
);

AlgoKit Utils适配器

对于AlgoKit Utils TypeScript v10 beta版的事务签名,请使用适配器 帐户。它是针对10.0.0-beta测试的。4、连接AlgoKit客户端到 APlane的事务签名功能,并给出了 addr + signer(txnGroup, indexesToSign) 形状。

最小的存储库示例是 examples/algokit_self_send.ts. 从一个 签出时安装了依赖项并构建了SDK:

cd ~/aplanesdk/typescript
npm install
npm install --no-save "@algorandfoundation/algokit-utils@^10.0.0-beta.4"
npm run build
export APCLIENT_DATA=~/aplane/apclient
export APLANE_ADDRESS=SENDER_ADDRESS
node --import tsx examples/algokit_self_send.ts

该示例使用AlgoKit构建一个事务,并通过APlane对其进行签名 适配器,然后提交签名blob与AlgoKit的algod客户端:

import { AlgorandClient, microAlgo } from "@algorandfoundation/algokit-utils";
import { SignerClient, createApsignerAccount } from "aplanesdk";

const sender = "SENDER_ADDRESS";
const algorand = AlgorandClient.testNet();
const signer = await SignerClient.fromEnv();

const info = await algorand.account.getInformation(sender);
const account = createApsignerAccount({
  client: signer,
  address: sender,
  authAddress: info.authAddr?.toString() ?? sender,
});
const txn = await algorand.createTransaction.payment({
  sender,
  signer: account,
  receiver: sender,
  amount: microAlgo(0),
  validityWindow: 1000,
});
const signed = await account.signer([txn], [0]);
const txId = (await algorand.client.algod.sendRawTransaction(signed)).txId;

使用 createTransaction.* 当APlane必须拥有最终签名和任何 aplane管理的集团扩张。 algorand.send.* 拥有编写器发送路径 在那条路上有标志。

签名呼叫发现 /status.approval_wait_seconds 然后使用这个值 加上30秒的请求超时。如果发现失败了,还是老的 签名者省略该字段,签名者将返回到6分钟。明确的更短的 超时仍然是赢家;SDK /sign 呼叫包括 request_id 然后发送一个 的最优 /sign/cancel HTTP请求超时或断开连接。 高级签名方法接受 requestId 和 signal 选项 需要用户发起取消的应用程序。

cancelSignRequest(requestId): Promise<CancelSignResponse>

要求指派者取消正在进行的同步 /sign 按请求ID请求。 成功的响应对于客户端行为和返回状态是幂等的 "canceled" 或者 "not_found".

TypeScript高级签名默认会生成一个请求ID。互动 应用程序可以传递应用程序拥有的ID和 AbortSignal;流产的 signal终止HTTP请求并发送best-effort /sign/cancel 与 相同的ID:

const controller = new AbortController();
const requestId = "wallet-ui-approval-123";
const signed = await client.signTransaction(txn, undefined, undefined, {
  requestId,
  signal: controller.signal,
});

// elsewhere, if the user aborts while approval is pending:
controller.abort();

支持的密钥类型

密钥类型描述笔记
ed25519本地Algorand键标准签署
aplane.falcon1024.v1Post-quantum LogicSigLogicSig中的签名。Args [0]
aplane.ed25519.v1Ed25519 DSA LogicSig库可见的普通DSA帐户
aplane.witness-falcon1024.v1见证关键共同签名者保管的策略签名密钥;不是支出账户
aplane.falcon1024-cosigner1024.v1保护账户需要用户和共同签名者组件签名
aplane.corridor.v1有界走廊帐户bounded1 合同; bounded-cosigner1 花流
aplane.falcon1024-allowlist.v1有界allowlist内联allowlist; bounded1 签字流程
aplane.falcon1024-allowlist.v2有界allowlistMerkle allowlist; bounded1 签字流程
aplane.falcon1024-timelock.v1有界timelockRound-gated bounded1 签字流程
aplane.falcon1024-allowlist-alock.v1Rekey-locked bounded allowlist一般开支用途 bounded1;admin rekey不在SDK范围内
aplane.htlc.v1Hash-locked基金需要 preimage arg(检查 signingArgs)

服务器组装完整的签名事务—SDK返回一个准备提交的base64字符串。

共同签署人和保密账户

注册为共同签名者的见证密钥是公共策略签名选择器,而不是Algorand 支出账户。不要将它们用作发送者、接收者、验证地址或 再续键的目标。保护帐户密钥必须通过保护流程进行签名。

低级端点包装器可用:

const userPart = await userClient.requestComponents({
  group_bytes_hex: ["5458..."],
  targets: [{
    target_index: 0,
    kind: "user",
    auth_address: "GUARDED_ACCOUNT_ADDRESS",
  }],
});

const cosignerPart = await cosignerClient.requestComponents({
  group_bytes_hex: ["5458..."],
  targets: [{
    target_index: 0,
    kind: "cosigner",
    component_key: "COSIGNER_COMPONENT_SELECTOR",
  }],
});

const assembled = await userClient.requestAssemble({
  group_bytes_hex: ["5458..."],
  targets: [{
    target_index: 0,
    kind: "guarded",
    auth_address: "GUARDED_ACCOUNT_ADDRESS",
    user_signature: userPart.components[0].signature!,
    cosigner_signature: cosignerPart.components[0].signature!,
  }],
});

对于常见的显式双客户机流,使用 signGuardedGroup. 直接 Helper不执行库存发现,因此通过审查的支出路径 返回的资源配置文件 listKeys():

const result = await signGuardedGroup({
  userClient,
  cosignerClient,
  cosignerComponentKey: "COSIGNER_COMPONENT_SELECTOR",
  groupBytesHex: ["5458..."],
  guardedTargets: [
    {
      targetIndex: 0,
      guardedAccount: "GUARDED_ACCOUNT_ADDRESS",
      logicSigResources: reviewedSpendResources,
    },
  ],
});
const signedGroup = result.signedGroup;

assembleGroup() 保留本地多方连接助手;事实并非如此 与服务器端保护程序集相同的操作。

有限的联名人账户

走廊使用有界契约 bounded1 与不同的 bounded-cosigner1 在线签名流程。合同标识LogicSig 规则;该流标识用户优先的多端点编排。这个 Prepared helper从签名者目录中检测该流并对其进行路由 自动:

const result = await signPreparedGuardedGroup({
  userClient,
  cosignerResolver,
  preparedGroup,
});
const signedGroup = result.signedGroup;

SDK首先冻结整个规范组 /plan;用户 签名者然后批准这些字节通过 requestComponents() 与 kind: "bounded-base". 然后,它在普通的位置上签字,而且只有在签字之后 用户端是完整的,它请求寄售人签名相同 字节和调用 requestAssemble(). 之前 签署任何东西,SDK比较 签名者制作的计划与呼叫者的准备组:只报告费用 接受池和组id分配,并且附加的位置必须是 规范预算假人。返回的组必须使用规范事务 编码和从呈现的成员关系重新计算的组ID。SDK也是 控件验证普通签名位置和每个组装的事务 冻结的事务字节。

requestComponents() 发送感伤。 /sign/cancel 当它 承载审批的请求被中止、超时或断开连接。 requestAssemble() 不开审批请求,是不是一个 取消处理。

签名者规划器拥有费用选择、授权资源大小等 报告了两种保护流的群体突变。

拥有编排的应用程序可以调用 requestComponents() 和 requestAssemble() 直接。签名者授权仅在此使用 合同;有限的合约管理密钥仍然是外部的 aprekey 仪式 并且不是由SDK完成的。

错误处理

签署例外

import {
  SignerError,

## Project

This SDK is part of the APlane project:

- Repository: https://github.com/aplane-algo/aplanesdk
- SDK path: `typescript`

APlane is an open-source project stewarded by the APlane Project.

See the repository [README](https://github.com/aplane-algo/aplanesdk/blob/main/README.md) for project overview and alpha-status guidance, and [DISCLAIMER.md](https://github.com/aplane-algo/aplanesdk/blob/main/DISCLAIMER.md) for risk, liability, and usage information.
  AuthenticationError,
  SigningRejectedError,
  SignerUnavailableError,
  KeyNotFoundError,
} from "aplanesdk";

try {
  const signed = await client.signTransaction(txn);
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.log("Connection is not authenticated by an enrolled SSH key");
  } else if (error instanceof SigningRejectedError) {
    console.log("Operator rejected the request");
  } else if (error instanceof SignerUnavailableError) {
    console.log("Signer not reachable or locked");
  } else if (error instanceof KeyNotFoundError) {
    console.log("Key not found in signer");
  } else if (error instanceof SignerError) {
    console.log(`Signing failed: ${error.message}`);
  }
}

提交异常

sendRawTransaction() 将冗长的algod错误打包为干净的异常:

import {
  sendRawTransaction,
  TransactionRejectedError,
  LogicSigRejectedError,
  InsufficientFundsError,
  InvalidTransactionError,
} from "aplanesdk";

try {
  const txid = await sendRawTransaction(algodClient, signed);
} catch (error) {
  if (error instanceof LogicSigRejectedError) {
    console.log(`LogicSig failed: ${error.reason}`); // error.txid also available
  } else if (error instanceof InsufficientFundsError) {
    console.log(`Not enough funds: ${error.reason}`);
  } else if (error instanceof InvalidTransactionError) {
    console.log(`Invalid transaction: ${error.reason}`);
  } else if (error instanceof TransactionRejectedError) {
    console.log(`Rejected: ${error.reason}`);
  }
}

示例:完整的工作流

import { SignerClient, SignerError, sendRawTransaction } from "aplanesdk";
import algosdk from "algosdk";

async function main() {
  // Connect to the signer with the enrolled client key
  const client = await SignerClient.fromEnv();

  // List keys
  const keys = await client.listKeys();
  const sender = keys[0].address;
  console.log(`Using: ${sender}`);

  // Build transaction
  const algodClient = new algosdk.Algodv2("", "https://testnet-api.4160.nodely.dev", "");
  const params = await algodClient.getTransactionParams().do();

  const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
    sender: sender,
    receiver: sender,
    amount: 0,
    suggestedParams: params,
  });

  // Sign (will wait for operator approval)
  try {
    const signed = await client.signTransaction(txn);
    console.log("Signed!");

    // Submit directly (no processing needed)
    const txid = await sendRawTransaction(algodClient, signed);
    console.log(`TxID: ${txid}`);

    // Wait for confirmation
    const result = await algosdk.waitForConfirmation(algodClient, txid, 4);
    console.log(`Confirmed in round ${result.confirmedRound}`);
  } catch (error) {
    if (error instanceof SignerError) {
      console.log(`Failed: ${error.message}`);
    } else {
      throw error;
    }
  }
}

main().catch(console.error);

LogicSig资源规划

签名者将LogicSig程序字节、参数字节和操作码开销规划为 独立的共识资源。在v42下,多余的程序字节由 团费;只有当参数或操作码容量大时才添加假人 需要他们。

如何工作(服务器端)

  1. 服务器选择授权路径资源配置文件。
  2. 服务器解决参数和操作码容量,只添加规范假人 在需要时。
  3. 服务器增加了v42程序字节费用贡献。
  4. 服务器计算最终的组ID并签署所有事务。
  5. SDK返回准备提交的完整签名组。

示例:Falcon-1024 Key

const params = await algodClient.getTransactionParams().do();
const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
  sender: falconAddr,
  receiver: receiverAddr,
  amount: 1000000,
  suggestedParams: params,
});

// Server automatically adds dummies - just sign and submit
const signed = await client.signTransaction(txn);
const txid = await sendRawTransaction(algodClient, signed);

费用的影响

密钥类型LogicSig大小假人需要额外的费用
Ed25519000
猎鹰- 1024~ 30353~ 3000 uA

额外费用包括后量子安全所需的虚拟交易。

许可证

麻省理工学院

项目

这个SDK是APlane项目的一部分:

APlane是一个由APlane项目管理的开源项目。

请参阅存储库 自述 对于项目概述和alpha状态指导,以及 DISCLAIMER.md 风险、责任和使用信息。

版本与发布记录

aplanesdk 共发布 22 个版本,首次发布于 2026/05/26;过去 12 个月发布了 22 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.47.0latest2026/11/03今天
0.46.02026/11/021 天前
0.45.02026/10/304 天前
0.44.02026/10/2113 天前
0.43.02026/10/2014 天前
0.42.02026/10/1518 天前

关于 aplanesdk 的常见问题

aplanesdk 是什么?

通过apsigner签署Algorand事务的TypeScript SDK

如何安装 aplanesdk?

在项目目录运行 npm install aplanesdk,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:algosdk(^3.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

aplanesdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

aplanesdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

aplanesdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

aplanesdk 可以商用吗?

aplanesdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

aplanesdk 还在维护吗?

最新版本 0.47.0 发布于 2026/11/03(今天);至今共发布 22 个版本,其中过去 12 个月发布了 22 个;首次发布于 2026/05/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。