This is the public source mirror behind Kaspa Forge — Safe + Escrow + Market + Desk, built on Kaspa Toccata covenants.
- Safe — a vault for KAS. Every withdrawal waits out a delay you set and can be cancelled with a separate alarm key.
- Escrow — non-custodial escrow for P2P deals.
- Market — a marketplace powered by Kaspa payments and escrow.
- Desk — the browser workspace for the wallet, safes, deals, listings and opt-in encrypted profile sync.
The current backup format is one passphrase-encrypted Desk profile (.age). It replaces the old
per-vault and per-deal text recovery sheets. Keep the .age file, its password, and any separate
Safe alarm cards offline.
This repository contains:
web/keyfile-decrypt.html— a self-contained offline decryptor for the Desk.agebackup. Its WebAssembly core is embedded in the HTML; it loads no scripts, fonts or code from the network.vaultctl/— the complete standalone Safe recovery CLI. It can inspect and operate a vault against any Kaspa v2+ node with--utxoindex, without the Kaspa Forge website or API.contracts/vault.silandcontracts/escrow.sil— the on-chain covenant sources.web/andapp/— the browser frontend and Android wrapper source.RECOVERY-SHA256SUMS— checksums generated from the exact recovery kit in this revision.
Recovery capability is intentionally stated narrowly: Safe has a standalone recovery CLI.
For Escrow, the covenant and browser frontend are public, but a separately packaged party-side
recovery CLI is not published yet. Do not rely on a cached or third-party escrowctl binary.
- Use Code → Download ZIP on this repository and store the ZIP with your encrypted
.agebackup. Do not wait for an emergency. - Extract the ZIP and open
web/keyfile-decrypt.htmlfrom disk once with networking disabled. - Confirm that it accepts a test/exported
.agebackup and offers Download decrypted JSON. - Delete the plaintext JSON after the rehearsal. It contains private keys.
The key file is standard passphrase-encrypted age data. Technical users may also decrypt it with:
age -d kaspa-office-profile.age > profile.jsonDisconnect the computer from the network, open web/keyfile-decrypt.html, choose the .age file,
enter its password, and click Download decrypted JSON. The result is profile.json.
The decryptor is deliberately a single local HTML file. The password, encrypted backup and
plaintext profile stay in that browser window. Never upload the .age file or plaintext JSON to
an online decryptor or send either file to support.
List the vault addresses, then select the record whose vault_addr matches your vault:
jq -r '.vaults[] | .vault_addr' profile.json
jq '.vaults[] | select(.vault_addr == "kaspa:YOUR_VAULT_ADDRESS")' profile.json > vault.json
chmod 600 profile.json vault.jsonThe normal recovery input is now vault.json. A full .age export from the Safe's creation
device includes alarm_sk only when you chose shared alarm-key storage. Forge Sync deliberately
never transfers alarm keys. For separate storage, add the private key from the alarm card as the
alarm_sk field before cancel or migrate. Keep hot and alarm keys apart: together they can
move the whole vault immediately.
Prerequisites: Rust, protobuf-compiler, and clang.
# Debian/Ubuntu
sudo apt install -y protobuf-compiler clang
cd vaultctl
cargo build --release
./target/release/vaultctl status --recovery ../vault.jsonvaultctl defaults to grpc://node.kaspaforge.org:16110. To remove that remaining service
dependency, run your own Kaspa v2+ node with --utxoindex and add
--node grpc://YOUR_NODE:16110.
| command | what it does | required secret |
|---|---|---|
status --recovery vault.json [--dest <addr>] |
show balance, vault age and timers; --dest checks a known in-flight withdrawal |
none |
initiate --recovery vault.json --to <kaspa:q…> |
start a delayed withdrawal; the destination becomes immutable | hot key |
cancel --recovery vault.json --dest <kaspa:q…> |
cancel an in-flight withdrawal and return coins to the vault | alarm key |
complete --recovery vault.json --dest <kaspa:q…> |
deliver a matured withdrawal to its fixed destination | none |
checkin --recovery vault.json |
reset the inheritance timer | hot key |
inherit --recovery vault.json [--heir-sk <hex>] |
claim after the inheritance period; automatic mode needs no key, manual mode needs the heir's key | none / heir key |
migrate --recovery vault.json --to <kaspa:q…> [--dest <addr>] |
instantly move the entire vault; --dest targets an in-flight withdrawal UTXO |
hot + alarm keys |
Useful flags:
--dry-run— build and sign the transaction but do not broadcast it. Use this first.--node grpc://host:16110— use any Kaspa v2+ node started with--utxoindex.
For cancel, complete, or an in-flight migrate, --dest is the withdrawal destination,
not the vault address. It is shown in the Safe panel/alert; status --dest ... verifies it.
Delete plaintext recovery files when finished:
rm -f profile.json vault.jsoncd vaultctl
cargo run --release -- selftestThe self-test executes all contract paths, including early completion, wrong-key, wrong-destination, premature-inheritance, one-key-migration and two-input-siphon attacks, in the Kaspa consensus VM.
contracts/escrow.sil is the covenant behind Kaspa Escrow. Every allowed
path sends funds only to the buyer, seller, their split, or the fixed service-fee address:
release/refund— amicable outcomes;autoRelease— seller payout after the dispute window when no dispute was opened;dispute— freezes optimistic auto-release;arbitrateToBuyer,arbitrateToSeller,arbitrateSplit— constrained arbiter outcomes;timeoutToBuyer,timeoutToSeller— emergency exits after the arbiter deadline.
The encrypted Desk profile contains the deal key, chat key, service token and public escrow data;
the offline decryptor exposes those fields. While the Kaspa Forge site is available, importing the
.age file into Desk restores the deal UI. Without the site, you can still inspect the escrow
address in any Kaspa explorer and audit the published covenant. A standalone party-side CLI for
release/refund/dispute is not part of this repository today; the timeout paths remain enforced
by the covenant, but this README does not claim an unpublished recovery tool.
- Keys are generated in the browser and encrypted in the Desk
.agebackup. Kaspa Forge does not know the profile password. - Forge Sync stores an encrypted profile projection and deliberately strips every Safe
alarm_sk. Sync is not a replacement for a fresh full.ageexport or a separate alarm card. - A Safe address is a pure function of its parameters.
vaultctl statusrecomputes it and reports a mismatch rather than operating on a different vault. - Never send support your password, private keys, complete
.agebackup,profile.json, orvault.json.
app/ is a Tauri 2 wrapper around the public web/ frontend. No prebuilt Android package is
currently distributed: old APK releases were retired because they no longer represent the current
product. Do not install an old package from a cache or third-party mirror.
cd app
npm install
cp -r ../web web
npx tauri android init
npx tauri icon app-icon.png
npx tauri android build --apk