feat: Add Mycelium IPv6 overlay networking

- Integrate Mycelium IPv6 overlay networking
- Add Mycelium configuration options to HeroPods
- Enable Mycelium setup and cleanup for containers
- Include Mycelium examples and documentation
This commit is contained in:
Mahmoud-Emad
2025-11-19 14:23:06 +02:00
parent 9986dca758
commit 1452d65f48
11 changed files with 1280 additions and 59 deletions

View File

@@ -0,0 +1,207 @@
# Mycelium IPv6 Overlay Network Integration for HeroPods
## Overview
HeroPods now supports Mycelium IPv6 overlay networking, providing end-to-end encrypted IPv6 connectivity for containers across the internet.
## What is Mycelium?
Mycelium is an IPv6 overlay network that provides:
- **End-to-end encrypted** connectivity in the `400::/7` address range
- **Peer-to-peer routing** through public relay nodes
- **Automatic address assignment** based on cryptographic keys
- **NAT traversal** for containers behind firewalls
## Architecture
### Components
1. **mycelium.v** - Core Mycelium integration logic
- Installation and service management
- Container IPv6 configuration
- veth pair creation for IPv6 routing
2. **heropods_model.v** - Configuration struct
- `MyceliumConfig` struct with enable flag, peers, key path
3. **container.v** - Lifecycle integration
- Mycelium setup during container start
- Mycelium cleanup during container stop/delete
### How It Works
1. **Host Setup**:
- Mycelium service runs on the host
- Connects to public peer nodes for routing
- Gets a unique IPv6 address in `400::/7` range
2. **Container Setup**:
- Creates a veth pair (`vmy-HASH``vmyh-HASH`)
- Assigns container IPv6 from host's `/64` prefix
- Configures routing through host's Mycelium interface
3. **Connectivity**:
- Container can reach other Mycelium nodes via IPv6
- Traffic is encrypted end-to-end
- Works across NAT and firewalls
## Configuration
### Enable Mycelium
All parameters are **required** when enabling Mycelium:
```heroscript
!!heropods.configure
name:'demo'
!!heropods.enable_mycelium
heropods:'demo'
version:'v0.5.6'
ipv6_range:'400::/7'
key_path:'~/hero/cfg/priv_key.bin'
peers:[
'tcp://185.69.166.8:9651',
'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651',
'tcp://65.109.18.113:9651'
]
```
### Configuration Parameters
All parameters are **required**:
- `version` (string): Mycelium version to install (e.g., 'v0.5.6')
- `ipv6_range` (string): Mycelium IPv6 address range (e.g., '400::/7')
- `key_path` (string): Path to Mycelium private key (e.g., '~/hero/cfg/priv_key.bin')
- `peers` (array of strings): Array of Mycelium peer addresses
### Default Public Peers
You can use these public Mycelium peers:
```text
tcp://185.69.166.8:9651
quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651
tcp://65.109.18.113:9651
quic://[2a01:4f9:5a:1042::2]:9651
tcp://5.78.122.16:9651
quic://[2a01:4ff:1f0:8859::1]:9651
tcp://5.223.43.251:9651
quic://[2a01:4ff:2f0:3621::1]:9651
tcp://142.93.217.194:9651
quic://[2400:6180:100:d0::841:2001]:9651
```
## Usage Example
See `examples/virt/heropods/container_mycelium.heroscript` for a complete example:
**Basic example:**
```heroscript
// Configure HeroPods
!!heropods.configure
name:'mycelium_demo'
// Enable Mycelium with all required parameters
!!heropods.enable_mycelium
heropods:'mycelium_demo'
version:'v0.5.6'
ipv6_range:'400::/7'
key_path:'~/hero/cfg/priv_key.bin'
peers:[
'tcp://185.69.166.8:9651',
'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651'
]
// Create and start container
!!heropods.container_new
name:'my_container'
image:'alpine_3_20'
!!heropods.container_start
name:'my_container'
// Test Mycelium connectivity
!!heropods.container_exec
name:'my_container'
cmd:'ip -6 addr show'
stdout:true
```
**Run the complete example:**
```bash
hero run examples/virt/heropods/container_mycelium.heroscript
```
## Network Details
### IPv6 Address Assignment
- Host gets address like: `400:1234:5678::1`
- Container gets address like: `400:1234:5678::2`
- Uses `/64` prefix from host's Mycelium address
### Routing
- Container → Host: via veth pair link-local addresses
- Host → Mycelium network: via Mycelium TUN interface
- End-to-end encryption handled by Mycelium
### Interface Names
- Container side: `vmy-HASH` (6-char hash of container name)
- Host side: `vmyh-HASH`
- Mycelium TUN: `mycelium0` (configurable)
## Troubleshooting
### Check Mycelium Status
```bash
mycelium inspect --key-file ~/hero/cfg/priv_key.bin --json
```
### Verify Container IPv6
```bash
# Inside container
ip -6 addr show
ip -6 route show
```
### Test Connectivity
```bash
# Ping a public Mycelium node
ping6 -c 3 400:8f3a:8d0e:3503:db8e:6a02:2e9:83dd
```
### Common Issues
1. **Mycelium service not running**: Check with `ps aux | grep mycelium`
2. **No IPv6 connectivity**: Verify IPv6 forwarding is enabled: `sysctl net.ipv6.conf.all.forwarding`
3. **Container can't reach Mycelium network**: Check routes with `ip -6 route show`
## Security
- All Mycelium traffic is end-to-end encrypted
- Each node has a unique cryptographic identity
- Private key stored at `~/hero/cfg/priv_key.bin` (configurable)
- Container inherits host's Mycelium identity
## Performance
- Minimal overhead for local routing
- Peer-to-peer routing for optimal paths
- Automatic failover between peer nodes
## Future Enhancements
- Per-container Mycelium identities
- Custom routing policies
- IPv6 firewall rules
- Mycelium network isolation

View File

@@ -154,6 +154,27 @@ pub fn (mut self Container) start() ! {
return error('Failed to setup network for container: ${err}')
}
// Setup Mycelium IPv6 overlay network if enabled
if self.factory.mycelium_config.enabled {
container_pid := self.pid()!
self.factory.mycelium_setup_container(self.name, container_pid) or {
self.factory.logger.log(
cat: 'container'
log: 'Mycelium setup failed, stopping container: ${err}'
logtype: .error
) or {}
// Stop container to clean up
self.stop() or {
self.factory.logger.log(
cat: 'container'
log: 'Failed to stop container during Mycelium cleanup: ${err}'
logtype: .error
) or {}
}
return error('Failed to setup Mycelium for container: ${err}')
}
}
self.factory.logger.log(
cat: 'container'
log: 'Container ${self.name} started'
@@ -415,9 +436,21 @@ fn (mut self Container) setup_network() ! {
//
// Delegates to HeroPods.network_cleanup_container() which uses network_mutex
// for thread-safe IP deallocation and network cleanup.
// Also cleans up Mycelium IPv6 overlay network if enabled.
fn (mut self Container) cleanup_network() ! {
mut factory := self.factory
factory.network_cleanup_container(self.name)!
// Cleanup Mycelium IPv6 overlay network if enabled
if factory.mycelium_config.enabled {
factory.mycelium_cleanup_container(self.name) or {
factory.logger.log(
cat: 'container'
log: 'Warning: Failed to cleanup Mycelium for container ${self.name}: ${err}'
logtype: .error
) or {}
}
}
}
// Check if container exists in crun (regardless of its state)

View File

@@ -32,19 +32,32 @@ pub mut:
subnet string = '10.10.0.0/24'
gateway_ip string = '10.10.0.1'
dns_servers []string = ['8.8.8.8', '8.8.4.4']
// Mycelium IPv6 overlay network configuration
enable_mycelium bool // Enable Mycelium IPv6 overlay network
mycelium_version string // Mycelium version to install (default: 'v0.5.6')
mycelium_ipv6_range string // Mycelium IPv6 address range (default: '400::/7')
mycelium_peers []string // Mycelium peer addresses (default: use public nodes)
mycelium_key_path string = '~/hero/cfg/priv_key.bin' // Path to Mycelium private key
}
pub fn new(args ArgsGet) !&HeroPods {
mut obj := HeroPods{
name: args.name
reset: args.reset
use_podman: args.use_podman
network_config: NetworkConfig{
name: args.name
reset: args.reset
use_podman: args.use_podman
network_config: NetworkConfig{
bridge_name: args.bridge_name
subnet: args.subnet
gateway_ip: args.gateway_ip
dns_servers: args.dns_servers
}
mycelium_config: MyceliumConfig{
enabled: args.enable_mycelium
version: args.mycelium_version
ipv6_range: args.mycelium_ipv6_range
peers: args.mycelium_peers
key_path: args.mycelium_key_path
}
}
set(obj)!
return get(name: args.name)!
@@ -163,6 +176,47 @@ pub fn play(mut plbook PlayBook) ! {
action.done = true
}
// Process heropods.enable_mycelium actions
for mut action in plbook.find(filter: 'heropods.enable_mycelium')! {
mut p := action.params
heropods_name := p.get_default('heropods', heropods_default)!
mut hp := get(name: heropods_name)!
// Validate required parameters
mycelium_version := p.get('version') or {
return error('heropods.enable_mycelium: "version" is required (e.g., version:\'v0.5.6\')')
}
mycelium_ipv6_range := p.get('ipv6_range') or {
return error('heropods.enable_mycelium: "ipv6_range" is required (e.g., ipv6_range:\'400::/7\')')
}
mycelium_key_path := p.get('key_path') or {
return error('heropods.enable_mycelium: "key_path" is required (e.g., key_path:\'~/hero/cfg/priv_key.bin\')')
}
peers_array := p.get_list('peers') or {
return error('heropods.enable_mycelium: "peers" is required. Provide array of peer addresses (e.g., peers:[\'tcp://185.69.166.8:9651\', \'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651\'])')
}
// Validate peers list is not empty
if peers_array.len == 0 {
return error('heropods.enable_mycelium: "peers" cannot be empty. Provide at least one peer address.')
}
// Update Mycelium configuration
hp.mycelium_config.enabled = true
hp.mycelium_config.version = mycelium_version
hp.mycelium_config.ipv6_range = mycelium_ipv6_range
hp.mycelium_config.key_path = mycelium_key_path
hp.mycelium_config.peers = peers_array
// Initialize Mycelium if not already done
hp.mycelium_init()!
// Save updated configuration
set(hp)!
action.done = true
}
// Process heropods.container_new actions
for mut action in plbook.find(filter: 'heropods.container_new')! {
mut p := action.params

View File

@@ -12,6 +12,18 @@ pub const version = '0.0.0'
const singleton = false
const default = true
// MyceliumConfig holds Mycelium IPv6 overlay network configuration
struct MyceliumConfig {
pub mut:
enabled bool // Whether Mycelium is enabled
version string // Mycelium version to install (e.g., 'v0.5.6')
ipv6_range string // Mycelium IPv6 address range (e.g., '400::/7')
peers []string // Mycelium peer addresses
key_path string // Path to Mycelium private key
mycelium_ip6 string // Host's Mycelium IPv6 address (cached)
interface_name string // Mycelium TUN interface name (e.g., "mycelium0")
}
// HeroPods factory for managing containers
//
// Thread Safety:
@@ -21,17 +33,18 @@ const default = true
@[heap]
pub struct HeroPods {
pub mut:
tmux_session string // tmux session name
containers map[string]&Container // name -> container mapping
images map[string]&ContainerImage // name -> image mapping
crun_configs map[string]&crun.CrunConfig // name -> crun config mapping
base_dir string // base directory for all container data
reset bool // will reset the heropods
use_podman bool = true // will use podman for image management
name string // name of the heropods
network_config NetworkConfig @[skip; str: skip] // network configuration (automatically initialized, not serialized)
network_mutex sync.Mutex @[skip; str: skip] // protects network_config for thread-safe concurrent access
logger logger.Logger @[skip; str: skip] // logger instance for debugging (not serialized)
tmux_session string // tmux session name
containers map[string]&Container // name -> container mapping
images map[string]&ContainerImage // name -> image mapping
crun_configs map[string]&crun.CrunConfig // name -> crun config mapping
base_dir string // base directory for all container data
reset bool // will reset the heropods
use_podman bool = true // will use podman for image management
name string // name of the heropods
network_config NetworkConfig @[skip; str: skip] // network configuration (automatically initialized, not serialized)
network_mutex sync.Mutex @[skip; str: skip] // protects network_config for thread-safe concurrent access
mycelium_config MyceliumConfig // mycelium IPv6 overlay network configuration
logger logger.Logger @[skip; str: skip] // logger instance for debugging (not serialized)
}
// obj_init performs lightweight validation and field normalization only
@@ -69,6 +82,11 @@ fn obj_init(mycfg_ HeroPods) !HeroPods {
mycfg.network_config.allocated_ips = map[string]string{}
}
// Initialize Mycelium configuration defaults (only for non-required fields)
if mycfg.mycelium_config.interface_name == '' {
mycfg.mycelium_config.interface_name = 'mycelium0'
}
return mycfg
}
@@ -104,6 +122,11 @@ fn (mut self HeroPods) initialize() ! {
// Initialize network layer
self.network_init()!
// Initialize Mycelium IPv6 overlay network if enabled
if self.mycelium_config.enabled {
self.mycelium_init()!
}
// Load existing images into cache
self.load_existing_images()!

View File

@@ -0,0 +1,422 @@
module heropods
import incubaid.herolib.osal.core as osal
import incubaid.herolib.clients.mycelium
import incubaid.herolib.installers.net.mycelium_installer
import time
import crypto.sha256
// Initialize Mycelium for HeroPods
//
// This method:
// 1. Validates required configuration
// 2. Installs Mycelium binary if not present
// 3. Starts Mycelium service with configured peers
// 4. Retrieves the host's Mycelium IPv6 address
//
// Thread Safety:
// This is called during HeroPods initialization, before any concurrent operations.
fn (mut self HeroPods) mycelium_init() ! {
if !self.mycelium_config.enabled {
return
}
// Validate required configuration
if self.mycelium_config.version == '' {
return error('Mycelium configuration error: "version" is required. Use heropods.enable_mycelium to configure.')
}
if self.mycelium_config.ipv6_range == '' {
return error('Mycelium configuration error: "ipv6_range" is required. Use heropods.enable_mycelium to configure.')
}
if self.mycelium_config.key_path == '' {
return error('Mycelium configuration error: "key_path" is required. Use heropods.enable_mycelium to configure.')
}
if self.mycelium_config.peers.len == 0 {
return error('Mycelium configuration error: "peers" is required. Use heropods.enable_mycelium to configure.')
}
self.logger.log(
cat: 'mycelium'
log: 'START mycelium_init() - Initializing Mycelium IPv6 overlay network'
) or {}
// Check if Mycelium is already installed and running
if mycelium_installed := self.mycelium_check_installed() {
if mycelium_installed {
self.logger.log(
cat: 'mycelium'
log: 'Mycelium is already installed'
logtype: .stdout
) or {}
} else {
// Install Mycelium
self.mycelium_install()!
}
}
// Start Mycelium service if not running
if mycelium_running := self.mycelium_check_running() {
if mycelium_running {
self.logger.log(
cat: 'mycelium'
log: 'Mycelium service is already running'
logtype: .stdout
) or {}
} else {
self.mycelium_start_service()!
}
}
// Get and cache the host's Mycelium IPv6 address
self.mycelium_get_host_address()!
self.logger.log(
cat: 'mycelium'
log: 'END mycelium_init() - Mycelium initialized successfully with address ${self.mycelium_config.mycelium_ip6}'
logtype: .stdout
) or {}
}
// Check if Mycelium binary is installed
fn (mut self HeroPods) mycelium_check_installed() !bool {
return osal.cmd_exists('mycelium')
}
// Check if Mycelium service is running
fn (mut self HeroPods) mycelium_check_running() !bool {
// Try to inspect Mycelium - if it succeeds, it's running
mycelium.inspect(key_file_path: self.mycelium_config.key_path) or { return false }
return true
}
// Install Mycelium binary
fn (mut self HeroPods) mycelium_install() ! {
self.logger.log(
cat: 'mycelium'
log: 'Installing Mycelium ${self.mycelium_config.version}...'
logtype: .stdout
) or {}
// Use the mycelium_installer to install
mut installer := mycelium_installer.get(create: true)!
installer.peers = self.mycelium_config.peers
// Install Mycelium using the instance method
installer.install(reset: false)!
self.logger.log(
cat: 'mycelium'
log: 'Mycelium installed successfully'
logtype: .stdout
) or {}
}
// Start Mycelium service
fn (mut self HeroPods) mycelium_start_service() ! {
self.logger.log(
cat: 'mycelium'
log: 'Starting Mycelium service...'
logtype: .stdout
) or {}
// Use the mycelium_installer to start the service
mut installer := mycelium_installer.get()!
installer.start()!
// Wait for Mycelium to be ready
for i in 0 .. 50 {
if self.mycelium_check_running()! {
self.logger.log(
cat: 'mycelium'
log: 'Mycelium service started successfully'
logtype: .stdout
) or {}
return
}
if i % 10 == 0 {
self.logger.log(
cat: 'mycelium'
log: 'Waiting for Mycelium service to start... (${i}/50)'
logtype: .stdout
) or {}
}
time.sleep(100 * time.millisecond)
}
return error('Mycelium service failed to start after 5 seconds')
}
// Get the host's Mycelium IPv6 address
fn (mut self HeroPods) mycelium_get_host_address() ! {
self.logger.log(
cat: 'mycelium'
log: 'Retrieving host Mycelium IPv6 address...'
logtype: .stdout
) or {}
// Use mycelium inspect to get the address
inspect_result := mycelium.inspect(key_file_path: self.mycelium_config.key_path)!
if inspect_result.address == '' {
return error('Failed to get Mycelium IPv6 address from inspect')
}
self.mycelium_config.mycelium_ip6 = inspect_result.address
self.logger.log(
cat: 'mycelium'
log: 'Host Mycelium IPv6 address: ${self.mycelium_config.mycelium_ip6}'
logtype: .stdout
) or {}
}
// Setup Mycelium IPv6 networking for a container
//
// This method:
// 1. Creates a veth pair for Mycelium connectivity
// 2. Moves one end into the container's network namespace
// 3. Assigns a Mycelium IPv6 address to the container
// 4. Configures IPv6 forwarding and routing
//
// Thread Safety:
// This is called from container.start() which is already serialized per container.
// Multiple containers can be started concurrently, each with their own veth pair.
fn (mut self HeroPods) mycelium_setup_container(container_name string, container_pid int) ! {
if !self.mycelium_config.enabled {
return
}
self.logger.log(
cat: 'mycelium'
log: 'Setting up Mycelium IPv6 for container ${container_name} (PID: ${container_pid})'
logtype: .stdout
) or {}
// Create unique veth pair names using hash (same pattern as IPv4 networking)
short_hash := sha256.hexhash(container_name)[..6]
veth_container := 'vmy-${short_hash}'
veth_host := 'vmyh-${short_hash}'
// Delete veth pair if it already exists (cleanup from previous run)
osal.exec(cmd: 'ip link delete ${veth_container} 2>/dev/null', stdout: false) or {}
osal.exec(cmd: 'ip link delete ${veth_host} 2>/dev/null', stdout: false) or {}
// Create veth pair
self.logger.log(
cat: 'mycelium'
log: 'Creating veth pair: ${veth_container} <-> ${veth_host}'
logtype: .stdout
) or {}
osal.exec(
cmd: 'ip link add ${veth_container} type veth peer name ${veth_host}'
stdout: false
)!
// Bring up host end
osal.exec(
cmd: 'ip link set ${veth_host} up'
stdout: false
)!
// Move container end into container's network namespace
self.logger.log(
cat: 'mycelium'
log: 'Moving ${veth_container} into container namespace'
logtype: .stdout
) or {}
osal.exec(
cmd: 'ip link set ${veth_container} netns ${container_pid}'
stdout: false
)!
// Configure container end inside the namespace
// Bring up the interface
osal.exec(
cmd: 'nsenter -t ${container_pid} -n ip link set ${veth_container} up'
stdout: false
)!
// Get the Mycelium IPv6 prefix from the host
// Extract the prefix from the full address (e.g., "400:1234:5678::/64" from "400:1234:5678::1")
mycelium_prefix := self.mycelium_get_ipv6_prefix()!
// Assign IPv6 address to container (use ::1 in the subnet)
container_ip6 := '${mycelium_prefix}::1/64'
self.logger.log(
cat: 'mycelium'
log: 'Assigning IPv6 address ${container_ip6} to container'
logtype: .stdout
) or {}
osal.exec(
cmd: 'nsenter -t ${container_pid} -n ip addr add ${container_ip6} dev ${veth_container}'
stdout: false
)!
// Enable IPv6 forwarding on the host
self.logger.log(
cat: 'mycelium'
log: 'Enabling IPv6 forwarding'
logtype: .stdout
) or {}
osal.exec(
cmd: 'sysctl -w net.ipv6.conf.all.forwarding=1'
stdout: false
) or {
self.logger.log(
cat: 'mycelium'
log: 'Warning: Failed to enable IPv6 forwarding: ${err}'
logtype: .error
) or {}
}
// Get the link-local address of the host end of the veth pair
veth_host_ll := self.mycelium_get_link_local_address(veth_host)!
// Add route in container for Mycelium traffic (400::/7 via link-local)
self.logger.log(
cat: 'mycelium'
log: 'Adding route for ${self.mycelium_config.ipv6_range} via ${veth_host_ll}'
logtype: .stdout
) or {}
osal.exec(
cmd: 'nsenter -t ${container_pid} -n ip route add ${self.mycelium_config.ipv6_range} via ${veth_host_ll} dev ${veth_container}'
stdout: false
)!
// Add route on host for container's IPv6 address
self.logger.log(
cat: 'mycelium'
log: 'Adding host route for ${mycelium_prefix}::1/128'
logtype: .stdout
) or {}
osal.exec(
cmd: 'ip route add ${mycelium_prefix}::1/128 dev ${veth_host}'
stdout: false
)!
self.logger.log(
cat: 'mycelium'
log: 'Mycelium IPv6 setup complete for container ${container_name}'
logtype: .stdout
) or {}
}
// Get the IPv6 prefix from the host's Mycelium address
//
// Extracts the /64 prefix from the full IPv6 address
// Example: "400:1234:5678::1" -> "400:1234:5678:"
fn (mut self HeroPods) mycelium_get_ipv6_prefix() !string {
if self.mycelium_config.mycelium_ip6 == '' {
return error('Mycelium IPv6 address not set')
}
// Split the address by ':' and take the first 3 parts for /64 prefix
parts := self.mycelium_config.mycelium_ip6.split(':')
if parts.len < 3 {
return error('Invalid Mycelium IPv6 address format: ${self.mycelium_config.mycelium_ip6}')
}
// Reconstruct the prefix (first 3 parts)
prefix := '${parts[0]}:${parts[1]}:${parts[2]}'
return prefix
}
// Get the link-local IPv6 address of an interface
//
// Link-local addresses are used for routing within the same network segment
// They start with fe80::
fn (mut self HeroPods) mycelium_get_link_local_address(interface_name string) !string {
self.logger.log(
cat: 'mycelium'
log: 'Getting link-local address for interface ${interface_name}'
logtype: .stdout
) or {}
// Get IPv6 addresses for the interface
cmd := "ip -6 addr show dev ${interface_name} | grep 'inet6 fe80' | awk '{print \$2}' | cut -d'/' -f1"
result := osal.exec(
cmd: cmd
stdout: false
)!
link_local := result.output.trim_space()
if link_local == '' {
return error('Failed to get link-local address for interface ${interface_name}')
}
self.logger.log(
cat: 'mycelium'
log: 'Link-local address for ${interface_name}: ${link_local}'
logtype: .stdout
) or {}
return link_local
}
// Cleanup Mycelium networking for a container
//
// This method:
// 1. Removes the veth pair
// 2. Removes routes
//
// Thread Safety:
// This is called from container.stop() and container.delete() which are serialized per container.
fn (mut self HeroPods) mycelium_cleanup_container(container_name string) ! {
if !self.mycelium_config.enabled {
return
}
self.logger.log(
cat: 'mycelium'
log: 'Cleaning up Mycelium IPv6 for container ${container_name}'
logtype: .stdout
) or {}
// Remove veth interfaces (they should be auto-removed when container stops, but cleanup anyway)
short_hash := sha256.hexhash(container_name)[..6]
veth_host := 'vmyh-${short_hash}'
osal.exec(
cmd: 'ip link delete ${veth_host} 2>/dev/null'
stdout: false
) or {}
// Remove host route (if it exists)
mycelium_prefix := self.mycelium_get_ipv6_prefix() or {
self.logger.log(
cat: 'mycelium'
log: 'Warning: Could not get Mycelium prefix for cleanup: ${err}'
logtype: .error
) or {}
return
}
osal.exec(
cmd: 'ip route del ${mycelium_prefix}::1/128 2>/dev/null'
stdout: false
) or {}
self.logger.log(
cat: 'mycelium'
log: 'Mycelium IPv6 cleanup complete for container ${container_name}'
logtype: .stdout
) or {}
}
// Inspect Mycelium status and return information
//
// Returns the public key and IPv6 address of the Mycelium node
pub fn (mut self HeroPods) mycelium_inspect() !mycelium.MyceliumInspectResult {
if !self.mycelium_config.enabled {
return error('Mycelium is not enabled')
}
return mycelium.inspect(key_file_path: self.mycelium_config.key_path)!
}

View File

@@ -79,44 +79,89 @@ container.start()!
```heroscript
!!heropods.configure
name:'demo'
name:'my_heropods'
reset:false
use_podman:true
!!heropods.container_new
name:'demo_container'
name:'my_container'
image:'custom'
custom_image_name:'alpine_3_20'
docker_url:'docker.io/library/alpine:3.20'
!!heropods.container_start
name:'demo_container'
name:'my_container'
!!heropods.container_exec
name:'demo_container'
name:'my_container'
cmd:'echo "Hello from HeroPods!"'
stdout:true
!!heropods.container_stop
name:'demo_container'
name:'my_container'
!!heropods.container_delete
name:'demo_container'
name:'my_container'
```
### Mycelium IPv6 Overlay Network
HeroPods supports Mycelium for end-to-end encrypted IPv6 connectivity:
```heroscript
!!heropods.configure
name:'mycelium_demo'
reset:false
use_podman:true
!!heropods.enable_mycelium
heropods:'mycelium_demo'
version:'v0.5.6'
ipv6_range:'400::/7'
key_path:'~/hero/cfg/priv_key.bin'
peers:'tcp://185.69.166.8:9651,quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651'
!!heropods.container_new
name:'ipv6_container'
image:'alpine_3_20'
!!heropods.container_start
name:'ipv6_container'
// Container now has both IPv4 and IPv6 (Mycelium) connectivity
```
See [MYCELIUM.md](./MYCELIUM.md) for detailed Mycelium configuration.
## Features
- **Container Lifecycle**: create, start, stop, delete, exec
- **Bridge Networking**: Automatic IP allocation with NAT
- **IPv4 Bridge Networking**: Automatic IP allocation with NAT
- **IPv6 Mycelium Overlay**: End-to-end encrypted peer-to-peer networking
- **Image Management**: Pull Docker images via Podman or use built-in images
- **Resource Monitoring**: CPU and memory usage tracking
- **Thread-Safe**: Concurrent container operations supported
- **Configurable**: Custom network settings, DNS, resource limits
## More Examples
## Examples
See `examples/virt/heropods/` for more detailed examples:
See `examples/virt/heropods/` for complete working examples:
- `heropods.vsh` - Complete API demonstration
- `demo.heroscript` - HeroScript usage
- `runcommands.vsh` - Simple command execution
### HeroScript Examples
- **simple_container.heroscript** - Basic container lifecycle management
- **ipv4_connection.heroscript** - IPv4 networking and internet connectivity
- **container_mycelium.heroscript** - Mycelium IPv6 overlay networking
### V Language Examples
- **heropods.vsh** - Complete API demonstration
- **runcommands.vsh** - Simple command execution
Each example is fully documented and can be run independently. See [examples/virt/heropods/README.md](../../../examples/virt/heropods/README.md) for details.
## Documentation
- **[MYCELIUM.md](./MYCELIUM.md)** - Mycelium IPv6 overlay network integration guide
- **[PRODUCTION_READINESS_REVIEW.md](./PRODUCTION_READINESS_REVIEW.md)** - Production readiness assessment
- **[ACTIONABLE_RECOMMENDATIONS.md](./ACTIONABLE_RECOMMENDATIONS.md)** - Code quality recommendations