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.
Choose the connection you have
| Task | Sender and transport | Destination |
|---|---|---|
Run !hello mesh | MeshCore app with a companion radio; ordinary encrypted DM | Bot identity |
| 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; 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. Management advertises a repeater-type CLI endpoint for administration; forwarding remains with the repeater role.
- Match the complete PHY: frequency, bandwidth, spreading factor and coding rate. Keep production radios on their existing profile and use a separate test radio for another PHY.
- Obtain authorization: use the existing Management password or an already authorized full sender public key. Public channels and radio proximity carry no administrative authority.
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. App menu names vary; if yours lacks 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. Raw KISS uses a separate endpoint. 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 sends without requesting 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; check the session and route before retrying.
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.
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.
Use bot !admin for supported owner controls from a trusted full-key identity. Use Management to change credentials, upload programs or transfer stored data. Bot access and runtime reference · Native RF framing and access.
Install one Lua command from a file over LoRa
Use 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 to the target Aspen.
Need a gateway? The WiFi modem setup prepares a separate radio with the KISS endpoint this route needs.
Use an 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 can use a dedicated identity or an existing operator-managed identity. Keep app and radio private keys in their original stores. Omit the password file 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. The example creates the file exclusively with mode 0600, prints the public key and leaves node authorization unchanged:
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. Reuse this seed for later uploads and retain its 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. Give --gateway the raw KISS TCP endpoint, usually port 8001. 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/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/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 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/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/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 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 and preserves 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. Send chat commands to the bot's contact. Use a separate companion radio when checking replies over RF.
What installation keeps and replaces
A named installation updates one file while keeping other installed files and bundled commands. The complete encoded Lua set shares the 4096-byte limit and eight-command registry; wrapper bytes also count. Whole-set source replace and package install replace that runtime's entire command set. Choose the LAN or RF upload route above to match your connection.
Installed programs return after restart. Installation keeps role identities, settings, scoped KV and notes; it cancels that runtime's pending work. Code rollback keeps data changes and cannot recall 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
This is a whole-set wire-format illustration. 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 for named-file installation that preserves 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 the node's current generation, slots and progress; the hash reply ends with gen=N.
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 for the same administrative attempt. --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. Named source installation accepts Lua source files; ordinary bot DMs, compressed files, bytecode and native multipart messages are invalid inputs here. A durable chunk ACK and final activation status confirm upload progress and installation.
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 that selection. Management remains available with optional roles disabled. 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; identity-bound bot data stays with the previous key. Repeater, Room and Management each use their own permissions. A compiled authority remains after removing an independent saved ACL entry.
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. Verify whether your client expects native companion TCP or raw 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 any retry. - 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 and help while preserving stored data.
A matching active selection confirms installation despite the lost commit reply.
Management remains available when the bot is disabled or custom source is quarantined. Use it to repair the selected program without formatting flash or replacing identities.
More command tutorials · Node administration overview · Source RF walkthrough.