diode is the Go CLI for connecting a device or service to the Diode mesh network. It can:
.diode servicespush / pulljoinAll traffic is routed through the Diode network and secured with Diode's client identity and end-to-end encrypted sessions.

1.22.x or newer enough to build the module as checked inOptional:
diode join -wireguarddiode sshIf you want the packaged CLI instead of building from source, the published CLI docs point to:
curl -Ssf https://diode.io/install.sh | shgo mod download
make openssl
make
This builds the main binary as ./diode.
If you only want to run it during development:
go run ./cmd/diode --help
The client stores local state in a small file database. By default that database is created at:
~/.config/diode/private.db~/Library/Application Support/diode/private.dbC:\Users\<yourname>\AppData\Roaming\diode\private.dbOn first start the client automatically creates a secp256k1 private key and derives your Diode client address from it. You can inspect what is stored locally with:
./diode -update=false config -list
Important stored values:
private: your client private keyfleet: the fleet/perimeter address this client usesIf fleet has not been configured yet, the client uses the default development fleet until you either:
diode reset to create a new fleet contract for this client, ordiode config -set fleet=0x..../diode -update=false config -list
If your app listens on localhost:8080 and you want it exposed on public Diode port 80:
./diode publish -public 8080:80
If the publish succeeds, the client logs the public gateway URL:
http://<name-or-address>.diode.link/ for public port 80https://<name-or-address>.diode.link:<port>/ for some public high ports such as 8000-8100./diode socksd
This starts a SOCKS5 proxy on 127.0.0.1:1080. Point your browser or app at that proxy to reach .diode names directly.
./diode fetch -url http://mydevice.diode
./diode fetch -url https://mydevice.diode.link:8080/file.txt -output file.txt
Top-level commands:
publish: publish local services to the networksocksd: run a local SOCKS5 proxyfetch: issue HTTP requests to Diode endpointsfiles: run a published HTTP file listenerpush / pull: upload or download files from a Diode file listenerssh: connect to a Diode target through an auto-managed local SOCKS proxyscp: copy files to/from a Diode target through an auto-managed local SOCKS proxyjoin: apply on-chain perimeter properties and optional WireGuard configbns: register, transfer, and resolve BNS namesquery: resolve a Diode address or name and print device ticketstoken: check balance or transfer DIODEconfig: inspect or change local stored valuesmcp: run the client as an MCP server over stdin/stdoutgateway: run a public HTTP/HTTPS gatewaytime: query consensus timeversion: print build versionOne implementation detail that is easy to miss: if you run diode with no subcommand, the CLI falls back to publish. Use explicit subcommands in scripts and docs.
Another important parser rule: global options must come before the subcommand.
./diode -debug=true publish -public 80:80
Not:
./diode publish -debug=true -public 80:80
If you plan to contribute code, see CONTRIBUTING.md.
publish is the core command for exposing a local service.
./diode publish -public 8080:80
./diode publish -protected 3000:3000
./diode publish -private 22:22,0xabc...,myfriend
Port mapping forms:
<local_port>: publish the same local and remote port<local_port>:<remote_port>: publish local to a different external port<host>:<local_port>:<remote_port>: bind to a specific local host:tcp, :tls, or :udp to force a protocolExamples:
./diode publish -public 8080
./diode publish -public 127.0.0.1:8080:80:tcp
./diode publish -public 80:80,2368:8025
./diode publish -private 22:22:tcp,0x1111111111111111111111111111111111111111
./diode publish -protected 3000:3000
Publish modes:
-public: anybody can connect-private: you must provide at least one allowlisted address or BNS name-protected: intended for fleet/perimeter-controlled accessUseful flags:
-socksd: start a SOCKS proxy alongside the publisher-proxy_host / -proxy_port: configure that proxy-sshd: publish the embedded Diode SSH service-files: add one or more file listeners to the same long-running publisher-fileroot: root path for all -files listeners on that commandThe simplest static-site workflow from the public docs is:
mkdir project
cd project
echo "Hello World" > index.html
../diode publish -http
That starts the built-in static server and publishes it through Diode.
If you run publish without any ports, files listeners, or binds, the client exits with an error.
The client includes a simple HTTP file listener and matching upload/download commands.
Start a public file listener on Diode port 8080:
./diode files 8080
Serve files relative to a specific directory:
./diode files -fileroot /var/inbox 8080
Private file listener:
./diode files -fileroot /var/inbox 8080,mydevice,0x0000000000000000000000000000000000000001
Upload a file:
./diode push ./photo.jpg mydevice.diode.link:8080
./diode push ./photo.jpg mydevice.diode.link:8080:photos/vacation.jpg
Download a file:
./diode pull mydevice.diode.link:8080:photos/vacation.jpg
./diode pull mydevice.diode.link:8080:photos/vacation.jpg ./vacation.jpg
Important behavior:
-fileroot is omitted, the listener serves relative to the process working directory at startupdiode files flags must come before the positional <files-spec> because Go's flag parser stops at the first non-flag argumentpush with only <peer>:<port> writes to the basename of the local file on the remote sidepull without a local destination writes to ./<basename(remote-path)>The full contract for file serving and transfer is documented in docs/file-transfer-spec.md.
Start a local SOCKS proxy:
./diode socksd
Default listener:
127.0.0.11080Custom port:
./diode socksd -socksd_host 127.0.0.1 -socksd_port 8082
The repo includes a proxy.pac file you can use for browser proxy configuration. Typical usage is to send .diode, .diode.link, and .diode.ws traffic through the SOCKS proxy.
Other common SOCKS-based workflows from the CLI docs:
curl -x socks5h://localhost:1080 mydevice.diode
ssh -o "ProxyCommand=nc -X 5 -x localhost:1080 %h %p" user@mydevice.diode
nc -X 5 -x localhost:1080 mydevice.diode.link 8080
That last pattern is useful for tools like VLC that do not fully support SOCKS hostname resolution on their own.
You can also use a bind rule to open a local port that forwards to a remote Diode service:
./diode -bind auto:mydevice.diode:80:tls socksd
auto lets the OS choose a local port. The client prints the resolved local port after startup.
This same -bind feature is what the public docs use for remote SMB / drive mapping style workflows. Example:
./diode -bind 1039:0xREMOTEDEVICEADDRESS:445 socksd
After that, a local app can connect to localhost:1039 as if it were the remote service.
diode ssh is a convenience wrapper around OpenSSH. It:
ssh binary with the right proxy commandExample:
./diode ssh ubuntu@mymachine.diode -p 22
Notes:
-p 22, not ubuntu@mymachine.diode:22ssh and ssh-keygen must be installed locallydiode scp is the file-copy counterpart to diode ssh. It wraps the local
scp binary using the same auto-managed Diode SOCKS proxy, ephemeral SSH
identity, and ProxyCommand wiring, so remote paths that target a .diode
host (or raw Diode address) get tunnelled through the Diode network.
Examples:
./diode scp ./photo.jpg ubuntu@mymachine.diode:/tmp/photo.jpg
./diode scp -P 22 ubuntu@mymachine.diode:/etc/hostname ./hostname
./diode scp -r ./dir ubuntu@mymachine.diode:/tmp/dir
Notes:
diode ssh; flags may still changediode ssh, do not put a port in the hostname; use -P PORT
(uppercase, as scp expects it)scp, ssh, ssh-keygen) must be installed locallyscp are passed through to the system scp, so standard
flags such as -r, -p, -C, -o, -P, etc. all workIf you want to expose a normal local SSH daemon over Diode:
./diode publish -public 22:22
./diode publish -private 22:22,0x1111111111111111111111111111111111111111
./diode publish -protected 22:22
If you want to publish the embedded Diode SSH service instead:
./diode publish -sshd protected:2222:ubuntu
./diode publish -sshd private:2222:ubuntu,0x1111111111111111111111111111111111111111
fetch is a Diode-aware HTTP client. It only accepts Diode URLs, not arbitrary web2 URLs.
Examples:
./diode fetch -url http://myservice.diode
./diode fetch -url https://myservice.diode.link:8080/api -method POST -data '{"ok":true}' -header 'content-type: application/json'
./diode fetch -url diode://myservice.diode/path
Supported methods:
GETPOSTPUTDELETEOPTIONPATCH is currently not enabled.
BNS is the blockchain name service used by Diode names.
Lookup:
./diode bns -lookup mydevice
./diode bns -account mydevice
Register:
./diode bns -register mydevice=0x1234...
If you register BNS names with the CLI, the ownership workflow is tied to this client's local wallet. Back up the local private.db if you rely on CLI-managed BNS names. If you want one wallet to manage names independently of any single CLI install, the public docs recommend using the Diode Network Explorer with MetaMask instead.
Transfer:
./diode bns -transfer mydevice=0xabcd...
Unregister:
./diode bns -unregister mydevice
BNS names are lowercase and must be between 7 and 32 characters using a-z, 0-9, and -.
Resolve an address or name to device tickets:
./diode query -address 0x1234...
./diode query -address mydevice
Check your balance:
./diode token -balance
Send DIODE:
./diode token -to 0x1234... -value 1millidiode -gasprice 10gwei
./diode token -to mydevice -value 1diode -gasprice 10gwei
join is the command for contract-driven perimeter management. It watches an on-chain property set and applies the resulting local behavior.
Typical use:
./diode join 0xB7A5bd0345EF1Cc5E66bf61BdeC17D2461fBd968
Dry-run a single sync:
./diode join -dry 0xB7A5bd0345EF1Cc5E66bf61BdeC17D2461fBd968
Choose network:
./diode join -network testnet 0xB7A5bd0345EF1Cc5E66bf61BdeC17D2461fBd968
Generate and print a WireGuard public key without fully joining:
./diode join -wireguard
./diode join -wireguard -suffix staging
What join can consume from the perimeter/device properties includes:
public, private, and protected port definitionssshdwireguardsocksdbinddebugdiodeaddrsfleetextra_configWireGuard notes:
wireguard property must not contain PrivateKeyThe current README keeps the essentials here. The implementation details live in the command and are document
$ claude mcp add diode_client \
-- python -m otcore.mcp_server <graph>