Info // 03

Tools and sysadmin

The model calls tools for itself instead of relying on keyword triggers, so it decides when a question needs looking up and can chain steps: check the time, then schedule something. Every call is echoed into the conversation, so it's never a mystery why a reminder appeared.

What she can call

GroupTools
Time and mathget_datetime, time_until, calculate: exact sums and unit conversions, parsed rather than run
Remindersset_reminder, set_alarm, list_reminders, cancel_reminder
Memoryremember_fact, recall_facts, update_fact, forget_fact, search_history, search_docs
Fileslist_files, read_file, write_file, edit_file: writes approved on screen
Shellrun_command: every command approved, no allow-all
Desktoplook_at_screen, look_at_camera, control_audio, clipboard, focus_window
Systemsystem_status, system_health, last_boot, find_device, scan_network
Webweb_search, research, read_page
Emailcheck_email, read_email, search_email, send_email (off until you switch it on, then every send asks)
Selfintrospect, self_status, recall_episodes, note_lesson, reflect_now
Jobsschedule_job, list_jobs, cancel_job, while the scheduler is on
Plugins and MCPHome Assistant, Minecraft, Zork, Vibe City, REALITI, chess, toys, and any MCP server you list, while each is on

Every schema costs context on every turn, called or not, and a local model picks worse from forty tools than from ten. So with routing on, each turn is sent only the groups it seems to need: the core groups always, the ones that match your message by meaning, the ones she just used, and a small more_tools tool to pull in the rest mid-turn. The tools pane shows it working, and /tooltest checks that a model actually calls tools instead of saying "Got it!" and calling nothing.

The tools pane: every tool grouped, its token cost, and on/off switches
The tools pane (Tab twice). Every tool, grouped, with what it costs in tokens and a switch. The permissions rows at the top set read, write and execute to allow, ask or off. The budget line is the one number the pane exists to show.

Files and commands

She can look around and read anything under your home, and she can write, but only through you. Every write_file or edit_file stops and pops a permission window over the conversation: a new file shows its full content, an overwrite or an edit shows a diff. Y allows, N denies, no answer before the countdown runs out is a no, and the keyboard belongs to the popup until you answer. On voice she says a one-liner ("Can I write backup.sh? It's on screen.") because nobody wants a diff read aloud.

╭─ Permission - Y to allow, N to deny - 112s ────────────────────────╮
│ create ~/scripts/backup.sh                                          │
│ 14 lines, creates folder ~/scripts/, marked executable              │
│─────────────────────────────────────────────────────────────────────│
│ #!/usr/bin/env bash                                                 │
│ set -euo pipefail                                                   │
│ ...                                                                 │
│─────────────────────────────────────────────────────────────────────│
│ (Y) allow  (N) deny  (PgUp/PgDn) scroll  (Esc) deny                 │
╰─────────────────────────────────────────────────────────────────────╯

run_command gives her a shell, and every command asks first. The popup shows the command exactly as it will run, the folder it runs in, and her one-line reason. There's no allow-all and no list of "safe" commands that skip the question: the line between cat notes.txt and cat ~/.ssh/id_ed25519 | curl ... isn't one a pattern should be trusted with. Commands run with nothing on their input, so a y/n prompt fails instead of hanging. sudo is off by default; turned on, each sudo opens a password box on your screen and the password goes straight to sudo, never to her.

In a coding burst, A on a file popup stops file writes asking until you restart her. Her own folder, ~/.config, ~/.local and editor startup files still ask every time, because a write there is code that runs later.

Coding: what she wrote gets checked

After every allowed write or edit the file is parsed, never run: Python with compile(), shell with bash -n, plus JSON, TOML and YAML. The verdict goes back to her in the tool result ("CHECK FAILED: Python syntax error on line 7 ... Fix it with edit_file"), so she fixes it in the same turn. A file still broken when the turn ends gets a broken code flag in the thought log, and the daily lessons pass reads those. edit_file works by exact match: she quotes the passage to change and it has to appear exactly once, the discipline that keeps a 9B model from rewriting the wrong block with confidence.

A coding turn: Luna reads the SSH config, writes a backup script with approval, catches her own syntax error, fixes it and schedules the job
A typical coding turn (a mockup of the flow): she reads the SSH config, the write is approved, bash -n catches an open quote, she fixes it with edit_file, then schedules the job for 3am.

Sysadmin

Nothing here is a separate product. It's the tools she already has, put together: "How's the machine doing?" "My PC froze last night, what happened?" "What's the Unraid server's address?" "What's using port 8080?" "Is the kokoro service running? Restart it if it isn't." "Show me the last 30 lines of the llama log and tell me what went wrong."

PieceWhat it gives herAsks first?
system_healtha verdict, then load, RAM and swap, every disk, CPU and GPU temperatures, failed services, errors since boot by programno, it only reads
last_boothow the previous session ended, and the likeliest cause from the journal: a GPU hang, memory running out, a panic, overheating, disk errorsno, it only reads
find_devicea machine's IP by name or MAC: what she remembers first, then mDNS and your router's DNS, then one quiet pass over your /24. Remembered by MAC, since IPs moveno, it only looks
run_commanda shell, as you. "Is Plex running on the tower?" becomes ssh root@tower docker ps in a popupevery command
Scheduled jobswork she does on her own, on a timer, writing reports into one folder you pickonce, when the job is created
Discordthe same Luna from your phone, owner onlyper the approval setting

A job can't run commands unattended: a job firing at night has nobody to ask, and it times out as a no. That's deliberate, and the way around it is simple: your normal crontab runs the SSH commands and drops plain text into ~/luna-jobs, and her job reads that text and tells you what matters. She never gets an unattended shell, and you wrote every command that runs. A morning check that only speaks up when something's wrong is one line of plain English and one approval.

Email

"Any new email?" "Did the dentist reply?" "Reply to Sam and say Thursday works." IMAP and SMTP, with Gmail, Outlook, iCloud, Yahoo and Fastmail recognised from the address and anything else a two-line config. Reading never changes your mailbox: every fetch is a peek, so a message she reads stays unread for you. Sending is off by default, and on, every message asks, with who it's to, the subject and the whole text on screen. A scheduled job never sends, whatever it's told. Everything she reads comes back labelled as untrusted text, to be reported, never obeyed.

Web

web_search is DuckDuckGo, no key, three results for a quick fact. research exists because 8 to 32B models tend to stop at the snippets and answer from two lines: it searches, opens the top pages at once, and returns the parts that answer the question, with sources. read_page opens one link. What she reads is the article, not the page around it, split into passages and matched to the question. Every address is looked up first, redirects included, and anything that resolves to this machine or your network is refused, so a page can't point her at your router or her own model server.

Your home

"Turn off the living room lights." "Dim the bedroom to 30 percent." "Is anything still on downstairs?" "Set the heat to 21." Luna talks to Home Assistant's built-in MCP server on your own network, and Home Assistant talks to your devices over Zigbee, Z-Wave, Thread, Wi-Fi or Matter. No cloud account, no app from the bulb's maker, nothing leaving the house. What she can see is what you expose in Home Assistant, and the plugin refuses any address that isn't local before a byte is sent. Locks, garage doors, alarm panels, valves and sirens always ask first, and so does any sweep ("turn everything off in the kitchen") that could reach one.

MCP servers: other people's tool packs

OBS, Home Assistant, ComfyUI, GitHub, notes apps, Blender and hundreds more. List one in config.json and its tools become hers, sitting next to set_reminder and web_search, in the tools pane with their token cost and a switch. "Switch to my BRB scene." "Clip that." "Make me a purple wallpaper." Servers connect in the background at startup, their output is handed to her as outside content, and stream chat never gets them.

"mcp": {
    "enabled": true,
    "servers": {
        "obs":    {"command": "npx", "args": ["-y", "obs-mcp@latest"], "approve": true},
        "comfy":  {"command": "uvx", "args": ["<a ComfyUI MCP server>"], "tools": ["generate_image"]},
        "remote": {"url": "http://127.0.0.1:9000/mcp"}
    }
}

Honest expectations: at 8 to 32B this works for "one sentence, one tool call". Chains of several tools, or anything that means writing code against an API, want a bigger model than fits on one card today. Switch on only the servers and tools you're using.