ArombaDocsChangelogSecurityStatus

Docs

Aromba — Assistant

1. What it is

The assistant is the AI inside the Telegram bot. It lives on the server, knows it (what runs, what changed, what usually happens), answers questions, explains alerts, and proposes fixes. It never executes a change itself: it proposes, the Warden runs what the user taps. It answers on credits of Aromba’s own — a capped key the relay mints for each paired server (PLAN D108) — or on whatever key the user connects, which replaces ours.

2. Characteristics

Target
Weightinside the daemon’s budget (section 3); adds no process, no runtime, no local model
Speedsimple answers under 8 s; text streams into the Telegram message as it arrives
Cost to the useron a cheap open model: well under $1/month for typical use; heavy use on a strong model stays under ~$10 (est.)
Cost to uszero — model calls go from the user’s server to the user’s provider
Knowsserver card, notebook, health baselines, receipts, project structure, its own history
Canread (filtered), diagnose, propose, write chores, propose enabling skills
Cannotexecute, read secrets or fenced paths, act on text it reads, change its own limits
Learnson its own, from arithmetic, not from the model (section 8)

3. Weight budget and what it forces

The whole daemon — Worker, Warden, chores, state — has a fixed budget. The assistant lives inside it.

BudgetComparison
Disk, core30 MBtwo Go binaries, chores, SQLite; user data (quarantine, history) counted and shown separately
RAM idle50 MB for both processesabout what sshd uses
CPU idleunder 1% of one corecollector reads /proc once a minute
RAM during a turnspike up to 150 MB, back to idle aftercontext assembly and streaming
Skillseach declares its own disk/RAM; shown before install; counted against a separate, user-adjustable skills budgete.g. voice (whisper) is +143 MB disk, +300 MB RAM while transcribing

What the budget forces:

4. Saving tokens — every lever

Order matters: the earlier the lever, the more it saves. Percentages are estimates (est.) of what each removes from a naive design.

#LeverSavesHow
1Router before the model~30% of turnsa script answers commands and frequent questions (“disk?”, “last receipt”, “is X running”, “what ran tonight”) from state. Zero tokens.
2Summaries before raw~60–80% of tool tokensevery tool returns a summary first (counts, unique errors, time histogram, 20 samples). Raw lines only on request, in pages.
3Stable blocks first, cached~60% of base inputsystem prompt, server card, tool schemas, notebook go first and don’t change for days; providers with prompt caching bill them at a fraction. The card carries what Aromba already worked out — the last scan’s findings, what the watchers said in the last day, and the chores it may offer, by name — so a question starts from what is known instead of spending steps rediscovering it, and a small model never invents a chore that does not exist (PLAN 3.11’s bench found both).
4Short context—✅ the last few exchanges, not the whole chat. What falls out becomes one line at the front — “Earlier in this chat I asked about: …” — so “and the other one?” still means something after seven turns. The line is written by the Worker, in a few words a question, never by a model: a summary that costs a call costs more than the turns it replaces.
5Answer cache~10% of turnssame question, same state, 24 h → answered with what was said before, and the chat says so: an answer repeated without saying it is a caretaker pretending to have looked. “Same state” is strict — a scan, a chore run or an alert since makes it a different server, so on a busy one the cache almost never hits.
6Per-turn ceiling that scalesbounds the worst case✅ three quarters of the model’s own context window, never under 40k and never over 150k: 40k on a 32k model, 98k on the everyday 131k one, 150k on a 200k one. depth: light / normal / deep in /settings halves it or doubles it, and the two bounds hold either way. It ends the looking, never the answer — the turn is told to answer with what it has, and may still offer the fix. It is not a budget and is never off; the budget is the person’s own and off until they set one. ✅ A scan that finds ten or more things running says so once, quietly, with a button that sets deep: on a server that size the ceiling is what the person feels first, and a knob nobody knows about is not a setting.
7Ask before going deep—✅ answered by levers 6 and §6 together rather than by a question in the middle of a turn: the ceiling ends the looking and the turn answers with what it has, and [Look deeper] under that answer is the person’s own way to spend more. A turn that stops to ask is a turn the person has to watch (PLAN D142).
8Loop and repeat guard—same tool + same args twice → stop; max 6 tool steps.
9Learning without the model—section 8: all learning is arithmetic; one model sentence a week.
10Show the price—cost per answer in /status, optionally under each answer. People self-regulate when they see cents.

Result (est.): a diagnosis that would cost 30–40k tokens naively costs 8–12k here. On a cheap open model that is a fraction of a cent.

5. Scenarios → tools → adaptive limits

Tools follow from what people actually ask. The five scenarios that cover most turns:

ScenarioExampleTools usedTypical tokens (est.)
Status question“how’s the server?”router, or status0–3k
Alert explained“Take a look” on a restart looplook (the unit’s or container’s log), health6–10k
Fix proposed“start redis”propose, then explain+2k
Setup“prepare for Next.js + Postgres”inventory, write_chore, skill_propose12–20k
Custom job“back up Postgres to Backblaze nightly”write_chore10–15k

Tools

Every call goes through the Warden’s policy; the model sees the schema, the engine decides.

ToolColorFirst pageExecuted byDoes
status()green30 linesWorkersame as /status
health(metric, range)green60 pointsWorkeraggregated series, never raw samples
inventory()greensummary + pagesWorkersoftware, ports, services, containers, cron
receipts(days)green100 linesWorkerrecent receipts
notebook()green100 linesWorkerlearned facts
read_file(path, page)green400-line pagesWarden (path policy, redaction)allowed paths only
look(command, args)green40 lines, and how many were cutWarden (allowlist)one read-only command and its output: df, du, ls, ps, docker ps, docker logs, journalctl, systemctl status, ss, free, uptime, cat on allowed paths. It is logs and run_readonly in one tool, which is what shipped: all three ended in the same sentence — run a command the Warden allows and read what came back — and one schema, one refusal and one policy row are better than three (PLAN 3.3)
plan(title, goal, steps)greenone plan a turn, 8 stepsWorker → the chatWrites a plan down when one move is not enough and shows it. It runs nothing: a chore step runs with the approval that chore always needs, a check step is one read-only command the Warden decides on, an ask step is a button. The owner moves it a step at a time, and /task is where it lives (PLAN D119)
chores(), offer_chore(name)greenthe list; one offer a turnWorker → the chatchores lists what this server can be asked to do; offer_chore puts one to the owner with a button and runs nothing. The tap runs it the way /run does — dry-run pair, approval, receipt — whatever its colour (PLAN D109)
propose(cmd, why, touches, undoable)red—Worker stores, Warden runs on tapapproval message with the exact command and a plain-language line
write_chore(file)red—Worker → mine/written to the chore brief (PLAN D234); the header is checked, and the chore is shown whole with [Save it] [Not this]. Saved red and off; /run tries it dry first
skill_propose(name, why)red—user tapsproposes installing a skill from the catalog, showing its weight and permissions: /build’s second place to look, after this server’s chores (PLAN D223)
undo(action_id)red—Wardenrestores the snapshot taken before a red action (7 days)
explain(cmd)green5 linesWorker (model)“what this will do” for a non-admin; under every red button
web(url)green400-line pagesWorkerdocumentation domains only; provided by the docs skill, off until enabled
calendar(days)greendays 1–14; anything else refusedWorker (the calendar, PLAN D107)the merged timeline: Aromba’s plan, the user’s items with their titles, and connected calendars’ busy and free time and marked events by word and time — their titles only with the “Calendar titles” setting on (the calendar skill)
calendar_add(item), calendar_done(id)greenone item a turnWorkeradds, moves or finishes an item in Aromba’s own calendar at once, with [Undo] under the message: it changes nothing on the server. Never a calendar of the user’s own (PLAN D107)

The build turn (15-instruments.md, PLAN D210) is not the chat’s. A product on this machine hands over a job; the turn has its own short prompt, the product’s brief, the job and the library, and three tools of its own — use_instrument(slug, params), read_instrument(slug), write_instrument(slug, name, description, input_schema, params, page) — plus none_fits on the cheap step. None of them reads this server: status, look, health and the logs are not in it. The cheap model reads the job against the library’s eight nearest instruments and answers from one, or says none fits; only then does the job’s writer — the model its direction has at the tier the owner chose (PLAN D224) — write, once, and what it writes is checked in code and put in front of the owner. An exact repeat of an earlier ask is answered with no model at all. The person’s daily budget covers these turns as it covers questions.

The models a product’s pages are written on (PLAN D216, D224), by the direction a job names, as providers.toml ships them for OpenRouter, and the model /build works on, which is the programming direction’s (PLAN D231). They are open models only, each with its weights published, and the catalog refuses one that is not (PLAN D235): on the same work the closed ones cost five to a hundred times as much. Each page direction was chosen by writing the same pages on several models and reading every one at phone width; programming by /build’s find bench, the twenty programming cases of REPORTS/cases.md on a server with the core chores and the catalog, and by reading the chores each model wrote for the six that need one. /model <direction> moves a server between the three; the default is the middle one.

DirectionBestDefaultCheapest
healthz-ai/glm-5.3deepseek/deepseek-v4.1-flashz-ai/glm-5.3-flash
hobbyz-ai/glm-5.3deepseek/deepseek-v4.1-flashz-ai/glm-5.3-flash
financez-ai/glm-5.3deepseek/deepseek-v4.1-flashz-ai/glm-5.3-flash
designz-ai/glm-5.3deepseek/deepseek-v4.1-flashz-ai/glm-5.3-flash
programmingmoonshotai/kimi-k2.7-codedeepseek/deepseek-v4.1-flashopenai/gpt-oss-120b
incidentz-ai/glm-5.3z-ai/glm-5.3deepseek/deepseek-v4.1-flash
usersz-ai/glm-5.3z-ai/glm-5.3deepseek/deepseek-v4.1-flash

incident answers a product’s report of an error one of its users met (PLAN D279), and the screen, when its words cannot settle it, is described by z-ai/glm-5.3-flash, which reads pictures. users is the pages a product’s own users order, sent without a tap by the owner’s rule (PLAN D277): a job under that rule that names no direction is written there, on the model a bench of three real user asks found strongest at about $0.16 a page (PLAN D278). A direction whose best and default are one model writes a page on its cheapest when that model could not write one that was kept.

A page of any other direction is written on deepseek/deepseek-v4.1-flash. Whether an approved page answers a job, is extended for it, or none fits is decided on deepseek/deepseek-v4.1-flash (PLAN D229, D235). Every page is written to a design brief and then reviewed and written again as a designer would (PLAN D228).

Adaptive limits

Limits are pages, not walls:

6. Suggested replies and voice

7. Models

The user connects an OpenRouter key, or runs Ollama on the server itself — and nothing else: Aromba runs on open models only (PLAN D235, D236). The Anthropic and OpenAI providers were taken out. On this bench’s work a closed model costs four to thirty times what the best open one does, and a person setting up should not reach for one and pay hundreds a month for what open models do for a few dollars. Wherever a key is set up — the chat’s /key, aromba key, and the message when a key stops working, which is how Aromba’s own credits running out reaches the person — they are told so in one sentence (provider.OpenModels). They pick the model; they pay the provider. Aromba’s requirements: tool calling and a 32k context; the assistant tests the key with one tiny call when set.

The audience is people who can open OpenRouter and pick an open-weights model that matches top closed models on this kind of work for a few cents per million tokens. For them: /model lists what their key can reach, with price per million next to each. For everyone else: the dashboard’s Models page shows three recommendations, balanced for price and quality, refreshed with each release, with the exact price and a one-tap “use this”.

There are no tiers and no AI cost that depends on the plan. A server that has no key of its own is given one at pairing, minted by the relay against our own account (PLAN D108). The ceiling is per person, not per machine (D125): each server has a key of its own so one can be revoked alone, and when an account’s servers together reach the month’s allowance all of its keys stop until the 1st, when they start again: the credits are ours, the calls still go from that server straight to the provider, and a key the user sets themselves replaces it. An account may be given an allowance of its own, on the relay (D222). /key says which of the two is answering and what is left of the month.

7.1 Two models, and moving between them by itself

Every provider names two: the everyday one that answers almost everything, and the one a turn moves up to (deep in providers.toml; assistant.deep overrides it, off turns it off). On OpenRouter they are openai/gpt-oss-120b for the everyday question and z-ai/glm-5.3 for the move up, chosen on the five servers in trouble the replays hold the chat to (PLAN D236). Nothing asks a model whether a model is needed — the decision is made from what the cheap turn already showed, so it costs no tokens and no time:

The cheap turnWhy it means the cheap model is out of its depth
tool arguments that are not JSONit cannot call the tool, it will not write the answer
the same tool with the same arguments twiceit is going in a circle
nothing said at allthere is nothing to send
every step used and still told to answerit did not converge
more than 3 tools, or 12k tokens of evidencemore reading than a small model holds
an answer that hedges after using a tool (not sure, unclear, might be)it is guessing
the person asked, by word or by buttonthey asked

A question that is about code — a path, a file extension, a traceback, “write a chore”, “why does X fail” — starts on the stronger model; trying the cheap one first would only cost a round trip.

A turn of a direction — /build — does neither (PLAN D231). It runs on the model the owner chose for that work, start to finish, and nothing moves it: the move up is the everyday model’s, and the direction’s own model is not a cheap one waiting for a stronger one. Each of its steps may write 8,000 tokens, because a chore goes out whole inside one tool call, and at the chat’s 600 the strongest model’s chores were cut off mid-line and could not be read. Its answer has no Look deeper; /model programming is where it changes.

The move is one call over the evidence already gathered, with the instruction not to start over: the tools do not run twice. The cheap answer is sent to the chat the moment the turn decides to move up, ending with its own line — Handing this to a stronger model: the cheap model was not sure. One moment. — and the stronger answer arrives as a second message that does not repeat the first. If the stronger model then fails, the chat is told that and the first answer stands. A question that started deep says so under its answer. Either way the receipt names which model answered and why.

8. What it learns on its own

The second week must be smarter than the first without the user teaching anything. Six sources, all free of tokens except the last, all written to the notebook as plain lines the user can read and delete.

8.1 Rhythms — from the health collector

8.2 Baselines — what normal is for this server

8.3 The user — from their buttons, never from their words

8.4 The project — from what runs, not from what was written

8.5 History — its own past

8.6 Gaps — quiet suggestions, one per week at most

✅ The only learning that uses the model, and the only place in Aromba where a model is asked to suggest rather than to answer. Finding the gap is arithmetic like everything else above; the model turns the facts into one sentence of at most twenty words, and the chore that would close it is offered under it with its own button.

One a week at most, and each gap once — not again for ninety days, because a person who did not set up a backup in three months heard the suggestion and decided. A server with no AI key says nothing at all, and nothing is written down when it does, so the week is not spent.

Built:

Still to come, each with the watcher that finds it: the domain that expires in 20 days (a WHOIS watcher), and the dead projects holding 12 GB (a growth watcher that knows a project from a folder).

Rules that keep it from being creepy

9. Failure modes

What breaksWhat the user seesWhat keeps working
Provider down or slow“AI is unreachable right now; I’ll retry in a minute.” Then “Still down; chores are unaffected.”everything except the assistant
Key invalid or out of credit“Your AI key stopped working (provider says: …). aromba key to replace.”everything else
Per-turn ceiling hit“Ran out of room on this one. Here’s what I found so far.”—
Model proposes a denied command“The assistant proposed a blocked command; not run.” + ladder counter—
Tool loopstops at 6 steps or on repetition; reports—