Manage a node. Upload a command over LoRa.
Your companion radio can open Aspen's Management console without WiFi on the node. A computer with a separate KISS gateway can add a Lua file over the same mesh, without reflashing or replacing your other command files.
Two identities, two conversations. Send ordinary ! commands to the bot. Log in to the separate Management contact for settings and file transfers. A bot DM, channel nickname or BLE pairing does not grant Management access.
This guide covers the current Aspen Management/source client. Check your node's help and capabilities first: the published firmware and current source are different snapshots. The conversations are labelled HTML examples, not screenshots or private message history.
Choose the connection you have
| Task | Sender and transport | Destination |
|---|---|---|
Run !hello mesh | MeshCore app with a companion radio; ordinary encrypted DM | Bot identity, no admin login |
| Read/change settings | Native companion radio; app repeater/admin console or meshcli | Aspen Management identity |
| Upload a file from a computer | Python client owning a sender key; independent raw-KISS radio gateway | Aspen Management identity |
| Manage Pine | Authenticated Pine-Relay/repeater console | Pine-Relay, not Pine-Bot; use the Pine guide for its commands and limits |
| Manage a host locally | Private owner socket | Birch host or Willow host endpoint |
- Use the Management public key from your existing setup/contact, not the bot key. Management advertises a repeater-type CLI endpoint but does not forward packets.
- Match the complete PHY: frequency, bandwidth, spreading factor and coding rate. Keep existing identities/settings; do not retune production radios just to try the guide.
- Obtain authorization: the existing Management password, or an already authorized full sender public key. Public channels and radio proximity do not authorize administration.
Open the Management console over RF
In an app that exposes the native repeater administrator console, select the Management contact, log in and send status, then bot status. Use the admin console, not ordinary bot chat. App menu names vary; if yours does not expose this console, use meshcli with a companion radio:
meshcli -s /dev/serial/by-id/YOUR_COMPANION
Or connect to a native companion TCP endpoint with meshcli -t COMPANION_HOST -p COMPANION_PORT. That port is not KISS. In the interactive console, replace the key and password placeholders locally:
add_contact MANAGEMENT_PUBLIC_KEY64 2 mast
reset_path mast
login mast ADMIN_PASSWORD
cmd mast status
wmt8
cmd mast "bot status"
wmt8
cmd mast "source status"
wmt8
For an already authorized full companion key, use login mast "". cmd does not request a delivery ACK. Read the native encrypted CLI reply with wmt8, or sync_msgs for replies already queued. Keep console history containing a password private.
The status fields depend on the node. A missing reply leaves the outcome uncertain; it is not a confirmed failure.
Native login, telemetry and ordinary bot owner controls
Login uses native encrypted ANON_REQ with timestamp/password, or an authorized full key and empty password. Subsequent commands and replies use native encrypted CLI TXT_MSG. This uses existing MeshCore framing, not a new public bot/upload protocol.
After login, req_status mast reads device counters and req_telemetry mast reads permitted Cayenne LPP sensor measurements. Unsupported board values are omitted from telemetry. telemetry status instead describes the optional metrics publisher.
Trusted full-key bot !admin supports bounded owner controls, not credentials or bulk source/data transfers. Use Management for those operations. Bot authority and runtime reference · Native RF framing and access.
Install one Lua command from a file over LoRa
This file route uses a different sender setup from the console above. You need a separate MeshCore KISS gateway radio reachable over TCP, the source checkout and a Python environment with the admin client dependencies. The gateway transmits the client's encrypted packets; it is not the target Aspen.
Need a gateway? Follow the WiFi modem setup on a different radio. You do not need to start a Go host just to use that modem's KISS port.
Use an existing operator-managed 32-byte Ed25519 sender seed file and a password file, both private regular files with mode 0600. The password file contains only the password, with no final newline. The client owns this sender identity; it need not be your mobile app's identity. Do not extract private keys from an app or radio. Omit the password file only when this client's full public key is already authorized.
Create a new standalone client identity, offline
Create the key once in an existing private directory outside the source checkout. Replace the path below. This refuses to overwrite an existing file, prints only a public key and does not change or authorize anything on the node:
python3 - /absolute/private/rf-client.seed <<'PY'
import os, secrets, sys
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
seed = secrets.token_bytes(32)
fd = os.open(sys.argv[1], os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, "wb") as output:
output.write(seed)
output.flush()
os.fsync(output.fileno())
public = ed25519.Ed25519PrivateKey.from_private_bytes(seed).public_key()
print("Client public key:", public.public_bytes(
serialization.Encoding.Raw, serialization.PublicFormat.Raw).hex())
PY
Use the existing Management password, or ask its authorized operator to grant this exact public key. Do not generate another seed for each upload. Retain the seed and the adjacent .clock timestamp file; the directory must be writable. Use one active client per seed so concurrent clients do not compete for replay state. Keep both files private and out of commits.
Keep the gateway network private. KISS and native companion TCP are unencrypted network services; use a trusted LAN or protected tunnel. --gateway does not connect to app BLE, native companion TCP or Aspen's web admin page. RF login and CLI packets sent by this client are encrypted.
Read capabilities and save the current set
Run from the source repository root. Substitute your own gateway, full Management key and protected file paths in every invocation:
python3 tools/hardware/admin.py \ --gateway KISS_GATEWAY_HOST --port 8001 --target MANAGEMENT_PUBLIC_KEY64 \ --seed-file /absolute/private/rf-client.seed \ --password-file /absolute/private/node-password \ command 'source api sources' python3 tools/hardware/admin.py \ --gateway KISS_GATEWAY_HOST --port 8001 --target MANAGEMENT_PUBLIC_KEY64 \ --seed-file /absolute/private/rf-client.seed \ --password-file /absolute/private/node-password \ download saved-source.lua chmod 600 saved-source.luaExpected: the capability reply includes
format=meshcore-sources/1andcommit-base=sha256. The download verifies full readback and writes a new file;chmodmakes that backup private. Keep it out of public commits. With the same options,source-listlists names andsource-export NAME NEW_FILEexports one installed file.Save and test a greeting
Download hello.lua or save the following as UTF-8 with LF newlines and a final newline:
function hello(name) return "Hello " .. name end command("hello", "name:string:32", "Greet a name", "hello", "public", "!hello mesh")Test the greeting locally before using airtime. Choose a source name not used by an unrelated file. Duplicate command names reject the candidate.
Add the named file and wait for activation
python3 tools/hardware/admin.py \ --gateway KISS_GATEWAY_HOST --port 8001 --target MANAGEMENT_PUBLIC_KEY64 \ --seed-file /absolute/private/rf-client.seed \ --password-file /absolute/private/node-password \ source-install greeting hello.lua python3 tools/hardware/admin.py \ --gateway KISS_GATEWAY_HOST --port 8001 --target MANAGEMENT_PUBLIC_KEY64 \ --seed-file /absolute/private/rf-client.seed \ --password-file /absolute/private/node-password statusExpected:
Durable chunk N/TOTAL, acceptance of verification, thensource durably saved and active. The client verifies the selected hash. The file namedgreetingchanges; other files remain. A concurrent source update rejects the conditional commit rather than overwriting that update.A disabled bot reports
source saved; bot disabled; loads on next enabled boot. Inspect its channel policy before enabling/restarting. If activation is still pending when the client times out, inspectsource statusandsource hashinstead of repeating the install.Switch to the bot conversation
From a separate companion on the same PHY, advertise your companion so the bot learns its contact, then open the bot's ordinary private DM:
Example · Companion → Bot DM · greeting active Generated help describes the argument and example. This uses the bot's key, not Management's. A reply reflected through the node's own Base is not reception by another radio.
Preserved state, whole-set replacement and limits
Named source installation reads the existing set, composes a candidate and commits against its original SHA-256. The complete encoded Lua set shares the 4096-byte limit and eight-command registry; wrapper bytes also count. Whole-set install and package-install replace that runtime's entire command set. A browser LAN upload is a web operation, not an automatic LoRa upload.
Durable source returns after restart. Installation retains role identities, settings, scoped KV and notes; it cancels that runtime's pending work. Reverting code does not undo data changes or packets already sent. Do not format, provision or erase a node to add a command.
Understand the transfer and its replies
The client logs in to Management, admits an upload, sends numbered 48-byte chunks and starts verification. Each durable chunk reply confirms readback and saved progress. Commit acceptance is only the start: read status until activation finishes, then check the hash.
Example Management conversation: all three chunks, activation and hash
Wire-format illustration, not the named-file installer. These are the exact bytes of the downloadable 144-byte greeting alone. Sending this raw commit replaces the whole Lua set. Use source-install greeting hello.lua to preserve other files: its composed bundle has a different size and hash.
Login first as above. In meshcli, each outgoing source line is the argument to cmd mast "…"; read each reply with wmt8. For actual file installation, let the Python client manage these exchanges.
Status also includes generation, slots and progress; the hash reply ends with gen=N. Those values depend on the node. Excerpts omit them rather than inventing fixture values.
Resume, retries and airtime
Matching source begin ID16 SIZE SHA256 resumes saved progress after a disconnect/restart. Sequential chunks are 48 source bytes except the final one. Repeating an identical chunk is safe; a gap or conflicting duplicate fails. The upload loop makes at most three identical chunk attempts on timeout, then polls activation up to 20 times with 0.5-second pauses plus RF round-trip time.
The RF transport can also retransmit a command twice with the same timestamp/text so Management returns its cached outcome. This is not a new administrative attempt after an unknown result. --no-command-retry disables those transport retransmissions; the bounded identical-chunk upload retries still apply.
A 4096-byte source needs 86 chunks and their replies. Do not paste whole Lua into an ordinary bot DM, compress it, send bytecode or assume native multipart messages are a file protocol. A native packet ACK is neither a durable chunk ACK nor installation completion.
Admin commands you can use over LoRa
After Management login, begin with read-only checks. In meshcli, use cmd mast "COMMAND" and wmt8. In the Python client, use the RF connection options above followed by command 'COMMAND'. Consult help and help TOPIC PAGE on your actual build before changing anything.
| Task | Read first | Change / consequence |
|---|---|---|
| Health and diagnostics | status, ver, board, stats help, job | Read-only; job reports deferred-control outcome |
| Roles and names | roles, roles list 2, bot status, role help, bot key | role name ROLE TEXT persists without replacing a key; roles MASK and bot on/off save boot selection; apply restarts |
| Shared PHY | get radio, get freq, get tx | radio HZ BW_HZ SF CR DBM changes all roles after the old-PHY reply; tempradio SECONDS HZ BW_HZ SF CR DBM later restores the durable profile |
| Adverts | get autoadvert, role config bot | set autoadvert off/on; role advert ROLE zerohop/flood deliberately queues a transmission, not confirmed reception |
| Sensors and native status | Companion req_status mast, req_telemetry mast | Board measurements and permissions determine available telemetry; no publisher setup is needed to request native telemetry |
| Bot work | bot stats, bot admission, bot log, bot limits, bot diagnostics | bot cancel cancels bot work; already admitted effects may have committed |
| Channel policy | bot aliases, bot membership SLOT, bot access CONTEXT list OFFSET, bot thread CONTEXT list OFFSET | Joining resets defaults to 63 and clears overrides. Disable/restart before creating addressed-only membership; set default 12 before enabling |
| Source lifecycle | source api sources, source status, source hash, source metadata | Named Python install retains other files; source retry, source rollback, source remove, source cancel have different recovery effects |
| Management permissions | get acl, password, key management pending | setperm FULL_KEY64 3 grants admin; 0 removes the saved entry. trust FULL_KEY/trust none controls additional singleton trust; native role ACLs are separate |
| WiFi | wifi status, get wifi.enabled, get wifi.ip | Save credentials over encrypted RF only; wifi apply reconnects; disabling WiFi keeps RF Management running |
| Native services | bot https status, telemetry endpoint status, mqtt status, cloudroom status | Build-dependent; use service guides for destinations, grants and protected credential uploads |
| Restart | status, job | apply or reboot restarts the device; read running state before retrying an uncertain restart |
Command catalog and service guides · Exact syntax, units and limits · Safe addressed-only channel walkthrough.
Permissions, secrets and operations that can disconnect you
Role bits are repeater=1, room=2, companion=4, observer=8; bot selection is separate. roles MASK saves a mask, not a help page. Management remains available without optional roles. Shared PHY changes affect every role and host using the modem; keep a matching recovery route and follow local spectrum rules.
Use protected-file operations for passwords, private identities, WiFi credentials, service tokens and scoped backups. Do not place secrets in public posts, command-line examples or diagnostic captures. Password, identity and WiFi secret setters require encrypted Management RF; LAN HTTP exposes passwords/session traffic and is for trusted networks only.
Key rotation deliberately changes identity and requires restart; it does not move identity-bound bot data. Repeater/Room permissions are not Management permissions. Removing a saved ACL entry does not remove independent compiled authority.
ACL and session behavior · Password changes · Identity imports.
Recover without erasing the node
- Management does not answer
- Check the destination key, native admin login, route and complete PHY. Read queued replies with
sync_msgs. Do not confuse native companion TCP with KISS. - A chunk reply is lost
- A matching
source beginreports saved progress; only repeat the identical numbered chunk. The current source remains selected during upload. - Commit, install or restart is uncertain
- Read
source status,source hash,statusand the affected saved/live settings before retrying. Do not blindly replay a mutation. - A candidate is rejected
- Inspect the error; the old source remains selected. Fix the size, hash, command collision or initialization failure locally before a new explicit attempt.
- The durable selection cannot run
- Inspect bot readiness and source status, then choose
source retry,source rollbackorsource remove. Removal restores bundled handlers and removes the installed set; rollback restores code/help, not data.
If the selection matches and is active, the lost commit reply does not require reinstalling.
Native Management remains available when the bot is disabled or custom source is quarantined. Source repair is not provisioning: do not format, erase flash or replace identities to install a command.
More command tutorials · Node administration overview · Source RF walkthrough.