@axon-chain/axon-skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # @axon-chain/axon-skills
2
+
3
+ Install official Axon skills for OpenClaw and the global skills root with `npx`.
4
+
5
+ ## Included skills
6
+
7
+ - `axon-validator-node`: Axon mainnet validator and sync-node operations
8
+ - `axon-mainnet-user`: Axon mainnet wallet and public RPC usage
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npx @axon-chain/axon-skills install validator
14
+ npx @axon-chain/axon-skills install user
15
+ npx @axon-chain/axon-skills install all
16
+ ```
17
+
18
+ Default installs update both roots:
19
+
20
+ - `${OPENCLAW_STATE_DIR:-~/.openclaw}/skills`
21
+ - `${AGENTS_SKILLS_DIR:-${AGENTS_HOME:-~/.agents}/skills}`
22
+
23
+ Repeat installs update existing skill folders in place.
24
+
25
+ ## Commands
26
+
27
+ ```bash
28
+ npx @axon-chain/axon-skills list
29
+ npx @axon-chain/axon-skills install validator
30
+ npx @axon-chain/axon-skills install user
31
+ npx @axon-chain/axon-skills install all
32
+ npx @axon-chain/axon-skills install all --dry-run
33
+ npx @axon-chain/axon-skills install validator --dest /path/to/skills
34
+ ```
35
+
36
+ ## Verify
37
+
38
+ ```bash
39
+ openclaw skills list
40
+ ```
41
+
42
+ If OpenClaw is not installed on the current machine, inspect the installed skill folders directly under the target roots.
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runCli } from "../lib/cli.js";
4
+
5
+ try {
6
+ const exitCode = await runCli(process.argv.slice(2));
7
+ process.exitCode = exitCode;
8
+ } catch (error) {
9
+ const message = error instanceof Error ? error.message : String(error);
10
+ process.stderr.write(`${message}\n`);
11
+ process.exitCode = 1;
12
+ }
package/lib/catalog.js ADDED
@@ -0,0 +1,54 @@
1
+ export const SKILL_CATALOG = Object.freeze({
2
+ validator: Object.freeze({
3
+ key: "validator",
4
+ packageDir: "axon-validator-node",
5
+ skillName: "axon-validator-node",
6
+ summary: "Official Axon mainnet validator and node-operator workflow."
7
+ }),
8
+ user: Object.freeze({
9
+ key: "user",
10
+ packageDir: "axon-mainnet-user",
11
+ skillName: "axon-mainnet-user",
12
+ summary: "Ordinary Axon mainnet wallet and chain-usage workflow."
13
+ })
14
+ });
15
+
16
+ export const LIST_TARGETS = Object.freeze([
17
+ SKILL_CATALOG.validator,
18
+ SKILL_CATALOG.user,
19
+ Object.freeze({
20
+ key: "all",
21
+ packageDir: null,
22
+ skillName: null,
23
+ summary: "Install both skills."
24
+ })
25
+ ]);
26
+
27
+ export function resolveInstallTargets(requested) {
28
+ const normalized = String(requested || "").trim().toLowerCase();
29
+
30
+ if (!normalized) {
31
+ throw new Error("Missing install target. Use one of: validator, user, all.");
32
+ }
33
+
34
+ if (normalized === "all") {
35
+ return [SKILL_CATALOG.validator, SKILL_CATALOG.user];
36
+ }
37
+
38
+ for (const entry of Object.values(SKILL_CATALOG)) {
39
+ if (normalized === entry.key || normalized === entry.skillName) {
40
+ return [entry];
41
+ }
42
+ }
43
+
44
+ throw new Error(`Unknown install target "${requested}". Use one of: validator, user, all.`);
45
+ }
46
+
47
+ export function formatList() {
48
+ return [
49
+ "Available install targets:",
50
+ "validator\tOfficial Axon mainnet validator and node-operator workflow",
51
+ "user\tOrdinary Axon mainnet wallet and chain-usage workflow",
52
+ "all\tInstall both skills"
53
+ ].join("\n");
54
+ }
package/lib/cli.js ADDED
@@ -0,0 +1,126 @@
1
+ import { formatList } from "./catalog.js";
2
+ import { installSkills } from "./install.js";
3
+
4
+ function buildHelpText() {
5
+ return [
6
+ "Usage:",
7
+ " axon-skills list",
8
+ " axon-skills install <validator|user|all> [--dest <dir>] [--force] [--dry-run]",
9
+ "",
10
+ "Examples:",
11
+ " npx @axon-chain/axon-skills list",
12
+ " npx @axon-chain/axon-skills install validator",
13
+ " npx @axon-chain/axon-skills install all --force",
14
+ "",
15
+ "Default install roots:",
16
+ " ${OPENCLAW_STATE_DIR:-~/.openclaw}/skills",
17
+ " ${AGENTS_SKILLS_DIR:-${AGENTS_HOME:-~/.agents}/skills}",
18
+ "",
19
+ "Repeat installs update existing skill folders in place."
20
+ ].join("\n");
21
+ }
22
+
23
+ function parseInstallArgs(args) {
24
+ const parsed = {
25
+ requested: args[0],
26
+ destRoot: null,
27
+ force: false,
28
+ dryRun: false
29
+ };
30
+
31
+ for (let index = 1; index < args.length; index += 1) {
32
+ const current = args[index];
33
+
34
+ if (current === "--force") {
35
+ parsed.force = true;
36
+ continue;
37
+ }
38
+
39
+ if (current === "--dry-run") {
40
+ parsed.dryRun = true;
41
+ continue;
42
+ }
43
+
44
+ if (current === "--dest") {
45
+ index += 1;
46
+ if (index >= args.length) {
47
+ throw new Error("Missing value after --dest.");
48
+ }
49
+ parsed.destRoot = args[index];
50
+ continue;
51
+ }
52
+
53
+ if (current === "--help" || current === "-h") {
54
+ parsed.help = true;
55
+ continue;
56
+ }
57
+
58
+ throw new Error(`Unknown option: ${current}`);
59
+ }
60
+
61
+ return parsed;
62
+ }
63
+
64
+ function write(stream, text) {
65
+ stream.write(`${text}\n`);
66
+ }
67
+
68
+ export async function runCli(
69
+ argv,
70
+ options = {}
71
+ ) {
72
+ const stdout = options.stdout ?? process.stdout;
73
+ const stderr = options.stderr ?? process.stderr;
74
+ const env = options.env ?? process.env;
75
+ const homedir = options.homedir;
76
+ const packageRoot = options.packageRoot;
77
+
78
+ const args = Array.isArray(argv) ? argv : [];
79
+ const command = args[0];
80
+
81
+ if (!command || command === "--help" || command === "-h" || command === "help") {
82
+ write(stdout, buildHelpText());
83
+ return 0;
84
+ }
85
+
86
+ if (command === "list") {
87
+ write(stdout, formatList());
88
+ return 0;
89
+ }
90
+
91
+ if (command === "install") {
92
+ const parsed = parseInstallArgs(args.slice(1));
93
+ if (parsed.help) {
94
+ write(stdout, buildHelpText());
95
+ return 0;
96
+ }
97
+
98
+ const result = await installSkills({
99
+ requested: parsed.requested,
100
+ destRoot: parsed.destRoot,
101
+ force: parsed.force,
102
+ dryRun: parsed.dryRun,
103
+ env,
104
+ homedir,
105
+ packageRoot
106
+ });
107
+
108
+ const actionLabel = result.dryRun ? "Planned installs:" : "Installed skills:";
109
+ write(stdout, actionLabel);
110
+ for (const item of result.installed) {
111
+ write(stdout, `- ${item.skillName} -> ${item.finalDir}`);
112
+ }
113
+ write(stdout, "");
114
+ write(stdout, "Install roots:");
115
+ for (const root of result.destinationRoots) {
116
+ write(stdout, `- ${root}`);
117
+ }
118
+ write(stdout, "Verify with: openclaw skills list");
119
+ write(stdout, "Global skills live under the .agents skills root.");
120
+ return 0;
121
+ }
122
+
123
+ write(stderr, `Unknown command: ${command}`);
124
+ write(stderr, buildHelpText());
125
+ return 1;
126
+ }
package/lib/install.js ADDED
@@ -0,0 +1,175 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ import { resolveInstallTargets } from "./catalog.js";
7
+
8
+ export function resolvePackageRoot(metaUrl = import.meta.url) {
9
+ return path.resolve(path.dirname(fileURLToPath(metaUrl)), "..");
10
+ }
11
+
12
+ export function resolveHomeDir(env = process.env, homedir = os.homedir) {
13
+ const explicit = env.HOME?.trim() || env.USERPROFILE?.trim();
14
+ if (explicit) {
15
+ return path.resolve(explicit);
16
+ }
17
+ return path.resolve(homedir());
18
+ }
19
+
20
+ export function expandUserPath(input, env = process.env, homedir = os.homedir) {
21
+ const raw = String(input || "").trim();
22
+ if (!raw) {
23
+ throw new Error("Destination path must not be empty.");
24
+ }
25
+
26
+ if (raw === "~") {
27
+ return resolveHomeDir(env, homedir);
28
+ }
29
+
30
+ if (raw.startsWith("~/")) {
31
+ return path.join(resolveHomeDir(env, homedir), raw.slice(2));
32
+ }
33
+
34
+ return path.resolve(raw);
35
+ }
36
+
37
+ export function resolveDefaultSkillsRoot(options = {}) {
38
+ const env = options.env ?? process.env;
39
+ const homedir = options.homedir ?? os.homedir;
40
+ const stateDir = env.OPENCLAW_STATE_DIR?.trim()
41
+ ? expandUserPath(env.OPENCLAW_STATE_DIR, env, homedir)
42
+ : path.join(resolveHomeDir(env, homedir), ".openclaw");
43
+ return path.join(stateDir, "skills");
44
+ }
45
+
46
+ export function resolveDefaultGlobalSkillsRoot(options = {}) {
47
+ const env = options.env ?? process.env;
48
+ const homedir = options.homedir ?? os.homedir;
49
+
50
+ if (env.AGENTS_SKILLS_DIR?.trim()) {
51
+ return expandUserPath(env.AGENTS_SKILLS_DIR, env, homedir);
52
+ }
53
+
54
+ if (env.AGENTS_HOME?.trim()) {
55
+ return path.join(expandUserPath(env.AGENTS_HOME, env, homedir), "skills");
56
+ }
57
+
58
+ return path.join(resolveHomeDir(env, homedir), ".agents", "skills");
59
+ }
60
+
61
+ export function resolveInstallRoots(options = {}) {
62
+ if (options.destRoot) {
63
+ return [expandUserPath(options.destRoot, options.env, options.homedir)];
64
+ }
65
+
66
+ const roots = [
67
+ resolveDefaultSkillsRoot(options),
68
+ resolveDefaultGlobalSkillsRoot(options)
69
+ ];
70
+
71
+ return [...new Set(roots.map((root) => path.resolve(root)))];
72
+ }
73
+
74
+ export async function pathExists(targetPath) {
75
+ try {
76
+ await fs.access(targetPath);
77
+ return true;
78
+ } catch {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ function randomSuffix() {
84
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
85
+ }
86
+
87
+ async function copyDirectoryAtomic({ sourceDir, finalDir, replaceExisting }) {
88
+ const destinationRoot = path.dirname(finalDir);
89
+ const destinationName = path.basename(finalDir);
90
+
91
+ await fs.mkdir(destinationRoot, { recursive: true });
92
+
93
+ const tmpRoot = await fs.mkdtemp(path.join(destinationRoot, `.tmp-${destinationName}-`));
94
+ const stagedDir = path.join(tmpRoot, destinationName);
95
+ let backupDir = null;
96
+
97
+ try {
98
+ await fs.cp(sourceDir, stagedDir, {
99
+ recursive: true,
100
+ force: false,
101
+ errorOnExist: true
102
+ });
103
+
104
+ const destinationExists = await pathExists(finalDir);
105
+ if (destinationExists) {
106
+ if (!replaceExisting) {
107
+ throw new Error(`Destination already exists: ${finalDir}. Re-run with --force to replace it.`);
108
+ }
109
+
110
+ backupDir = path.join(destinationRoot, `.${destinationName}.backup-${randomSuffix()}`);
111
+ await fs.rename(finalDir, backupDir);
112
+ }
113
+
114
+ await fs.rename(stagedDir, finalDir);
115
+
116
+ if (backupDir) {
117
+ await fs.rm(backupDir, { recursive: true, force: true });
118
+ backupDir = null;
119
+ }
120
+ } catch (error) {
121
+ if (backupDir && !(await pathExists(finalDir)) && (await pathExists(backupDir))) {
122
+ await fs.rename(backupDir, finalDir).catch(() => undefined);
123
+ }
124
+ throw error;
125
+ } finally {
126
+ await fs.rm(tmpRoot, { recursive: true, force: true }).catch(() => undefined);
127
+ }
128
+ }
129
+
130
+ export async function installSkills(options) {
131
+ const env = options.env ?? process.env;
132
+ const homedir = options.homedir ?? os.homedir;
133
+ const packageRoot = options.packageRoot ?? resolvePackageRoot(import.meta.url);
134
+ const replaceExisting = options.replaceExisting ?? true;
135
+ const dryRun = Boolean(options.dryRun);
136
+ const destinationRoots = resolveInstallRoots({
137
+ destRoot: options.destRoot,
138
+ env,
139
+ homedir
140
+ });
141
+
142
+ const targets = resolveInstallTargets(options.requested);
143
+ const installed = [];
144
+
145
+ for (const target of targets) {
146
+ const sourceDir = path.join(packageRoot, "skills", target.packageDir);
147
+
148
+ if (!(await pathExists(sourceDir))) {
149
+ throw new Error(`Missing packaged skill source: ${sourceDir}`);
150
+ }
151
+
152
+ for (const destinationRoot of destinationRoots) {
153
+ const finalDir = path.join(destinationRoot, target.packageDir);
154
+
155
+ if (!dryRun) {
156
+ await copyDirectoryAtomic({ sourceDir, finalDir, replaceExisting });
157
+ }
158
+
159
+ installed.push({
160
+ key: target.key,
161
+ skillName: target.skillName,
162
+ sourceDir,
163
+ finalDir,
164
+ destinationRoot
165
+ });
166
+ }
167
+ }
168
+
169
+ return {
170
+ destinationRoots,
171
+ dryRun,
172
+ replaceExisting,
173
+ installed
174
+ };
175
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@axon-chain/axon-skills",
3
+ "version": "0.1.0",
4
+ "description": "OpenClaw-installable Axon mainnet skills for validator operators and ordinary users.",
5
+ "type": "module",
6
+ "bin": {
7
+ "axon-skills": "./bin/axon-skills.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "lib",
12
+ "skills"
13
+ ],
14
+ "scripts": {
15
+ "test": "node --test"
16
+ },
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "keywords": [
24
+ "axon",
25
+ "openclaw",
26
+ "skills",
27
+ "validator",
28
+ "wallet"
29
+ ],
30
+ "license": "Apache-2.0"
31
+ }
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: axon-mainnet-user
3
+ description: Help ordinary users connect a wallet to Axon mainnet, check balances, inspect the chain through the public RPC, and send basic transactions. Use for MetaMask setup, AXON balance checks, public JSON-RPC reads, and wallet troubleshooting. Do not use for validator, SDK, or contract-deployment workflows.
4
+ metadata: {"openclaw":{"skillKey":"axon-mainnet-user","emoji":"🧭"}}
5
+ ---
6
+
7
+ # Axon Mainnet User
8
+
9
+ Use this skill when the user is an ordinary Axon mainnet participant who needs wallet-first help.
10
+
11
+ Do not use this skill for validator nodes, sync nodes, SDK integration, or contract deployment. If the user wants to run a node or become a validator, switch to `axon-validator-node`.
12
+
13
+ ## Non-negotiable rules
14
+
15
+ - Wallet-facing chain ID is `8210`.
16
+ - Public RPC is `https://mainnet-rpc.axonchain.ai/`.
17
+ - `AXON` is the gas token.
18
+ - `axon_8210-1` is the Cosmos node chain ID and should not be used as the MetaMask chain ID.
19
+ - Keep this skill wallet-first. Do not turn it into a validator workflow.
20
+
21
+ ## Workflow routing
22
+
23
+ - For wallet onboarding and network fields, read `references/mainnet-wallet.md`.
24
+ - For direct public JSON-RPC reads, read `references/basic-rpc-usage.md`.
25
+ - For wrong-network, gas, or RPC errors, read `references/user-troubleshooting.md`.
26
+
27
+ ## Response policy
28
+
29
+ - Give wallet configuration steps and public RPC examples.
30
+ - Prefer public RPC checks over node-operator commands.
31
+ - Keep advanced builder or validator topics out of scope in v1.
32
+ - If the user drifts into node operation, redirect to `axon-validator-node`.
@@ -0,0 +1,48 @@
1
+ # Basic Public JSON-RPC Usage
2
+
3
+ Use the public endpoint:
4
+
5
+ - `https://mainnet-rpc.axonchain.ai/`
6
+
7
+ ## Read the wallet-facing chain ID
8
+
9
+ ```bash
10
+ curl -s https://mainnet-rpc.axonchain.ai/ \
11
+ -H 'content-type: application/json' \
12
+ --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
13
+ ```
14
+
15
+ Expected result:
16
+
17
+ - `0x2012`, which is decimal `8210`
18
+
19
+ ## Read the latest block number
20
+
21
+ ```bash
22
+ curl -s https://mainnet-rpc.axonchain.ai/ \
23
+ -H 'content-type: application/json' \
24
+ --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
25
+ ```
26
+
27
+ ## Read an address balance
28
+
29
+ Replace `<EVM_ADDRESS>` with a `0x...` address:
30
+
31
+ ```bash
32
+ curl -s https://mainnet-rpc.axonchain.ai/ \
33
+ -H 'content-type: application/json' \
34
+ --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["<EVM_ADDRESS>","latest"],"id":1}'
35
+ ```
36
+
37
+ The balance is returned in hex wei-style units for the EVM side.
38
+
39
+ ## When to stop and redirect
40
+
41
+ If the user asks for:
42
+
43
+ - validator registration
44
+ - node sync
45
+ - P2P bootstrap
46
+ - the validator registration transaction
47
+
48
+ Switch to `axon-validator-node`.
@@ -0,0 +1,34 @@
1
+ # Axon Mainnet Wallet Setup
2
+
3
+ ## MetaMask network fields
4
+
5
+ - Network name: `Axon`
6
+ - RPC URL: `https://mainnet-rpc.axonchain.ai/`
7
+ - Chain ID: `8210`
8
+ - Currency symbol: `AXON`
9
+
10
+ ## Important distinction
11
+
12
+ - Use `8210` in MetaMask and other EVM wallets.
13
+ - `axon_8210-1` is for Cosmos node operators, not wallet setup.
14
+
15
+ ## What users can do with this setup
16
+
17
+ - view AXON balances
18
+ - send AXON transfers
19
+ - connect EVM dapps that support Axon mainnet
20
+ - inspect chain state through the public RPC
21
+
22
+ ## Before sending transactions
23
+
24
+ - confirm the wallet is on chain ID `8210`
25
+ - confirm the wallet has enough AXON for gas
26
+ - confirm the recipient address format matches the tool being used
27
+
28
+ ## Public RPC
29
+
30
+ Use only:
31
+
32
+ - `https://mainnet-rpc.axonchain.ai/`
33
+
34
+ Do not send ordinary wallet users to validator bootstrap peers or node-only chain IDs.
@@ -0,0 +1,61 @@
1
+ # User Troubleshooting
2
+
3
+ ## Wrong network in wallet
4
+
5
+ Symptoms:
6
+
7
+ - transactions fail immediately
8
+ - wallet shows a different chain
9
+ - dapp requests a chain switch
10
+
11
+ Fix:
12
+
13
+ - set MetaMask chain ID to `8210`
14
+ - keep RPC URL at `https://mainnet-rpc.axonchain.ai/`
15
+ - do not use `axon_8210-1` in wallet configuration
16
+
17
+ ## No gas for a transaction
18
+
19
+ Symptoms:
20
+
21
+ - wallet says insufficient funds
22
+ - transfer cannot be signed or broadcast
23
+
24
+ Fix:
25
+
26
+ - fund the wallet with `AXON`
27
+ - leave some AXON unspent for gas
28
+
29
+ ## Public RPC seems unavailable
30
+
31
+ Checks:
32
+
33
+ ```bash
34
+ curl -s https://mainnet-rpc.axonchain.ai/ \
35
+ -H 'content-type: application/json' \
36
+ --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
37
+ ```
38
+
39
+ If this fails:
40
+
41
+ - retry later
42
+ - confirm the wallet or app is still pointed at the public Axon RPC
43
+ - avoid switching the user into validator-only node endpoints
44
+
45
+ ## Chain ID confusion
46
+
47
+ - `8210` is the EVM wallet chain ID
48
+ - `axon_8210-1` is the Cosmos node chain ID
49
+
50
+ Correct the configuration before moving on.
51
+
52
+ ## Out-of-scope requests
53
+
54
+ If the user asks to:
55
+
56
+ - run a validator
57
+ - bootstrap a node
58
+ - troubleshoot peers
59
+ - submit the validator registration transaction
60
+
61
+ Switch to `axon-validator-node`.
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: axon-validator-node
3
+ description: Help users bootstrap, fund, sync, create, verify, and operate an Axon mainnet validator with the official script-based workflow. Use for validator onboarding, sync checks, create-validator troubleshooting, fee issues, peer issues, key handling, and ongoing node operations.
4
+ metadata: {"openclaw":{"skillKey":"axon-validator-node","emoji":"⛓️"}}
5
+ ---
6
+
7
+ # Axon Validator Node
8
+
9
+ Use this skill when the user wants to run, recover, verify, or troubleshoot an Axon mainnet validator or sync node.
10
+
11
+ Do not use this skill for ordinary wallet usage. If the user only needs MetaMask, balances, transfers, or public RPC reads, switch to `axon-mainnet-user`.
12
+
13
+ ## Non-negotiable rules
14
+
15
+ - Mainnet node identity uses Cosmos chain ID `axon_8210-1`.
16
+ - Wallet-facing EVM tooling uses chain ID `8210`.
17
+ - Prefer the official script workflow from the `axon-chain/axon` repository.
18
+ - Do not suggest a manual `axond init` plus manual genesis-fetch flow unless the user explicitly needs low-level debugging.
19
+ - Do not tell the user to submit `create-validator` until the validator account is funded, local RPC is up, and `catching_up` is `false`.
20
+ - Treat the mnemonic, `KEYRING_PASSWORD_FILE`, validator key files, and any signing key material as sensitive.
21
+ - Current practical transaction default: use `GAS_PRICES=1000000000aaxon` unless the local helper already sets a non-zero gas price.
22
+
23
+ ## Workflow routing
24
+
25
+ - For first-time setup, rebuilds, or exact shell commands, read `references/mainnet-validator.md`.
26
+ - For backups, exposed ports, double-sign prevention, and key hygiene, read `references/validator-security.md`.
27
+ - For fee failures, peer failures, sync lag, or missing validator status, read `references/validator-troubleshooting.md`.
28
+
29
+ ## Response policy
30
+
31
+ - Give exact commands when operating the node.
32
+ - Keep `axon_8210-1` and `8210` clearly separated in the response.
33
+ - Prefer `start_validator_node.sh` and `start_sync_node.sh` over lower-level alternatives.
34
+ - If the user asks to proceed with `create-validator`, explicitly check sync state, funding state, and local RPC readiness first.
@@ -0,0 +1,149 @@
1
+ # Axon Mainnet Validator Workflow
2
+
3
+ ## Network constants
4
+
5
+ - Cosmos chain ID: `axon_8210-1`
6
+ - EVM chain ID: `8210`
7
+ - Public wallet RPC: `https://mainnet-rpc.axonchain.ai/`
8
+ - Bootstrap peer: `e47ec82a1d08a371e3c235e6554496be2f114eae@mainnet-node.axonchain.ai:26656`
9
+ - Genesis file: `https://raw.githubusercontent.com/axon-chain/axon/main/docs/mainnet/genesis.json`
10
+ - Bootstrap peers file: `https://raw.githubusercontent.com/axon-chain/axon/main/docs/mainnet/bootstrap_peers.txt`
11
+
12
+ ## Preferred working directory
13
+
14
+ Use `/opt/axon-node` for bare metal and containerized installs.
15
+
16
+ ## Bootstrap the validator workspace
17
+
18
+ ```bash
19
+ mkdir -p /opt/axon-node
20
+ cd /opt/axon-node
21
+
22
+ curl -fsSLo start_validator_node.sh https://raw.githubusercontent.com/axon-chain/axon/main/scripts/start_validator_node.sh
23
+ curl -fsSLo start_sync_node.sh https://raw.githubusercontent.com/axon-chain/axon/main/scripts/start_sync_node.sh
24
+ curl -fsSLo genesis.json https://raw.githubusercontent.com/axon-chain/axon/main/docs/mainnet/genesis.json
25
+ curl -fsSLo bootstrap_peers.txt https://raw.githubusercontent.com/axon-chain/axon/main/docs/mainnet/bootstrap_peers.txt
26
+ chmod 0755 start_validator_node.sh start_sync_node.sh
27
+ printf 'replace-with-a-strong-passphrase\n' > keyring.pass
28
+ chmod 0600 keyring.pass
29
+ ```
30
+
31
+ ## Optional: pre-download `axond`
32
+
33
+ For Linux AMD64:
34
+
35
+ ```bash
36
+ curl -fsSLo axond https://github.com/axon-chain/axon/releases/latest/download/axond_linux_amd64
37
+ curl -fsSLo axond.sha256 https://github.com/axon-chain/axon/releases/latest/download/axond_linux_amd64.sha256
38
+ echo "$(cat axond.sha256) axond" | sha256sum -c -
39
+ chmod 0755 axond
40
+ ```
41
+
42
+ Adjust the release asset URL for a different platform if needed.
43
+
44
+ ## Initialize the validator account
45
+
46
+ ```bash
47
+ cd /opt/axon-node
48
+ KEYRING_PASSWORD_FILE=/opt/axon-node/keyring.pass ./start_validator_node.sh init
49
+ ```
50
+
51
+ What this creates:
52
+
53
+ - `data/validator.address`
54
+ - `data/validator.valoper`
55
+ - `data/validator.consensus_pubkey.json`
56
+ - `data/peer_info.txt`
57
+
58
+ The mnemonic is only printed once when a new account is generated.
59
+
60
+ ## Start the node
61
+
62
+ ```bash
63
+ cd /opt/axon-node
64
+ KEYRING_PASSWORD_FILE=/opt/axon-node/keyring.pass ./start_validator_node.sh start
65
+ ```
66
+
67
+ Default ports:
68
+
69
+ - P2P: `26656`
70
+ - CometBFT RPC: `26657`
71
+ - JSON-RPC: `8545`
72
+ - JSON-RPC WS: `8546`
73
+ - REST API: `1317`
74
+ - gRPC: `9090`
75
+
76
+ ## Verify sync before any validator transaction
77
+
78
+ ```bash
79
+ curl -s http://127.0.0.1:26657/status | jq '.result.sync_info'
80
+ ```
81
+
82
+ Wait until:
83
+
84
+ - latest block height is moving
85
+ - peer count is non-zero
86
+ - `catching_up` becomes `false`
87
+
88
+ Do not submit `create-validator` before `catching_up=false`.
89
+
90
+ ## Fund the validator account
91
+
92
+ Read the address from `data/validator.address` and transfer AXON to it.
93
+
94
+ Example:
95
+
96
+ ```bash
97
+ cat /opt/axon-node/data/validator.address
98
+ ```
99
+
100
+ ## Submit `create-validator`
101
+
102
+ Use a non-zero gas price. Current safe default:
103
+
104
+ - `GAS_PRICES=1000000000aaxon`
105
+
106
+ Command:
107
+
108
+ ```bash
109
+ cd /opt/axon-node
110
+ KEYRING_PASSWORD_FILE=/opt/axon-node/keyring.pass \
111
+ COMETBFT_RPC=http://127.0.0.1:26657 \
112
+ GAS_PRICES=1000000000aaxon \
113
+ ./start_validator_node.sh create-validator
114
+ ```
115
+
116
+ Notes:
117
+
118
+ - The official script default self-bond is `100 AXON` unless `VALIDATOR_STAKE` is overridden.
119
+ - The node must already be running locally when you point `COMETBFT_RPC` at `http://127.0.0.1:26657`.
120
+
121
+ ## Verify validator status
122
+
123
+ Query the transaction if needed:
124
+
125
+ ```bash
126
+ curl -s http://127.0.0.1:1317/cosmos/tx/v1beta1/txs/<TX_HASH>
127
+ ```
128
+
129
+ Query the validator:
130
+
131
+ ```bash
132
+ axond query staking validator "$(cat /opt/axon-node/data/validator.valoper)" \
133
+ --node http://127.0.0.1:26657 \
134
+ --output json
135
+ ```
136
+
137
+ Useful success indicators:
138
+
139
+ - validator exists on chain
140
+ - validator has tokens bonded
141
+ - status is `BOND_STATUS_BONDED` or the expected waiting-state for the network
142
+
143
+ ## Ongoing operations
144
+
145
+ - Logs: `tail -f /opt/axon-node/data/node.log`
146
+ - Sync info: `curl -s http://127.0.0.1:26657/status | jq '.result.sync_info'`
147
+ - Validator address: `cat /opt/axon-node/data/validator.valoper`
148
+ - Bootstrap peers should stay aligned with the official repository
149
+ - Set `P2P_EXTERNAL_ADDRESS=host:26656` only on a public inbound validator
@@ -0,0 +1,50 @@
1
+ # Validator Security
2
+
3
+ ## Sensitive material
4
+
5
+ Treat these as secrets:
6
+
7
+ - mnemonic
8
+ - `keyring.pass`
9
+ - `data/node/config/priv_validator_key.json`
10
+ - any imported mnemonic source file
11
+
12
+ Do not paste them into chat, public issues, or shared docs.
13
+
14
+ ## Backups
15
+
16
+ Back up at least:
17
+
18
+ - `/opt/axon-node/keyring.pass`
19
+ - `/opt/axon-node/data/validator.address`
20
+ - `/opt/axon-node/data/validator.valoper`
21
+ - `/opt/axon-node/data/node/config/priv_validator_key.json`
22
+ - the mnemonic if the validator account was generated locally
23
+
24
+ Keep backups offline when possible.
25
+
26
+ ## Double-sign prevention
27
+
28
+ - Never run the same validator signing key on two live machines at the same time.
29
+ - If moving the validator, stop the old node completely before starting the new one.
30
+ - Treat failover automation as high risk unless the handoff is explicit and verified.
31
+
32
+ ## RPC and network exposure
33
+
34
+ - Leave `P2P_EXTERNAL_ADDRESS` unset on ordinary outbound-only nodes.
35
+ - Only expose `26656` when the node should accept inbound peers.
36
+ - Keep admin access to the host locked down.
37
+ - Do not expose key material over network shares.
38
+
39
+ ## Operational hygiene
40
+
41
+ - Use a dedicated machine or VM for validator duties.
42
+ - Keep system time correct and disks healthy.
43
+ - Monitor logs for downtime and peer churn.
44
+ - Review upgrade notes before restarting production validators.
45
+
46
+ ## Chain ID hygiene
47
+
48
+ - Use `axon_8210-1` for node commands and validator operations.
49
+ - Use `8210` for wallet-facing EVM tools only.
50
+ - Do not mix them.
@@ -0,0 +1,95 @@
1
+ # Validator Troubleshooting
2
+
3
+ ## `fee not provided` or `insufficient fee`
4
+
5
+ Symptom:
6
+
7
+ - `create-validator` returns `fee not provided`
8
+ - raw log mentions insufficient fee
9
+
10
+ Fix:
11
+
12
+ ```bash
13
+ cd /opt/axon-node
14
+ KEYRING_PASSWORD_FILE=/opt/axon-node/keyring.pass \
15
+ COMETBFT_RPC=http://127.0.0.1:26657 \
16
+ GAS_PRICES=1000000000aaxon \
17
+ ./start_validator_node.sh create-validator
18
+ ```
19
+
20
+ Use a non-zero gas price until the local helper script already defaults to one.
21
+
22
+ ## `catching_up` stays `true`
23
+
24
+ Checks:
25
+
26
+ ```bash
27
+ curl -s http://127.0.0.1:26657/status | jq '.result.sync_info'
28
+ cat /opt/axon-node/data/peer_info.txt
29
+ tail -f /opt/axon-node/data/node.log
30
+ ```
31
+
32
+ Actions:
33
+
34
+ - confirm the bootstrap peers file matches the official repository
35
+ - confirm the node has reachable peers
36
+ - confirm disk and network are not constrained
37
+ - wait before any validator transaction
38
+
39
+ Do not submit `create-validator` until `catching_up=false`.
40
+
41
+ ## Missing `KEYRING_PASSWORD_FILE`
42
+
43
+ Symptoms:
44
+
45
+ - the script refuses to run
46
+ - the keyring password file is missing or empty
47
+
48
+ Fix:
49
+
50
+ ```bash
51
+ ls -l /opt/axon-node/keyring.pass
52
+ cat /opt/axon-node/keyring.pass
53
+ chmod 0600 /opt/axon-node/keyring.pass
54
+ ```
55
+
56
+ Use a non-empty local passphrase file and point `KEYRING_PASSWORD_FILE` at it.
57
+
58
+ ## Validator not found after submission
59
+
60
+ Checks:
61
+
62
+ ```bash
63
+ curl -s http://127.0.0.1:1317/cosmos/tx/v1beta1/txs/<TX_HASH>
64
+ axond query staking validator "$(cat /opt/axon-node/data/validator.valoper)" \
65
+ --node http://127.0.0.1:26657 \
66
+ --output json
67
+ ```
68
+
69
+ Possible causes:
70
+
71
+ - the transaction never landed
72
+ - the validator transaction was rejected
73
+ - the wrong RPC endpoint was queried
74
+
75
+ ## No peers or unstable peers
76
+
77
+ Checks:
78
+
79
+ ```bash
80
+ tail -f /opt/axon-node/data/node.log
81
+ cat /opt/axon-node/bootstrap_peers.txt
82
+ ```
83
+
84
+ Actions:
85
+
86
+ - re-sync the official bootstrap peers file
87
+ - verify outbound connectivity to `mainnet-node.axonchain.ai:26656`
88
+ - only set `P2P_EXTERNAL_ADDRESS` when the host is publicly reachable
89
+
90
+ ## Chain ID confusion
91
+
92
+ - `axon_8210-1` is the Cosmos chain ID for validator and node commands
93
+ - `8210` is the EVM chain ID for wallets
94
+
95
+ If the user mixes them, correct the command before continuing.