The private node from the command line
A small runtime of your own next to the marketplace, wired to your agents from a shell. The node
keeps your accounts, keys, memory and local tools on a machine you control, encrypted, and pays a
marketplace node for the public steps of a workflow from a budget-capped wallet; nothing personal
leaves it. aioum node … speaks to the node's API with the bearer token from its token file.
What you need
- A machine: your laptop, or a VPS with Docker.
- The repository, with
npm installdone inoffchain/, and the marketplace node the private node pays through (MARKET_NODE_URL,http://127.0.0.1:8546on the local development network). - A passphrase you keep. It is the key to everything the node stores; there is no recovery.
- For Gmail or Google Calendar: a Google OAuth client, its
GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRETin the node's environment, withhttp://127.0.0.1:8770/oauth/gmail/callback(and/oauth/gcal/callback) as its redirect URIs. For a key-based service: the key and the base URL of its API.
Steps
-
Start the node, in a shell of its own. It binds to loopback only.
cd offchain export PRIVATE_NODE_PASSPHRASE='a long passphrase you keep' export MARKET_NODE_URL=http://127.0.0.1:8546 npm run private-nodeThe first start writes a bearer token to
~/.aioum-private/token(mode0600) and prints that path, never the token; the CLI reads the file by itself. On a server,docker compose up -dinoffchain/src/private-nodedoes the same in a container, with the data in theaioum-privatevolume anddeploy/Caddyfilefor TLS in front; read the token withdocker compose exec private-node cat /data/token, set it asAIOUM_PRIVATE_NODE_TOKENand the address asAIOUM_PRIVATE_NODE_URL. -
Confirm it is up, back in
packages/cli.npx aioum node statusThe health line (
ok, kindprivate-node), then one row per connector with its label, its auth type, whether it is connected and its tool names:workspace(a folder on your machine your agents work in),hf(Hugging Face models),hardware,train,skillsandmemory(the local brain), connected from the start;gmail,gcal,apikey,wallet,exchange, the OAuth accounts andghost, connected below. A401here means the token the CLI found is not this node's. -
List the tools you can call now.
npx aioum node toolsEvery tool of every connected connector, with what it does (
--jsonadds each input schema):ws.list,ws.read,ws.write,ws.run,ws.fetch,ws.screenshot,ws.browseandws.ffmpegfrom the workspace,hf.install,hf.status,hf.list,hf.removeandhf.run,hardware.checkandhardware.optimize,train.lora,train.distill,train.status,train.listandtrain.export,skills.installandskills.list, andrememberandrecallfrommemory.npx aioum node call hardware.checkprints what this machine has (the same reportnpm run hardware:checkinoffchain/writes to.deploy/hardware.json) (Your node). -
Put a file in the workspace and read it back.
npx aioum node call ws.write --args '{"path":"hello.md","content":"hi"}' npx aioum node call ws.read --args '{"path":"hello.md"}'The first prints the file's workspace path, its URL on the node and its size in bytes; the second prints its content. A tool ran on your machine, a file exists on your disk (
~/.aioum-private/workspace, orPRIVATE_NODE_WORKSPACE); nothing was paid and nothing left the box. -
Connect Gmail or Google Calendar.
npx aioum node connect gmailIt prints
open this URL to authorise:and a URL onaccounts.google.com: open it in your browser and approve. The node asks only forgmail.send(forgcal,calendar.events) plus offline access, receives the tokens at its callback, stores them encrypted, and the tab reads "Connected gmail. You can close this tab."sendGmail(to,subject,body) now appears innode tools, and after the same step withgcal,createGcalEvent(title,time); call them withnode callas in step 4. The node refreshes an expired token by itself; if you revoke the grant at Google, the next call fails withgmail: re-connect requiredand you repeat this step. Without the two Google values in the node's environment the command fails withOAuth not configured. -
Connect a key-based service.
npx aioum node connect apikey --key sk-your-key --base-url https://api.example.com/v1/Here
--keyis the provider's key, not your wallet's. It printsok,connected apikeyand the base URL. The key is sent asAuthorization: Bearer <key>; add--header X-Api-Key --prefix ""when the API wants it another way. Its one tool iscall:npx aioum node call call --args '{"path":"weather?city=Oslo"}'resolves the path against the base URL, takes an optionalmethod(GET when unset) and JSONbody, and answers with the status line and the reply's first 4,000 characters. A path that resolves anywhere but the origin you connected is refused before any request is made. A wallet, an exchange account or an OAuth account (Google Ads, X Ads and the rest) is connected the same way with its own fields and the caps you set, from the Your node cards on the marketplace page orPOST /connect(Connect local services). -
Give the node a memory, then recall it.
npx aioum node memory add "I prefer window seats" --kind preference --tags travelIt prints the item with its id.
--kindisfact,preference,resultornote, and--tagsis comma-separated.npx aioum node memory listshows everything, newest first;npx aioum node call recall --args '{"query":"window seats","limit":"5"}'answers• [preference] I prefer window seats;npx aioum node memory delete <id>forgets one item, andnpx aioum node memory forget-allwipes the brain. -
Wire a workflow that feeds a marketplace step into a local one. Write
job.json. A step whosecategorystarts withpersonal:runs on your node, where thetoolis looked up among your connectors; any other category is a marketplace listing, matched against the catalogue and paid from the job's budget (serviceIdpins one node,maxPricePerCallcaps its price). An input is a literal or a reference to an earlier step's output.{ "name": "trending to memory", "intervalSeconds": 3600, "budgetUsd": 1, "steps": [ { "id": "s1", "category": "mcp:web-search", "tool": "search", "inputs": { "query": { "kind": "literal", "value": "most trending stock today" } } }, { "id": "s2", "category": "personal:memory", "tool": "remember", "inputs": { "text": { "kind": "ref", "fromStep": 0, "extract": "text" }, "kind": { "kind": "literal", "value": "fact" }, "tags": { "kind": "literal", "value": "stocks" } } } ] }npx aioum node schedule create --file job.jsonIt prints the job with its id, interval, budget, whether it is enabled and its next run. It runs every hour from now;
npx aioum node schedule toggle <id>pauses it and resumes it. -
Fire it once.
npx aioum node schedule run <id>The job comes back with its latest run first: whether it succeeded, what it spent, and one output per step. Step 1 was paid on the marketplace with a signed receipt; step 2 ran on your node for free and kept that answer:
npx aioum node memory listshows it as anotetaggeduntrusted:step0, although the step asked for afact. Marketplace output is tagged untrusted, and two sinks refuse it:rememberdowngrades it to a note, and a recipient field (sendGmail'sto, once Gmail is connected) stops the run withuntrusted recipient blocked. Only your ownmemory addcreates a fact or a preference. -
See every job.
npx aioum node schedulesOne row per job: id, name, interval, budget, enabled, last run and next run.
npx aioum node schedule delete <id>removes one. -
Back it up.
npx aioum node backup export ./aioum-backup.bin --passphrase 'twelve chars or more'The node bundles
secrets.enc,memory.jsonandjobs.json, never the token, encrypts the bundle under that passphrase (at least 12 characters, and separate from the node's own), and the CLI writes it to the file (mode0600) and answerswrote <file> (N bytes, encrypted with your passphrase). The passphrase goes into your shell history like any argument; a space before the command keeps it out in most shells. -
Restore, then restart the node.
npx aioum node backup restore ./aioum-backup.bin --passphrase 'twelve chars or more'It prints which files were restored and
restart the private node to load restored state. The node that restores must run under the samePRIVATE_NODE_PASSPHRASE(or a copy of.secretkey) as the one that exported: the secrets and memory inside stay encrypted under it. A wrong passphrase fails withrestore failed: wrong passphrase or corrupt backup. -
Remove a connection, or rotate one.
npx aioum node disconnect apikeyThe secret is deleted from
secrets.enc. To rotate a key, run step 5 or 6 again: the new secret replaces the old. To rotate the node's own token, delete~/.aioum-private/tokenand restart the node.
What you have now
A private node on a machine you control, behind a bearer token, with an encrypted backup you can restore, and your agents acting as you without anyone else holding you: a memory, a workspace, email, a calendar and a keyed account answer on your node, every secret encrypted on your disk, a scheduled workflow handing a paid answer to a local tool, and marketplace bytes never choosing who gets the email or what you believe.
Next: Agents from the command line — one agent from flags or a file, a run and its receipts, and a second agent that hires the first.