# arch-kit
`arch-kit` is a CLI for generating Arch keys, deploying programs, publishing
canonical on-chain IDLs, and inspecting APL tokens.
## Install
Install the published crate with Cargo:
```bash
cargo install arch-kit --locked
```
Install from this repository when developing arch-kit itself:
```bash
cargo install --path .
```
Cargo installs the binary to `$CARGO_HOME/bin` (usually `~/.cargo/bin`). If it
is not already available on your `PATH`, add this to your shell profile:
```bash
export PATH="$HOME/.cargo/bin:$PATH"
```
After pulling changes, update the installed binary with:
```bash
cargo install --path . --force
```
## GitHub deployment workflow
Store the program and authority key contents as `PROGRAM_KEY` and
`AUTHORITY_KEY` repository secrets. Both accept the same hex or SDK-compatible
JSON formats as local key files. A program repository can then build and deploy
with the reusable workflow:
```yaml
jobs:
deploy:
uses: Arch-Network/arch-kit/.github/workflows/deploy-program.yml@v0.1.5
with:
program-path: program
bitcoin-network: testnet
rpc-url: https://rpc.testnet.arch.network
fund-authority: true
secrets:
PROGRAM_KEY: ${{ secrets.PROGRAM_KEY }}
AUTHORITY_KEY: ${{ secrets.AUTHORITY_KEY }}
```
To select a GitHub deployment environment, add `environment` under `with`:
```yaml
with:
program-path: program
environment: production
```
The input defaults to an empty string, so omitting it runs without an
environment. When set, the deployment job uses that environment's protection
rules and secrets. Environment secrets take precedence over caller-provided
secrets with the same name; see [GitHub's reusable workflow documentation](https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows#using-inputs-and-secrets-in-a-reusable-workflow).
When `idl-path` is omitted, the workflow builds the Satellite IDL from the
program. Programs with manually maintained IDLs can bypass that step:
```yaml
with:
program-path: program
idl-path: idl/program.json
idl-size: 20000
```
The workflow builds the SBF ELF, checks node health, and deploys or upgrades
the program and canonical IDL. Faucet funding is disabled unless explicitly
enabled. Reusing the same two key secrets updates the existing deployment;
changing the program key creates a new deployment.
Set `expect-program-id` to the intended program ID (Base58 or 64-character hex)
to check that `PROGRAM_KEY` derives that ID before faucet funding or deployment.
It defaults to an empty string, which skips this check.
Set `allow-idl-resize: true` to opt into growing an existing populated canonical
IDL when the new IDL no longer fits, or to reserve more space with `idl-size`.
The input defaults to `false`, preserving the existing fixed-capacity behavior.
Growth also supports Satellite 0.31.5/0.31.6 handlers: the CLI backs up the IDL,
then clears, grows, and restores it within each resize transaction.
## Program development
Programs using Satellite require Rust nightly during the IDL-building step.
Install it with `rustup toolchain install nightly`; Satellite's
[IDL builder explicitly selects the nightly toolchain](https://docs.rs/arch-satellite-lang-idl/0.31.5/src/arch_satellite_lang_idl/build.rs.html#141-146).
## Commands
### Program development and deployment
| [`init`](#initialize-a-program) | `arch-kit init <PATH> --program-key <PATH>` | Initialize a new Satellite Hello World program. |
| [`build-idl`](#build-a-program-idl) | `arch-kit build-idl <PROGRAM_PATH> <OUTPUT>` | Build a Satellite program's IDL JSON. |
| [`deploy`](#deploy-a-program) | `arch-kit deploy [OPTIONS]` | Deploy or update a program and its IDL. |
### Key management
| [`keygen`](#generate-keys) | `arch-kit keygen [OPTIONS] <PATH>...` | Generate one or more secp256k1 key files, with optional public key prefixes (vanity). |
| [`pubkey`](#derive-a-public-key) | `arch-kit pubkey <PATH>` | Derive a Base58 Arch public key from a secret key file. |
### Token program
| [`ata`](#inspect-tokens) | `arch-kit ata <OWNER> <MINT>` | Derive an associated token account address. |
| [`token-balance`](#inspect-tokens) | `arch-kit token-balance <OWNER> <MINT>` | Read an owner's ATA balance for a mint. |
| [`token-account`](#inspect-tokens) | `arch-kit token-account <ADDRESS>` | Inspect one APL token account. |
| [`token-accounts`](#inspect-tokens) | `arch-kit token-accounts <OWNER>` | List every APL token account owned by an address. |
| [`mint-info`](#inspect-tokens) | `arch-kit mint-info <MINT>` | Inspect an APL token mint. |
| [`create-mint`](#create-a-mint) | `arch-kit create-mint --mint-signer <SOURCE> --signer <SOURCE>` | Create an APL token mint with optional initial supply. |
| [`mint-tokens`](#mint-tokens) | `arch-kit mint-tokens <RECIPIENT> <MINT> <AMOUNT> --signer <SOURCE>` | Mint tokens to a user's ATA. |
| [`token-transfer`](#transfer-tokens) | `arch-kit token-transfer <RECIPIENT> <MINT> <AMOUNT> --signer <SOURCE>` | Transfer tokens to a user's ATA, creating it idempotently. |
| [`token-transfer-to-account`](#transfer-tokens) | `arch-kit token-transfer-to-account <DESTINATION> <MINT> <AMOUNT> --signer <SOURCE>` | Transfer tokens directly to an APL token account. |
### Native ARCH
| [`faucet`](#fund-an-account) | `arch-kit faucet --key <PATH>` | Create or fund an account using a non-mainnet faucet. |
| [`transfer-arch`](#transfer-native-arch) | `arch-kit transfer-arch <DESTINATION> <AMOUNT> --signer <SOURCE>` | Transfer native ARCH to an account. |
| [`arch-balance`](#inspect-native-arch) | `arch-kit arch-balance <ACCOUNT>` | Read an account's native ARCH balance. |
### Network
| [`health`](#check-node-health) | `arch-kit health` | Check validator readiness and block progression. |
Run `arch-kit <COMMAND> --help` for the complete option list.
## Network configuration
Networked commands share these top-level settings:
| Arch RPC | `--rpc-url <URL>` | `ARCH_RPC_URL` | `https://rpc.testnet.arch.network` |
| Bitcoin network | `--bitcoin-network <NETWORK>` | `ARCH_BITCOIN_NETWORK` | `testnet` |
Explicit arguments override environment variables and defaults. Place them
before the command, for example `arch-kit --bitcoin-network regtest deploy ...`.
Supported networks are `mainnet`, `testnet`, `testnet4`, `signet`, and
`regtest`.
## Transaction signers
Transaction commands accept local files and remote arch-cosigner roles through
the same source format:
```bash
--signer file:./keys/authority.key
--signer cosigner:TREASURY
```
A bare path is treated as a local file. `cosigner:TREASURY` reads
`TREASURY_COSIGNER_URL`, `TREASURY_COSIGNER_TOKEN`,
`TREASURY_COSIGNER_ROLE`, and `TREASURY_COSIGNER_PUBKEY`; the public key must
be 64-character hex. Existing `--key` and `--mint-key` spellings remain aliases.
Deployment and faucet funding remain file-only until their SDK helpers accept
external signers.
## Initialize a program
```bash
arch-kit init ./hello-world --program-key ./keys/program.key
```
The destination must not exist. The command creates a Satellite program whose
declared ID is derived from the supplied program key. Its `say_hello`
instruction requires a user signer and logs `Hello <USER_BASE58_PUBKEY>`; the
signature constraint uses the custom error defined in `src/error.rs`. The
secret key is read only and is not copied into the project.
Build the generated program from its project directory, or pass its manifest
path explicitly:
```bash
cargo build-sbf --manifest-path ./hello-world/Cargo.toml
```
## Build a program IDL
Generate the Satellite IDL JSON before publishing it with `deploy --idl`:
```bash
arch-kit build-idl ./hello-world ./target/idl/hello_world.json
```
The Satellite builder uses Rust nightly and derives the IDL from the program's
declared instructions and accounts.
## Check node health
```bash
arch-kit health
```
The command checks validator readiness, reports RPC latency, and samples the
block height twice. It exits successfully only when the node is ready and the
height increases during its two-second observation window.
## Generate keys
Generate one or several independent keys:
```bash
arch-kit keygen ./keys/program.key ./keys/authority.key
```
Search for a Base58 Arch public-key prefix, optionally limiting CPU threads:
```bash
arch-kit keygen --prefix PAMM --threads 8 ./keys/vanity-program.key
```
Parent directories must exist, and destination paths must not. Existing paths
are never replaced. Secrets are stored as SDK-compatible hex, never printed,
and created with `0600` permissions on Unix.
Vanity search uses all available CPU parallelism by default. Each additional
Base58 character increases the rough expected work by about 58 times; the
estimate is only a baseline because first characters are not uniformly
distributed.
## Derive a public key
```bash
arch-kit pubkey ./keys/authority.key
```
The command reads either supported secret-key file format and writes only the
derived Base58 Arch public key to standard output.
## Inspect tokens
Derive an ATA locally without contacting an RPC node:
```bash
arch-kit ata <OWNER> <MINT>
```
Read its balance or inspect token state:
```bash
arch-kit token-balance <OWNER> <MINT>
arch-kit token-account <TOKEN_ACCOUNT>
arch-kit token-accounts <OWNER>
arch-kit mint-info <MINT>
```
Public keys may be Base58 or 64-character hex. Amounts include both raw and
decimal-formatted values. RPC-backed token commands accept `--json`; raw token
amounts are encoded as strings in JSON to preserve full `u64` precision. A
missing ATA has a zero balance and `exists: false`, while malformed or
incorrectly owned accounts are errors.
## Create a mint
Create a mint using existing mint and authority key files:
```bash
arch-kit create-mint \
--mint-signer file:./keys/mint.key \
--signer file:./keys/authority.key \
--decimals 6 \
--initial-supply 1000000
```
Decimals default to `9`. The payer also becomes the mint authority. Mints are
non-freezable by default; pass `--freeze-authority <PUBKEY>` to set one. Initial
supply is minted to the authority's ATA in the same transaction. Add
`--fixed-supply` to permanently revoke mint authority after the initial mint.
## Mint tokens
Mint additional tokens to a user's ATA, creating it idempotently when needed:
```bash
arch-kit mint-tokens <RECIPIENT> <MINT> 100 --signer file:./keys/authority.key
```
Amounts are interpreted using the mint's decimals. Fixed-supply mints reject
this operation because they no longer have a mint authority.
## Transfer tokens
Transfer tokens from the signing key's ATA to another user's ATA. The recipient
ATA is derived and idempotently created in the same transaction:
```bash
arch-kit token-transfer <RECIPIENT> <MINT> 1.25 --signer file:./keys/owner.key
```
Transfer directly to an existing token account, including a non-ATA account:
```bash
arch-kit token-transfer-to-account <TOKEN_ACCOUNT> <MINT> 1.25 \
--signer file:./keys/owner.key
```
Amounts are human-readable decimals interpreted using the mint's configured
decimals. Both commands derive the source ATA from the signing key by default;
pass `--source <TOKEN_ACCOUNT>` to use another token account owned by the same
signer.
## Fund an account
Create or top up an account through the configured network's faucet:
```bash
arch-kit faucet --key ./keys/owner.key
```
The command is unavailable on mainnet. It waits for funding to be processed
and reports the resulting native ARCH balance.
## Transfer native ARCH
Transfer native ARCH using a local secret key file:
```bash
arch-kit transfer-arch <DESTINATION> 0.1 --signer file:./keys/owner.key
```
ARCH uses nine decimal places. The command validates the sender's system
account and requires enough balance for both the amount and the network's
5,000-lamport base fee before submitting a native system transfer. For mainnet,
place the shared network arguments before the command:
```bash
arch-kit --rpc-url https://rpc.mainnet.arch.network \
--bitcoin-network mainnet \
transfer-arch <DESTINATION> 0.1 --signer file:./keys/owner.key
```
## Inspect native ARCH
Read an account's native balance:
```bash
arch-kit arch-balance <ACCOUNT>
arch-kit arch-balance <ACCOUNT> --json
```
ARCH uses nine decimal places. JSON output represents lamports as a string to
preserve full `u64` precision.
## Deploy a program
```bash
arch-kit deploy \
--elf ./target/deploy/example.so \
--program-key ./keys/program.key \
--authority ./keys/authority.key
```
Key files may contain a secp256k1 secret-key string or an SDK-compatible JSON
byte array.
Useful deployment options:
- `--expect-program-id <PUBKEY>` checks that `--program-key` derives the expected
program ID before faucet funding or deployment. Accepts Base58 or 64-character
hex; omitted by default.
- `--generate-if-missing` securely creates missing program or authority keys.
- `--fund-authority` requests faucet funding before deployment; it is rejected
on mainnet.
- `--idl <PATH>` publishes or upgrades an IDL after deployment.
- `--idl-size <BYTES>` sets the minimum IDL account size and requires `--idl`.
Growing a populated account also requires `--allow-idl-resize`.
- `--allow-idl-resize` opts into growing a populated canonical IDL and requires
`--idl`. It is disabled by default.
IDL accounts default to at least 10,000 bytes, including the 44-byte header.
Populated accounts retain their capacity unless `--allow-idl-resize` is supplied.
With the flag, growth accommodates the new compressed IDL and any larger
`--idl-size`, including when the IDL contents are unchanged. Accounts are never
shrunk. The target program must include canonical Satellite IDL handlers.
For compatibility with older handlers, the CLI copies the current IDL into a
backup buffer, verifies the copy, then clears, grows, and restores the canonical
IDL within each resize transaction. A failed transaction leaves the published
IDL intact. The temporary resize buffers are closed after the final growth
step, returning their rent to the authority. The CLI verifies the restored IDL
before publishing an update. If IDL publication fails, the deployed program
remains deployed and its program ID is included in the error.
## Publishing arch-kit
Maintainers publish from GitHub Actions using the `Publish Crate` workflow.
Configure a protected `crates-io` environment containing the
`CRATES_IO_TOKEN` secret, update `Cargo.toml`, `Cargo.lock`, and the pinned
`ARCH_KIT_VERSION` in the reusable workflow, then run the workflow with that
version. After publication, create the matching immutable Git tag, such as
`v0.1.0`, for workflow consumers.