Skip to content

Conversation

@Mobile-Crest
Copy link

@Mobile-Crest Mobile-Crest commented Dec 3, 2025

Add CLI commands for axon management

Closes #225

Summary

This PR adds two new CLI commands to manage axon serving information for neurons on the Bittensor network:

  • btcli axon reset - Resets axon to stop serving (sets IP to 0.0.0.0 and port to 1 as chain doesn't accept 0 as invalid port)
  • btcli axon set - Sets axon serving endpoint with specified IP and port

Motivation

Miners and validators need a convenient way to manage their axon serving endpoints directly from the CLI without writing custom scripts. This is particularly useful for:

  • Temporarily stopping a neuron from serving
  • Updating serving endpoints when changing infrastructure
  • Managing multiple neurons across different subnets

Changes

New Files

  1. bittensor_cli/src/bittensor/extrinsics/serving.py

    • reset_axon_extrinsic() - Handles axon reset logic
    • set_axon_extrinsic() - Handles axon set logic
    • ip_to_int() - Utility for IP address conversion
    • Includes validation, error handling, and user prompts
  2. tests/unit_tests/test_axon_commands.py

    • 16 comprehensive unit tests
    • Tests for IP conversion, extrinsic functions, and CLI handlers
    • 100% test coverage for new functionality
  3. tests/e2e_tests/test_axon.py

    • 3 end-to-end tests against local blockchain
    • Tests full workflow, IPv6 support, and error handling

Modified Files

  1. bittensor_cli/cli.py
    • Added import for serving extrinsics module
    • Created axon_app Typer application
    • Registered axon app with main CLI
    • Added axon_reset() command handler
    • Added axon_set() command handler
    • Registered commands with the axon app

Usage

Reset Axon

Stop a neuron from serving by resetting its axon information:

btcli axon reset --netuid 1 --wallet-name my_wallet --wallet-hotkey my_hotkey

Set Axon

Configure a neuron's serving endpoint:

# IPv4
btcli axon set --netuid 1 --ip 192.168.1.100 --port 8091 \
  --wallet-name my_wallet --wallet-hotkey my_hotkey

# IPv6
btcli axon set --netuid 1 --ip 2001:db8::1 --port 8091 --ip-type 6 \
  --wallet-name my_wallet --wallet-hotkey my_hotkey

Available Options

Both commands support:

  • --netuid - Subnet ID (required)
  • --wallet-name - Wallet name
  • --wallet-hotkey - Hotkey name
  • --wallet-path - Custom wallet path
  • --network - Network to connect to (default: finney)
  • --prompt / --no-prompt - Interactive confirmation
  • --wait-for-inclusion - Wait for block inclusion
  • --wait-for-finalization - Wait for finalization
  • --quiet - Minimal output
  • --verbose - Detailed output

Additional for set:

  • --ip - IP address (required)
  • --port - Port number 0-65535 (required)
  • --ip-type - 4 for IPv4, 6 for IPv6 (default: 4)
  • --protocol - Protocol version (default: 4)

Examples

Example 1: Reset axon for maintenance

btcli axon reset --netuid 1 --wallet-name validator --wallet-hotkey default

Example 2: Set axon with public IP

btcli axon set --netuid 1 --ip 203.0.113.42 --port 8091 \
  --wallet-name miner --wallet-hotkey default

Example 3: Set axon without prompts (automation)

btcli axon set --netuid 1 --ip 192.168.1.100 --port 8091 \
  --wallet-name miner --wallet-hotkey default --no-prompt

Testing

Unit Tests

# Run all unit tests
python -m pytest tests/unit_tests/test_axon_commands.py -v

Test Coverage:

  • ✅ IP address conversion (IPv4, IPv6, validation)
  • ✅ Reset axon extrinsic (success, failures, cancellation)
  • ✅ Set axon extrinsic (success, validation, IPv6, errors)
  • ✅ CLI command handlers

E2E Tests

# Run e2e tests (requires Docker)
python -m pytest tests/e2e_tests/test_axon.py -v

Test Coverage:

  • ✅ Full workflow (create subnet, register, set, verify, reset)
  • ✅ IPv6 address support
  • ✅ Invalid input handling

Implementation Details

Technical Approach

  1. Extrinsic Composition

    • Uses SubtensorModule::serve_axon call
    • Parameters: netuid, version, ip, port, ip_type, protocol, placeholders
    • Signed with hotkey (not coldkey)
  2. IP Address Handling

    • Converts IP strings to integers using netaddr library
    • Supports both IPv4 and IPv6
    • Validates format before submission
  3. Error Handling

    • Input validation (IP format, port range)
    • Hotkey unlock verification
    • Blockchain submission errors
    • User-friendly error messages
  4. User Experience

    • Optional confirmation prompts
    • Rich console output with colors and icons
    • Progress indicators during submission
    • Clear success/failure messages

Security Considerations

  • Hotkey must be unlocked to sign extrinsic
  • Extrinsic signed with hotkey (not coldkey)
  • IP and port are publicly visible on blockchain
  • No sensitive information logged

Contribution by Gittensor, learn more at https://gittensor.io/

@thewhaleking thewhaleking requested a review from a team December 3, 2025 12:27
Copy link
Contributor

@thewhaleking thewhaleking left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is at least better than #725, but still has a lot going on that should have been verified before submitting.

@Mobile-Crest Mobile-Crest marked this pull request as draft December 3, 2025 13:50
@Mobile-Crest Mobile-Crest marked this pull request as ready for review December 3, 2025 19:20
@Mobile-Crest Mobile-Crest marked this pull request as draft December 3, 2025 19:23
@Mobile-Crest Mobile-Crest marked this pull request as ready for review December 3, 2025 19:43
@Mobile-Crest
Copy link
Author

@thewhaleking
can you please review this pr again?

Copy link
Contributor

@thewhaleking thewhaleking left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks better. Will do a full review tomorrow.

# is_success is a property that returns a coroutine
async def mock_is_success():
return True
mock_response.is_success = mock_is_success()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be careful with this. It's fine here, but be careful in future situations as this will assign the coroutine object as is_success rather than the coroutine function property as it is usually used.

Typically you can do

await response.is_success
await response.is_success
await response.is_success
...

because it's a property, but by assigning the result of a coroutine function to it, you can only do it once, and then you'll raise exceptions if you try to await it again. It can be a pain to debug.

thewhaleking
thewhaleking previously approved these changes Dec 4, 2025
Copy link
Contributor

@thewhaleking thewhaleking left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Mobile-Crest
Copy link
Author

@thewhaleking
can you merge this pr if all looks good?

@thewhaleking
Copy link
Contributor

@thewhaleking can you merge this pr if all looks good?

Think we're not going to include this in 9.16, as it's already quite filled up. Should merge to staging after release of 9.16 (either today or Monday, coinciding with the release of mainnet)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants