Catalog npm
PACKAGE EXPLORERView in registry

@0xsend/cws-cli

npm

End-user Canton wallet CLI (TUI + scripted)

Versions collected11
Direct dependencies18From release 0.6.2
Last observed21 Oct, 09:44 UTC

README

Current package documentation

@0xsend/cws-cli

End-user Canton wallet CLI (cws) with both an interactive TUI and a scripted subcommand mode. Ships an encrypted keystore, profile-based multi-identity support, and mainnet typed-token confirmation for every mutation.

Install

npm install -g @0xsend/cws-cli
# or: pnpm add -g, yarn global add, bun install -g
cws --help

Requires Node.js 20 or newer. The published bundle is fully self-contained — no extra @0xsend/* packages need to be installed alongside.

First run

Create a profile. The wizard walks you through the choices:

cws profile create work

The prompts in order:

  1. Network — mainnet | testnet | localnet. Selects the Canton deployment and (for mainnet/testnet) the default cws-api base URL and Keycloak realm. Localnet requires you to supply baseUrl and keycloak.url explicitly.
  2. API key — paste the cws-api key for the target realm. Stored inside the encrypted keystore, never on disk in plaintext.
  3. Signing key source — one of:
    • generate — new P-256 keypair (default; secp256k1 also supported), private key held only in the keystore.
    • import — read a PEM/DER file from disk.
    • paste — paste hex/base64 material at the prompt.
  4. Password — encrypts the keystore with Argon2id + AES-256-GCM. You will be asked for it every time a command needs to sign (no session cache in v1).

Profiles live under $WALLET_CONFIG (default ~/.config/wallet-cli).

Commands

cws
├── health                                    check cws-api connectivity
├── profile
│   ├── create <name>
│   ├── list
│   ├── import <name> --from <path> --network <mainnet|testnet|localnet>
│   └── remove <name>
├── multisig
│   ├── list                                  list multisigs visible to the active profile
│   ├── show <id>                             show multisig details
│   ├── pending                               list pending items across all visible multisigs
│   ├── create                                create a new multisig
│   ├── queue                                 propose new multisig transactions (prepare + queue + self-sign)
│   │   ├── send <multisigPartyId> <to> <amount> <instrument> [--description <text>]
│   │   ├── accept <transferInstructionId> --multisig <partyId>
│   │   ├── preapproval grant <multisigPartyId> <providerPartyId> [--expires-at <ms>]
│   │   ├── preapproval revoke <multisigPartyId> <preapprovalContractId>
│   │   ├── automerge enable <multisigPartyId> --provider <partyId> --instrument <id>
│   │   ├── automerge disable <multisigPartyId> <delegationContractId>
│   │   └── automerge revoke <multisigPartyId> <delegationContractId>
│   ├── transaction                           operate on existing queued multisig transactions
│   │   ├── list --multisig <partyId> --queue [--status <status>] [--limit <n>]
│   │   ├── list --multisig <partyId> --offers [--direction <incoming|outgoing|all>]
│   │   ├── show <transactionId>
│   │   ├── history --multisig <partyId> [--status <status>] [--limit <n>]
│   │   ├── approve <transactionId>
│   │   ├── reject <transactionId>
│   │   ├── cancel <transactionId>
│   │   └── submit <transactionId>            re-submit a fully-signed queued transaction
│   └── topology
│       ├── list --multisig <id>
│       ├── show <id> --multisig <multisigId>
│       ├── propose add-member <multisigPartyId> <party>
│       ├── propose remove-member <multisigPartyId> <party>
│       ├── propose set-threshold <multisigPartyId> <from> <to>
│       ├── approve <id> --multisig <multisigId>
│       └── reject <id> --multisig <multisigId>
├── transaction                               single-signer (1/1) operations — partyId derived from keystore
│   ├── list --offers [--direction <incoming|outgoing|all>]
│   ├── send <to> <amount> <instrument> [--description <text>]
│   ├── accept <transferInstructionId>
│   ├── preapproval grant <providerPartyId> [--expires-at <ms>]
│   ├── preapproval revoke <preapprovalContractId>
│   ├── automerge enable --provider <partyId> --instrument <id>
│   ├── automerge disable <delegationContractId>
│   └── automerge revoke <delegationContractId>
├── instruments
│   └── list [--json]                         list available instruments
└── holdings
    └── list <partyId> [--instrument <id>] [--json]   list holdings for a party

All commands accept --json for machine-readable output and --profile <name> to override WALLET_PROFILE.

Multisig vs single-signer

multisig queue * commands take an explicit <multisigPartyId> and route the prepared transaction through the queue (threshold signers approve, then multisig transaction submit or auto-submit when threshold is met).

transaction * commands are for 1/1 wallets. The party id is derived automatically from the active profile's keystore — no party argument is needed. The cws-api submits the transaction directly upon signing.

Health

cws health

Profile

cws profile list
cws profile create <name>
cws profile import <name> --from <path> --network <mainnet|testnet|localnet>
cws profile remove <name>

Multisig — party-level

cws multisig list
cws multisig show <id>
cws multisig pending
cws multisig create

Multisig — propose new queued tx (queue)

cws multisig queue send <multisigPartyId> <to> <amount> <instrument> [--description <text>]
cws multisig queue accept <transferInstructionId> --multisig <partyId>
cws multisig queue preapproval grant <multisigPartyId> <providerPartyId> [--expires-at <ms>]
cws multisig queue preapproval revoke <multisigPartyId> <preapprovalContractId>
cws multisig queue automerge enable <multisigPartyId> --provider <partyId> --instrument <id>
cws multisig queue automerge disable <multisigPartyId> <delegationContractId>
cws multisig queue automerge revoke <multisigPartyId> <delegationContractId>

Multisig — act on queued tx (transaction)

list requires exactly one of --queue (queued multisig transactions) or --offers (incoming TransferInstructions). Pass --direction to filter offers by incoming (default), outgoing, or all.

cws multisig transaction list --multisig <partyId> --queue [--status <status>] [--limit <n>]
cws multisig transaction list --multisig <partyId> --offers [--direction <incoming|outgoing|all>]
cws multisig transaction show <transactionId>
cws multisig transaction history --multisig <partyId> [--status <status>] [--limit <n>]
cws multisig transaction approve <transactionId>
cws multisig transaction reject <transactionId>
cws multisig transaction cancel <transactionId>
cws multisig transaction submit <transactionId>   # re-submit a fully-signed queued transaction

Multisig — topology

cws multisig topology list --multisig <id>
cws multisig topology show <id> --multisig <multisigId>
cws multisig topology propose add-member <multisigPartyId> <party>
cws multisig topology propose remove-member <multisigPartyId> <party>
cws multisig topology propose set-threshold <multisigPartyId> <from> <to>
cws multisig topology approve <id> --multisig <multisigId>
cws multisig topology reject <id> --multisig <multisigId>

Single-signer transaction (1/1 wallets)

list --offers discovers TransferInstruction contract ids you can act on with accept / reject / withdraw. Default direction is incoming.

cws transaction list --offers [--direction <incoming|outgoing|all>]
cws transaction send <to> <amount> <instrument> [--description <text>]
cws transaction accept <transferInstructionId>
cws transaction preapproval grant <providerPartyId> [--expires-at <ms>]
cws transaction preapproval revoke <preapprovalContractId>
cws transaction automerge enable --provider <partyId> --instrument <id>
cws transaction automerge disable <delegationContractId>
cws transaction automerge revoke <delegationContractId>

Instruments

cws instruments list [--json]   # list available instruments across the active network

Holdings

cws holdings list <partyId> [--instrument <id>] [--json]   # list holdings for a party

Environment variables

  • WALLET_CONFIG — root directory for profiles (default ~/.config/wallet-cli).
  • WALLET_PROFILE — profile name used when --profile is not passed.
  • WALLET_PASSWORD — keystore password. Security warning: any process on the host can read the environment of another process owned by the same user; prefer the interactive prompt unless you are in a controlled non-interactive setting (CI runners with secret-backed env, etc.).

cws profile onboard <name> additionally reads the following when set, skipping the matching inquirer prompt:

  • WALLET_NETWORK — localnet | testnet | mainnet.
  • WALLET_USERNAME — Keycloak username the profile will authenticate as.
  • WALLET_ONBOARDING_SECRET — one-shot onboarding secret minted by an admin via POST /onboarding-secrets.
  • --yes additionally skips the prepared-submission confirm so the flow runs end-to-end without a TTY. The same security caveat as WALLET_PASSWORD applies; intended for test scripts and CI.

--password <value> is accepted on the argv but prints a warning on every invocation because ps(1) leaks argv to other users on multi-user hosts.

Security model

  • Single encrypted keystore per profile. The keystore envelope contains both the cws-api API key and the signing-key material (P-256 by default; secp256k1 supported).
  • KDF: Argon2id with per-profile salt. Cipher: AES-256-GCM (authenticated encryption; tampering fails decryption instead of yielding silent garbage).
  • Interactive mainnet mutations require typed-token confirmation: the operator types the first 6 characters of the multisig party id. --yes short-circuits typed-token confirmation on every network, including mainnet — intended for CI and headless automation. The same security caveat as WALLET_PASSWORD applies (env/argv visibility).
  • --password on argv emits a warning. Argv is visible to ps.
  • No session cache in v1. Every command that signs re-prompts for the password.
  • Password zeroing is best-effort: JavaScript strings are immutable, so only Buffer copies of the password inside keystore.ts are actively zeroed.

Exit codes

Categories map to exit codes via EXIT_CODES in src/core/output.ts:

CategoryCode
unknown1
auth2
cancelled0
config3
io4
network5
safety6
signing7
validation8

cancelled deliberately exits 0 so shell pipelines treat user-aborted confirmations as a successful no-op; the reason still surfaces in stderr.

Troubleshooting

  • Unlock fails with decryption error — wrong password. There is no recovery path; if the password is lost, remove the profile and re-import the signing key.
  • Keycloak 401 / 403 — API key expired or bound to the wrong realm. Re-provision from the cws-api admin and cws profile import a refreshed config, or edit the profile's encrypted keystore via the profile create flow.
  • Localnet cannot reach cws-api — localnet profiles require both baseUrl and keycloak.url to be set explicitly; the defaults only cover mainnet and testnet. Check the values with cws profile list --json.

TUI tour

cws (no args) or cws ui launches the terminal UI:

profile-picker  →  unlock  →  primary dashboard  ⇄  account picker  ⇄  multisig dashboard

After unlock, the TUI lands on the active profile's primary wallet account and attempts to show holdings inline. Multisigs are available from the account picker and are treated as switchable first-party accounts rather than a separate mode. If cws-api cannot load holdings because its Ledger API dependency is unavailable, the dashboard stays usable and explains the limitation. Press H for the full holdings view.

Primary-account dashboard hotkeys:

  • s — send transfer
  • A — accept transfer
  • P — manage transfer preapprovals
  • M — manage automerge delegations
  • i — browse instruments
  • H — open full holdings view
  • x / Esc — switch accounts
  • q — quit (suppressed while a text field has focus; Ctrl-C always quits)

Multisig dashboard hotkeys:

  • s — send transfer proposal
  • p — propose topology change
  • a — approve pending item
  • r — reject pending item
  • i — browse instruments
  • H — open full holdings view
  • x / Esc — switch accounts
  • q — quit (suppressed while a text field has focus; Ctrl-C always quits)

Mainnet safety: in interactive use, before any mutation submits, the TUI asks you to type the first 6 characters of the multisig party id. --yes short-circuits this prompt on every network — automation paths are responsible for supplying it deliberately.

License

MIT

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 11

Collected releases
VersionPublishedDirect dependenciesOpen version
0.6.2Registry latest12 Oct 202618
0.6.13 Sept 202618
0.6.022 Aug 202618
0.5.017 Jul 202618
0.4.129 May 202618
0.4.027 Apr 202618
0.3.026 Apr 202618
0.2.026 Apr 202618
0.1.326 Apr 202618
0.1.226 Apr 202618
0.1.025 Apr 202618

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
0xsend/canton-monorepo

Shared repository data · GitHub

License detected in repository
Not collected

Repository

PENDING
canton-foundation/canton-monorepo

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 21 Oct, 09:44 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:6feb364ec269e269391d6b8f2e21527e7d1e32cbcfd9aa056ee5e096bbe23bcf