# AZ Vote Delegate documentation > Install AZ Vote Delegate on your Zenon pillar, choose a pillar to copy, and manage AZ proposal and phase-payout votes from a terminal menu. az-vote-delegate is a Linux terminal application and background service for Zenon Accelerator-Z voting, using a pillar producer key. Dry run previews without submitting; Live submits votes. copyDelayMinutes (0–10080, default 0) delays each copied project or phase vote from first observation. Live timers persist; manual votes bypass the delay. Delays do not guarantee privacy. Manual overrides apply to individual proposal or phase request IDs and persist locally. Dynamic Plasma is not supported. Start with Installation & setup for the install command, producer configuration, delegate selection, and voting mode. Full documentation: [llms-full.txt](https://www.0x3639.com/az-vote-delegate/llms-full.txt). Source and releases: [GitHub](https://github.com/0x3639/az-vote-delegate). ## Installation & setup Install on the Linux host that runs your Zenon pillar. You need an amd64 or arm64 machine, access to a trusted node, and your existing **producer key**. The app uses the producer key, never the owner's stake key. This release supports legacy fused plasma and proof of work. **Dynamic Plasma is not supported.** ### Install For a node operated as root: ```bash curl -fsSL https://raw.githubusercontent.com/0x3639/az-vote-delegate/main/install.sh | sudo sh ``` If your node runs as an unprivileged user, sign in as that user and omit `sudo`: ```bash curl -fsSL https://raw.githubusercontent.com/0x3639/az-vote-delegate/main/install.sh | sh ``` The installer downloads the latest stable release, verifies its SHA-256 checksum, and opens setup in your terminal. No compiler or Docker is required. Root installations use `/usr/local/bin`; user installations use `~/.local/bin`. If setup does not open automatically, run `sudo az-vote-delegate` for a root installation or `~/.local/bin/az-vote-delegate` for a user installation. ### Complete setup 1. **Connect to your node.** Confirm the node URL and expected chain ID. The defaults are `ws://127.0.0.1:35998` and mainnet chain ID `1`. 2. **Install the service.** Choose **Yes** when asked to install and start a background service. It keeps working after you close the terminal menu. 3. **Load your producer key.** Choose **Use existing znnd configuration**, or select a producer key file and enter its derivation index and password. No new key is generated and your znnd configuration is unchanged. 4. **Choose a pillar to copy.** The menu shows the top 30 active voters in random order, with participation figures. [How participation is calculated](./delegation.md). 5. **Set a copy vote delay.** Enter the number of minutes to wait before copying each vote. `0` means no delay; manual votes bypass this setting. [Delay behavior](./delegation.md#delay-copied-votes). 6. **Choose your voting mode.** **Dry run** previews decisions without submitting votes; **Live** submits votes on eligible AZ proposals and phase-payout requests. Dry run is selected by default. 7. **Save and start.** Review the mode and confirm. The service starts in the mode you selected. The wizard saves a private password file so the service can unlock the key after a reboot. The file is readable and writable only by its owner (mode `0600`). Keep it and your application configuration private. For a regular user's service to keep running after logout and start at boot, enable lingering. Replace `YOUR_NODE_USER` with the account that runs the node: ```bash sudo loginctl enable-linger YOUR_NODE_USER ``` ### Check and run The main menu shows your mode, your pillar, and the pillar it follows. Start in **Dry run** to review the decisions on your node. When ready, select **Voting mode: Dry run / Live**, choose **Live**, and confirm. Check the [service status and logs](./service.md) to confirm the background worker is running. Closing the menu does not stop it. Switching to Dry run does not undo transactions already submitted. ### Reopen the menu For a root installation: ```bash sudo az-vote-delegate ``` For a user installation: ```bash ~/.local/bin/az-vote-delegate ``` Use the same user and configuration each time. **Esc** goes back one screen; at the main menu it exits. **Ctrl+C** exits the menu from any screen. During setup, Esc cancels unsaved setup and **Shift+Tab** returns to the previous field within a step. Next, learn about [manual votes and phase payouts](./manual-voting.md) or [upgrading an existing installation](./updates.md). --- ## Delegation & vote delays Select a pillar during setup, or choose **Choose pillar to copy** from the main menu. The service copies that pillar's votes on eligible AZ proposals and phase-payout requests. ### The top 30 voters The picker ranks active pillars by the number of distinct requests they have voted on, excludes your own pillar, and takes the top 30. If fewer are available, it shows them all. The list is shuffled each time you open it. Ties are randomly ordered before the cutoff, so names near the start of the alphabet do not get priority. An option may show: ```text 82.0% participation · 82/100 requests ``` Yes, no, and abstain all count as participation. The denominator includes all project proposals and retained phase requests returned by the node, including closed requests. It is not adjusted for when a pillar joined. Replaced or deleted phase revisions cannot be included when the node no longer returns them. Participation tells you how often a pillar has voted in the available history. It does not tell you whether you agree with its decisions. With no recorded requests, the menu reports that instead of inventing a percentage. ### What gets copied The service checks current votes on each poll. It follows open initial project proposals within their voting window and the current voting phase of active projects. If the delegate has not voted, there is no vote to copy yet. Project proposals and phase payouts have separate request IDs. A vote on a project does not automatically count as a vote on its phase. When an updated phase receives a new votable ID, the app treats it as a fresh voting round. The app votes on payout requests. It does not submit a project owner's payment request or guarantee an immediate payment; the contract rules and other pillars' votes still determine the result. ### Delay copied votes Available from **v0.3.0**. Set **Copy vote delay (minutes)** during setup, or select **Copy vote delay: … minutes** in the main menu. Enter a whole number from **0 to 10,080** (7 days). The default is **0**, which preserves copying without an intentional delay. One setting applies to all pillars managed by that configuration. For example, set **15** to wait at least 15 minutes after the service first observes a vote that needs copying. If it sees the vote at 12:00, the earliest submission is 12:15. The timer starts when the service observes the vote, not when the delegate originally voted on-chain. An old vote first seen after installing the app gets the same delay. Each proposal and phase-payout request has its own timer: - Live-mode timers are saved locally and survive service restarts and upgrades. Time spent offline counts toward an existing timer; the service rechecks the current votes before submitting after restart. - A changed delegate vote or source pillar starts a new timer when observed. If the vote disappears or a round reset is observed, the old timer is discarded. A phase revision with a new request ID starts fresh. - Manual votes made in the app bypass the copy delay and protect that request. Other votes keep their own timers. External manual votes detected by the service also prevent copying. - Changing the delay takes effect without restarting the service and recalculates waiting votes from their original observation times. Reducing it, including setting it to 0, can make a waiting vote eligible on the next poll. Already submitted transactions cannot be recalled. - Dry-run timers are previews held in memory. They are not saved. Switching to Live reloads the durable state, so new observations made only in Dry run start a new live timer. Open **Pending transactions & manual overrides** to see waiting votes and their earliest submission times in UTC. The `status` command also shows `waiting-delay` and a **COPY AFTER (UTC)** column. Submission happens on a poll after the delay; polling (30 seconds by default), proof of work, retries, or node outages can add time. A request may close before its timer expires. The app skips closed requests and does not shorten your delay to vote before a deadline. Choose a delay that suits your participation goals. #### What a delay does for privacy A delay separates your vote from the moment the service observes the delegate's vote. It does **not** hide votes on the public ledger or guarantee that delegation cannot be inferred. Matching choices, repeated timing patterns, and fixed delays can still reveal a relationship. The delay is a timing preference, not an anonymity feature. ### Change who you follow Choose **Choose pillar to copy**, select a new pillar, and confirm. Your saved [manual overrides](./manual-voting.md) stay in place. Other eligible requests follow the new delegate. Delegation chains can work, but an empty cycle where two pillars only follow each other produces no initial vote. Choose a pillar that makes voting decisions. --- ## Manual votes & phases A manual vote takes priority **only for that proposal or phase-payout request**. The service keeps running and copying other eligible requests. There is no pause for a number of days. Manual votes bypass the configured copy vote delay; they still wait for any earlier pending transaction, proof of work, or retry backoff as needed. ### Cast a vote 1. Open the terminal menu as the installation owner. 2. Choose **Vote on unanswered AZ proposals** or **Vote on unanswered phase payouts**. 3. Select a request and review its details. A phase includes its parent project and requested ZNN/QSR. 4. Choose **yes**, **no**, or **abstain**, then confirm. The lists show only requests your pillar has not voted on. Any recorded yes, no, or abstain hides that request. Queued manual choices also leave the list and appear under **Pending transactions & manual overrides**. The app checks again before accepting your selection in case your pillar voted while the list was open. In **Dry run**, the service does not submit votes, and the menu asks you to switch to **Live** before accepting a manual vote. The menu distinguishes a queued choice from a confirmed manual vote; a confirmation screen alone is not proof of on-chain execution. Press **Esc** or choose **Back** to return without submitting an unconfirmed choice. In a searchable list, **Tab** applies a filter and **Ctrl+R** clears it. ### Your choice persists The app saves an explicit manual override before acknowledging it. If you choose yes and the delegate later votes no, your manual yes stays protected. Overrides are saved by **pillar name + request ID** in the local state file. They survive service restarts, reboots, and upgrades. Preserve that file when moving the installation. The background service is the only signer and state writer; the menu sends your choices to it. An override stays in effect until you select **Resume copying** in **Pending transactions & manual overrides**, the request closes, or its confirmed votes reset. An updated phase with a new ID is a new request; an old phase's override does not carry over. Project and phase overrides are independent. ### If a copy vote is already in progress A manual choice interrupts copy-vote preparation. If the service has already submitted a copy vote, it waits for the confirmed contract receipt before applying your manual choice. An uncertain publish result stays pending instead of being blindly sent again. Inspect pending transaction hashes on your trusted node if necessary. A queued choice cannot reopen a closed request or undo a contract decision. ### Votes from another application Use this app's menu when you want the service to know that a vote is manual. A vote cast elsewhere can have exactly the same value as the service's last vote, so the value alone cannot identify your intent. Another application can also submit a vote at the same time as this service. For guaranteed manual control through an external tool, stop this service, resolve pending submissions, and leave it stopped while voting externally. --- ## Updates & upgrades ### Upgrade from the menu Open the app as its installation owner and select **Check for updates / upgrade**. If a newer stable version is available, the menu displays **Update available: v…** and offers **Upgrade application to v…**. The upgrade screen shows the installed and available versions, executable path, release-notes link, and whether the existing service will restart. Confirm **Upgrade** to download and install. The app verifies the download's SHA-256 checksum and checks the executable's reported version before replacing the existing binary. Configuration, passwords, voting mode, and saved manual overrides are preserved. An active service restarts automatically only when its generated systemd unit is unmodified and matches this executable and configuration. A stopped service stays stopped. Custom units, units with drop-ins, and foreground processes need a manual restart; the upgrade screen explains this before confirmation. An upgrade does not install or enable another service. Failed download or validation leaves the old executable in place. If installation succeeds but restarting the service fails, the app reports that partial result so you can check logs and restart it yourself. ### Upgrading to v0.3.0 Existing installations keep a zero-minute copy delay until you change it. After upgrading, choose **Copy vote delay: 0 minutes** to set your preferred delay. Existing manual overrides remain protected, and live timers persist on subsequent restarts. The new configuration and state fields are not readable by older releases, which reject unknown fields. If you need the option to downgrade, keep a private pre-upgrade backup of configuration and state. Do not discard current pending transactions or manual overrides just to make an older binary start; resolve them before restoring older state. ### Update notices The app checks GitHub when you open the menu and when you return to the main menu after 15 minutes. Selecting the update option always checks again. An idle menu updates when you return to or refresh the main screen. These notices appear inside the menu. The daemon does not send notifications while it is closed. Failed update checks do not interrupt voting. ### Upgrade with curl You can also rerun the installer as the **same user**, using the **same installation directory**. It replaces the executable in place and reconnects to your existing setup. For a root installation, these commands install without opening the menu, then restart the existing service: ```bash curl -fsSL https://raw.githubusercontent.com/0x3639/az-vote-delegate/main/install.sh | sudo env AZ_NO_UI=1 sh sudo systemctl restart az-vote-delegate.service ``` For a regular user installation: ```bash curl -fsSL https://raw.githubusercontent.com/0x3639/az-vote-delegate/main/install.sh | AZ_NO_UI=1 sh systemctl --user restart az-vote-delegate.service ``` The running service uses the old binary until restarted. Restarting uses the same named unit; it does not create a second service. If you run in the foreground, stop that process before starting the upgraded binary. Changing users, configuration paths, or install directories creates a separate installation. Keep your original user and paths for normal upgrades. If upgrading from v0.2.5 or earlier, use curl once to get the in-app upgrade option. See all [release notes on GitHub](https://github.com/0x3639/az-vote-delegate/releases). --- ## Background service The setup wizard can install and start a systemd service. The terminal menu controls that service; closing the menu leaves it running. ### Root installation ```bash sudo systemctl status az-vote-delegate.service sudo journalctl -u az-vote-delegate.service -n 100 --no-pager ``` To restart the existing service: ```bash sudo systemctl restart az-vote-delegate.service ``` To stop it: ```bash sudo systemctl stop az-vote-delegate.service ``` The generated unit is installed under `/etc/systemd/system`. ### Regular user installation Run these as the same user that installed the application: ```bash systemctl --user status az-vote-delegate.service journalctl --user -u az-vote-delegate.service -n 100 --no-pager systemctl --user restart az-vote-delegate.service ``` For the user service to survive logout and start at boot, enable lingering. Replace the placeholder with your node's account name: ```bash sudo loginctl enable-linger YOUR_NODE_USER ``` Use `systemctl --user stop az-vote-delegate.service` to stop it. ### If you skipped service installation After saving setup, run this as the installation owner to install and start the service: ```bash az-vote-delegate service-install ``` For root installations, prefix the command with `sudo`. For user installations without `~/.local/bin` on `PATH`, use `~/.local/bin/az-vote-delegate`. If systemd is unavailable, keep the worker running in a terminal: ```bash az-vote-delegate run ``` Open the menu in another terminal, with the same user and configuration. `run` uses your saved Dry run or Live mode. Do not start a foreground worker alongside the service. ### Configuration and persistent state By default, files are stored under `~/.config/az-vote-delegate/` for the user running the app: | File | Purpose | | --- | --- | | `config.json` | Node, key, delegation, copy-delay, and voting-mode settings | | `producer-password` | Owner-only password file created by setup | | `state/state.json` | Manual overrides, copy-delay observations, copied votes, and pending submissions | An existing `./config.json` is preferred for compatibility. Use `--config /absolute/path/config.json` to select a specific configuration. The menu and service must use the same file. Restarts and upgrades preserve manual choices. When migrating, stop the service and preserve the configuration, password, key, and state together, keeping them private. Resolve pending transactions before changing the setup. Do not delete state to clear an error: it contains the record of submissions and manual overrides. A state-file lock prevents two workers from using the same state. Do not create separate state files for competing instances with the same producer. ### Reliability details The service reads current votes every poll, so it does not need to observe the delegate's original vote block. It pages through all projects, using the node's 1,024-entry limit, and selects Voting projects within the 14-day window and the current Voting phase of Active projects. Reads use node time. Delegation chains work; an empty A↔B cycle produces no initial vote. The state file tracks copy votes, explicit manual choices, external overrides, copy-delay observations, backoff and pending submissions. Saves use a 0600 temporary file, file sync, atomic rename and directory sync. Save failures stop live operation. A file lock prevents a second service from using the same state, and the private directory and mode-0600 control socket restrict control to its owner. Do not run multiple instances for the same producer using different state paths. A signed block hash is journaled before publication. Pending sends wait for a confirmed contract receive block, then votes are reread. Preparation failures and definite rejections retry on the next poll, then back off exponentially up to 30 minutes. Ambiguous publications remain pending, including a crash between journaling and publishing. This favors avoiding duplicate votes over automatic recovery from an unknown outcome. Missing state conservatively treats existing votes as manual. Corrupt or cross-network state is refused. After stopping the service and resolving every pending transaction, an explicit reset can discard attribution and overrides: ```sh az-vote-delegate run --config /path/to/config.json --dry-run=false --reset-state ``` Existing on-chain votes remain. Resetting while a transaction might still execute is unsafe; back up and inspect the state first. The SDK asks the node whether fused plasma is sufficient, otherwise it computes PoW. The service does not auto-fuse QSR. Logs identify PoW versus fused plasma. Reading the node's plasma requirements does not establish Dynamic Plasma compatibility; that requires a separate implementation and validation effort. Sends are sequential, so CPU work can extend a poll beyond the configured poll interval. A status preview remains available while PoW runs. SIGINT/SIGTERM cancel work and save state. WebSocket reads are interrupted on cancellation; HTTP reads have a 30-second transport timeout. Subsequent polls open a fresh connection. The producer key is held in memory while running, as it is in znnd. Use the node host or a host with equivalent protection, and an RPC endpoint you trust. No browser server, remote control port or hosted key service is involved. --- ## Troubleshooting ### The app is running but does not vote Check the mode shown at the top of the menu. **Dry run** only previews decisions. Select **Voting mode: Dry run / Live** to change it. In Live mode, confirm the [background service is running](./service.md). Your delegate must have voted on an eligible request, and an existing manual override can prevent copying that request. Check **Pending transactions & manual overrides** and service logs. A configured [copy vote delay](./delegation.md#delay-copied-votes) shows `waiting-delay` until its earliest submission time. Changes to the delegate’s vote restart the timer. Manual votes bypass it. The app supports legacy fused plasma and proof of work. Dynamic Plasma is not supported. Proof of work can take time; sends are sequential and can extend a poll beyond its configured interval. ### Cannot connect to the node Verify that znnd is running and the configured RPC URL is reachable from the app's host. The default is `ws://127.0.0.1:35998`. Use your local or trusted node and the correct chain ID; mainnet is `1`. Run the read-only configuration check as the installation owner: ```bash az-vote-delegate check --config /absolute/path/config.json ``` Replace the path with your app configuration, and use `sudo` for a root installation. This checks the key, pillar, and chain ID without signing or publishing. ### A proposal or phase is missing from manual voting The lists show only eligible, unanswered requests. A recorded yes, no, or abstain removes the item. Queued manual choices appear in **Pending transactions & manual overrides** instead. A closed proposal is no longer eligible. A phase must be the current voting phase of an active project. Updated phases can have a new request ID. Refresh the menu to fetch current data. ### A manual vote says queued or pending Queued is not the same as confirmed. If a copy vote was already submitted, the service waits for its confirmed contract receipt before applying your choice. An ambiguous publish result remains pending to avoid duplicate votes. Inspect the transaction hash on your trusted node. Do not delete state or repeatedly submit through another tool to force progress. If the request closes first, a queued vote cannot reopen it. ### The service stops when I log out For a user service, enable lingering for the node's account. See [regular user service setup](./service.md#regular-user-installation). Also confirm you installed a service instead of running the worker only in a foreground terminal. ### The menu cannot find my setup or service Open it as the same user and with the same configuration used during setup. Root and regular user installations have different home directories. An existing `config.json` in your current directory can also select a different configuration; pass `--config` to remove ambiguity. ### I installed an update but the service uses the old version The curl installer replaces the file, but an already running service needs a restart. Follow the matching root or user command in [Updates & upgrades](./updates.md#upgrade-with-curl). If an in-app upgrade reports a restart failure, the new executable may already be installed. Check the service logs and restart the existing unit. Custom units and foreground workers need a manual restart. ### I cannot leave a menu Press **Esc** or select **Back** to return one screen. At the main menu, Esc exits. **Ctrl+C** exits from any screen. The background service keeps running. ### I need more help Check the [command reference](./commands.md) and [GitHub issues](https://github.com/0x3639/az-vote-delegate/issues). When reporting a problem, include the app version, selected mode, and relevant error text. Remove passwords, key material, and other private details from anything you share. --- ## Command reference Run commands as the installation owner. Use `sudo` for root installations, or `~/.local/bin/az-vote-delegate` if a regular user's install directory is not on `PATH`. | Command | Purpose | | --- | --- | | `az-vote-delegate` | Open the menu; start setup on first use | | `az-vote-delegate setup` | Open the setup wizard | | `az-vote-delegate service-install` | Install and start the systemd service | | `az-vote-delegate run` | Run the worker using the saved voting mode | | `az-vote-delegate run --dry-run` | Force previews without submitting votes | | `az-vote-delegate run --dry-run=false` | Explicitly enable live submissions | | `az-vote-delegate status` | Show current project and phase decisions | | `az-vote-delegate check` | Check the key, pillar, and chain ID without signing or publishing | | `az-vote-delegate version` | Print the installed version | The menu is the interface for selecting delegates, casting manual votes, and upgrading. Do not launch `run` alongside an existing service. ### Select a configuration Relevant commands accept an explicit path: ```bash az-vote-delegate status --config /absolute/path/config.json ``` If the service is running, `status` reads its cached snapshot without opening a second key. `run` also accepts `--debug` for additional logging. The default path is `~/.config/az-vote-delegate/config.json`, though an existing `./config.json` takes precedence. Paths within the configuration resolve relative to the configuration file. ### Configuration fields See the complete [example configuration](https://github.com/0x3639/az-vote-delegate/blob/main/config.example.json). | Setting | Behavior | | --- | --- | | `live` | Defaults to `false` (Dry run) | | `pollIntervalSeconds` | Defaults to `30` | | `copyDelayMinutes` | Defaults to `0`; whole minutes from `0` to `10080`. Applies to copied project and phase votes, not manual votes. | | `powTimeoutSeconds` | Defaults to `300` | | Chain ID | Must be explicitly configured | | `passwordEnv` / `passwordFile` | Configure exactly one per key | | `statePath` | Persistent voting state, including manual overrides and live copy-delay observations | Password files must be regular files with mode `0600`. Passwords are not accepted as CLI flags or stored directly in the JSON configuration. Setup uses a password file so the background service can unlock the producer key after reboot. Advanced configurations can include multiple delegations, one per producer. If you use an environment variable for a password, supply it to the service environment; see the [example systemd unit](https://github.com/0x3639/az-vote-delegate/blob/main/deploy/az-vote-delegate.service). ### Installer options | Environment variable | Purpose | | --- | --- | | `AZ_NO_UI=1` | Install without opening the terminal menu | | `AZ_VERSION=v…` | Install a specific release tag | | `AZ_INSTALL_DIR=/absolute/path` | Set the binary installation directory | When upgrading, keep the original user and installation directory. See [Updates & upgrades](./updates.md). Change the delay through the menu to apply it immediately. If you edit JSON directly, restart the existing service to load the new configuration. For example, add `"copyDelayMinutes": 15` at the top level of `config.json` for a 15-minute delay. See [delay behavior and limitations](./delegation.md#delay-copied-votes). --- ## Development & releases Source builds need Go 1.26 (toolchain 1.26.7) and a C compiler, because the SDK's transitive go-zenon/secp256k1 dependency requires CGO. macOS is supported for source-based development; prebuilt releases target Linux amd64 and arm64. ```sh make build make test # Go race detector, local mock RPC servers make vet make lint # golangci-lint v2.13.1 or compatible sh scripts/test-install.sh ``` The Makefile disables a parent `go.work`. Tests cover reconciliation, native znnd wallet compatibility, signed embedded votes and PoW, project/phase paging, manual same-value overrides, restart recovery, interruption of copy preparation, waiting for pending copies, phase-payout replacement, participation ranking and shuffling, unanswered-request filtering, and installer checksum failure. The installer tests also exercise a piped installation in a pseudo-terminal to verify automatic menu launch and its opt-out. No operator credentials are used in tests. Pushing a version tag runs [the release workflow](https://github.com/0x3639/az-vote-delegate/blob/main/.github/workflows/release.yml): tests and lint on Linux, static CGO builds for amd64 and arm64, smoke tests on both (arm64 via QEMU), archive/checksum creation and a draft GitHub release. Publish that draft only after the build and asset checks pass. Live mainnet/testnet acceptance remains separate. Before operating with real votes, run `check` and review dry-run output on the intended node. On an isolated testnet, verify project yes→no copying, same-value manual takeover, a manual vote while copying, phase-payout copying/manual override and a phase revision. No live voting acceptance is implied by the offline tests. The copy-delay tests use a controlled clock and mock votes to cover project and phase timing, restart recovery, changed or withdrawn source votes, round resets, closed requests, manual overrides, dry-run behavior, and live setting changes. They never wait real minutes or submit real votes.