*Reference for operators who want the full story. Nothing here is needed to install.*
Audience: an operator whose identity holds a delegate entry on the staking authority roster (acc://staking.acme/book/2). If your book is not on the roster yet, that comes first — installation verifies it and refuses otherwise.
The security model, in one paragraph. Installation creates a new signing key in the ASP app's own vault — *never* your wallet's key. Your wallet is opened once, interactively, only to authorize placing that key on chain: beside your delegate entry on the roster, on a contained page (the page also carries the dead key MHz126xxxDEAD… with an unreachable threshold, so the signing key can take part in staking multisig and can do nothing else, even if the server holding it is compromised). The running app never touches your wallet again.
asp install
identity … detected from your wallet (asked only if one wallet operates
several validators)
passphrase … you set one for the NEW staking vault, then enter your WALLET
passphrase once to authorize the key on chain
Where should ASP run? It should run whenever the system runs.
1) This machine, in Docker (restart: unless-stopped)
2) This machine, as a system service (systemd / launchd / Scheduled Task)
3) A remote server, over SSH
4) Nowhere — I'll run `asp auto` myself
Choosing 3 asks for user@host (and port), detects the remote's OS and architecture over SSH, and offers Docker when the remote has it — otherwise it installs the binary and the remote's native service (systemd on Linux, launchd on macOS, a Scheduled Task on Windows). Only the staking vault travels; your wallet never leaves this machine. If the remote's platform differs from yours, the binary is cross-compiled on the spot (Go toolchain + repo present) or fetched from the release page.
Every deployment ends the same way: asp auto under a supervisor that starts it with the system and restarts it on failure, reading the vault passphrase from a 0600 environment file (ASP_VAULT_PASSPHRASE) — never from a world-readable unit, plist or task definition. The wallet passphrase is never stored anywhere.
Scripted installs stay scripted: without a TTY the dialog never opens, flags behave exactly as before, and the deployment step is skipped.
| target | service | logs |
| Docker | container asp (--restart unless-stopped) | docker logs -f asp |
| Linux | asp.service, enabled | journalctl -u asp -f |
| macOS | com.accumulate.asp LaunchAgent | ~/Library/Logs/asp.log |
| Windows | Scheduled Task ASP, at system start | task's console |
Moving an installation to another machine is a first-class command:
asp move
A move is not a rotation — the key does not change, so the on-chain pages are untouched and your wallet is never needed; only the vault passphrase is asked, for the target's 0600 env file. The dialog picks the installation, asks for the target (user@host), detects its platform, and offers Docker or a systemd service — optionally updating asp to the latest release in the same motion. The discipline underneath: the target is fully staged while the source keeps signing; the source stops only for the final start; the target must prove itself armed (status page up, vault unlocked, signing enabled) before the source is retired — and any failure rolls back with the source running again. The old machine keeps the encrypted vault as an offline backup, and its configuration is marked MOVED: bare asp shows where it went and refuses to start it there again.
Key rotation / decommission happens where the wallet is: re-run asp install (rotation — detects and replaces the old installation with consent) and choose the same deployment; the service is replaced in place. If a remote server is ever compromised, the exposure is one contained staking key: rotate it; your identity key was never there.
Versions: v1.x.y is the ASP *product* version; the *build* number beside it is the internal revision. asp version prints both.
One key, born in the vault, never anywhere else. asp install generates the signing key inside asp's own vault (~/.accumulate/asp/<name>/vault). There is no import and no export — no command exists to type a key in or read one out. A key that could be typed in would exist outside the vault, and the containment story below would die with it.
Where the key lives: a sidecar on the staking page, contained on your own book. Install places the key in two places at once, and both matter:
*directly* onto acc://staking.acme/book/2 (in a simulation, the world's page — e.g. acc://staking-003.acme/book/1), beside your operator's delegate entry. This direct placement is what lets the key sign staking distributions: the staking page matches it by hash and never consults your book's threshold. It is a *sidecar* — it rides alongside your delegate entry and does nothing but staking.
contained page on your book (<operator>/book/2): the vault key beside the keyless dead sentinel MHz126xxxDEADxxxDEADxxxDEADxxxDEADxxxDEADxxxDEADQhfekn3, threshold 2 of 2, with UpdateKeyPage and UpdateAccountAuth denied. This page can never satisfy itself, so on *your* book the key authorizes nothing alone — not your funds, not your authorities, not its own restrictions.
So the key signs staking (via the sidecar on the staking page) and is inert everywhere else (via containment on your book) — it can do staking multisig and nothing more. Audit both, from the chain, no vault needed:
asp sign-path
A vote is signed AS the staking page. When your signer votes on a distribution, the signature is made directly as the roster page through the sidecar entry — that is what makes it count toward the threshold. The Asp Browser marks these votes with the Signature-A badge beside your validator's name, so anyone can see at a glance which votes the app cast and which a human cast.
Fund the signing page: ~1000 credits. Two very different costs come out of two different pages, and the difference matters:
the shared roster page (staking.acme/book/2) — the page the sidecar signature is made as.
slot submits it — costs the full transaction fee, ~195 credits for a mainnet-sized distribution, billed to the page that signs the initiating envelope. The signer deliberately originates through your contained page (<operator>/book/2), the page install funds, so each validator pays for the periods it leads.
A freshly created page has no credits, so allocate around 1000 credits (~5 leads) to it after install:
accumulate credits <your-token-account> <operator>/book/2 1000
asp install prints the page's current balance and this exact command when it finishes. A signer that cannot pay to originate refuses before submitting and says so — the fee and every balance it tried — rather than letting the network silently discard an unpayable submission.
What the key must never do: file staking requests. The dead sentinel that contains the key also identifies the page, and every reader — the Asp Browser and every signer — refuses a staking request signed from it: *"wrong page withdraw: requests to withdraw cannot be signed with the asp signing page."* Requests are filed by the account owner's book; the asp key signs distributions only.
The wallet stays home. Your wallet is opened once, interactively, at install — only to authorize placing the key on chain. It never travels to a server, its passphrase is never stored and never readable from the environment. Replacing the key on the signing page requires the wallet: re-run asp install (rotation, with consent); the asp key cannot replace itself.
The vault passphrase. Interactive runs prompt on the terminal. Services read it from the environment — a root-only 0600 env file (ASP_VAULT_PASSPHRASE; the docker fleet's compose variable VAULT_PASSPHRASE) written by the install dialog. It is never in config.json, never in a systemd unit, plist or task definition, and never a command-line argument.
Unlock lifetime. confirm and auto hold the unlocked key for at most --relock-after (default 8h) before discarding it; SIGHUP relocks immediately. watch never opens the vault at all — a watch-only install has no vault and no keys.
Every cycle, the signer reads a fresh view of the chain — the period, the pending distribution, the votes that count, the rotation, its own credit position — derives what to do from that view alone, acts, and forgets. It keeps no beliefs between cycles: nothing it remembers can go stale, so nothing can wedge it.
What that means for an operator:
lands in exactly the state the chain says — mid-period, mid-signing, after an outage, it does not matter. There is no state file to corrupt and no memory to clear.
prints the reason (holding: …) — an older period still owed, an unreadable roster, an unpayable fee — and re-derives it every cycle. Fix the cause (fund the page, wait out the outage) and the signer resumes on its own; no restart is ever required to un-stick it.
submitted only after the transaction is *pending on the principal* — the same fact every other validator reads. A submission the network accepted but silently dropped is reported as a drop and retried minutes later, automatically.
its own submissions and votes, so a signature in flight is not re-sent while it propagates. The debounce expires; the chain decides.
asp assumes the server holding it can be compromised, and is built so that the worst case is bounded, detected, and recoverable. Four defenses run continuously.
While confirm or auto runs, the signer re-inspects its own signing page every minute. If the page is ever weakened — the threshold drops to 1 (the key would be live alone), a key is added beside the sentinel (two signatures could move tokens), the sentinel disappears, or a denied operation (UpdateKeyPage/UpdateAccountAuth) is re-allowed — asp treats the page as compromised and, on the spot:
mechanism as the relock timer), so nothing more can be signed;
alert channel (--notify-command / --notify-webhook);
no longer proves containment.
Containment is latched beside the vault the first time asp confirms it, so a restart cannot be fooled by a page that has been stripped bare — a missing sentinel on a latched install is a compromise, not a fresh page. A failed chain read never flushes; the signer acts only on evidence.
A flush is deliberately not self-healing: the key stays out and the signer stays watch-only until you rebuild the page from your wallet. Recovery is a re-install, which mints a fresh key and a fresh contained page under your wallet's authority:
asp install
Because the wallet is required, an attacker who tampered with the page cannot bring the signer back — only the operator can. Until then the validator still watches and alerts; it simply does not sign.
Every validator also audits the OTHER signatures on a pending distribution. An asp-signed signature must come from an intact asp signing page (the sentinel present, threshold 2, exactly two entries, denials in place). A page that carries the sentinel but fails containment is a compromised peer, and every healthy validator announces it on every alert channel, naming the page and the failure — so a tampered validator is caught by the fleet, not just by itself. A signature from a plain key page is a human signing from their own book: permitted, and outside any asp app's reach.
The same containment that makes the key inert on your book also stops it being misused inside staking: every reader — the Asp Browser and every signer — refuses a staking request signed from an asp signing page (*"wrong page withdraw: requests to withdraw cannot be signed with the asp signing page"*). The asp key signs distributions and nothing else; requests are filed by the account owner's own book.
asp update lists the released versions, newest first, and lets you pick one — upgrade or roll back (asp update v3.26.1 targets a version directly; GitLab keeps every release, so picking an older one IS the rollback). The download comes only from the project's own GitLab origin over TLS, and the binary must report exactly the version it claims.
After the binary swap, update offers to bring every deployment to the same release — the docker container, the systemd/launchd/Scheduled Task service, or a remote server over SSH. Only the binary (or image) is replaced and the service restarted; the env file holding the vault passphrase is never rewritten, so no passphrase is asked and the signer comes back armed. One command, wherever asp runs.
The running service also updates itself: the status page (loopback only, per-process token) offers check and apply, and apply downloads THIS platform's binary over TLS from the project's own host, verifies it reports the expected version, keeps the old binary for rollback, and restarts in place. The vault passphrase survives the restart from its 0600 environment file, so a service comes back signing without a prompt. Keeping current matters for security: fixes to the defenses above reach a running fleet through this path — an out-of-date signer is missing them.
If a server is fully compromised, the exposure is one contained staking key: at worst it takes part in staking multisig until the fleet flags it and you rotate it from the wallet. Your identity/wallet key was never on the server, and the contained key can move nothing on your own book.
aspInstall keeps its answers. Every install saves a configuration under
~/.accumulate/asp/<name>/
config.json endpoint, browser, authority, principal, UI, deployment
vault/ the staking vault for this configuration
— never the passphrase. Several configurations coexist: a mainnet validator beside a test world, each with its own vault and UI port. The name derives from the authority (mainnet for staking.acme; staking-003 for a test world).
Running asp shows you your ASP. Bare asp probes every configuration's status UI and presents the directory:
ASP configurations (~/.accumulate/asp) 1) mainnet RUNNING http://127.0.0.1:8710 acc://staking.acme/book 2) staking-003 RUNNING http://127.0.0.1:8711 acc://staking-003.acme/book 3) devnet stopped Choose [1-3]:
One configuration goes straight through. A running instance opens its status page in your browser — state, roster, the current period's verdict. A stopped one starts with its saved configuration and opens the UI itself.
asp sign-path audits the key's on-chain placement — the sidecar onthe roster and the containment on your book — from the chain, no vault needed.
verdict.
asp pretend is the rehearsal: full validation, a key armed exactly as autoarms one, and no signature ever submitted — everything but the signing, safely.
validate-report-vs-spec semantics is refused with the failing rule named (§7.1 order, §8/§9 arithmetic, drift identities).
a governance act, whatever --full-stake-minimum says.
your funds.