Skip to content

What the hub keeps

The hub is your machine. Nothing on this page is sent to us, and most of it is not sent anywhere at all. What follows is what it writes down, where, for how long, and who in the house can read it.

Read the vocabulary note first if you have not — guaranteed, merely likely and unguarded mean specific things below.


A hub that nobody has configured keeps no record of anything anybody said. That is the shipped state and it is not a suggestion: the switch below defaults to off, and the code asks whether it is on before every single write rather than reading it once at startup.

Four things exist anyway. Three of them are in memory and die when the hub restarts. One is on disk and lasts two years.

So that “no, the other one” works, the assistant keeps the last few turns of a conversation in memory:

SurfaceKept forKeyed on
Voice — a handset, the phone app, a browser tab5 minutes after the last thing saidthe microphone
The console assistant2 hoursthe signed-in account

Then it is gone, and nothing was written to disk. The five-minute figure is the one quoted most often; the console’s is longer because somebody typing at a screen expects to come back to it.

Anybody who can talk to a conversation can also clear it, from the console’s voice button, from the phone, or over DELETE /api/voice/conversation/{source}. A clear reaches every screen sharing that microphone, which is the honest behaviour rather than a limitation — see “your sessions” means your account’s below for why a browser tab is not a person.

The live event and command feed — in memory

Section titled “The live event and command feed — in memory”

The hub’s Activity page (/logs) shows what the house is doing: the wire trace, the hub’s own log, the event feed and the commands that ran. Two of those are bounded rings in memory — the last 400 events and the last 500 commands — and both are lost on restart.

The event feed no longer carries the words. When the assistant hears something, the frame published on the bus says that an exchange happened, in which room, whether it worked, and a turn id — not what was said or what came back. It used to carry both, which made the ring a transcript that the conversation-log switch did not govern. A reader entitled to the words follows the turn id to the conversation log, where the audience rules are the ones described below.

Anything you build on top of an event — a rule that triggers on somebody speaking, a webhook — is yours, and what it does with the words is outside anything described here.

This is the record most people do not expect, so it is the one worth reading carefully.

Every AI call the hub makes — reasoning, vision, transcription — adds to a rollup on disk. It is on by default, it has nothing to do with reMaestro Cloud, and it exists because the hub is the only party that can see all of your AI spend: a call made straight to OpenAI on your own key never touches any service of ours.

One row per (hour, who asked, kind of work, where it went, model, outcome), with counters added into it. Storage grows with how many kinds of thing happen per hour rather than with how much you use it.

RecordedNot recorded
How many calls, how many succeeded, how many failedThe prompt
Tokens in and out; seconds of audioThe transcript
Cost, where it is a figure the hub could knowThe reply
How long the call took, and the slowest oneThe filename, the language hint, the room name
Which model, if you named one in settingsAnything anybody typed or said
Who or what asked — see below

Retention: two years, then deleted. That horizon is enforced by deletion on both the write path and the read path, and the tests prove that rows go rather than that a constant says 730 — a horizon nothing enforces buys the storage of keeping everything with the appearance of a limit.

You are told this during setup, on the page where you create the first account, rather than only here. There is no switch and you are not asked to agree to anything — this record is how the hub can show you your whole AI spend including the calls that never touch any service of ours, and it holds no words. What the setup page says is what this section says: counts for two years, none of the words, filed against a microphone where nobody was signed in, and readable by an admin.

Nothing here leaves the box. It is not reported to us, and it is deliberately excluded from the backup bundle: these rows say what each person in a household asked a model for and when, and restoring onto a second hub would import the first hub’s history as though it were the second’s.

Visible at /usage, Admin only.

The rollup records an origin, and it is honest about the difference between who and what:

OriginMeans
user:{id}A signed-in person genuinely initiated it
remote:{deviceId}A handset or satellite microphone
browser / appA screen or a phone with nobody signed in
hubThe hub on its own — a rule firing, a self-heal on a schedule

It is an account id and never a name or an email address: the console resolves it to a name when it draws the screen, so deleting an account takes the person’s name off every historical figure at once.

Voice cannot be attributed to a person and never will be. A physical remote reaches the assistant through an evdev key press picked up by a background service; there is no request, no session, no cookie and no principal anywhere on that path. A remote does not know who picked it up. Attributing it to whoever was last signed in somewhere else would be a claim nothing in the house could support, and it would look identical on screen to a true one.


Settings → AI → Keeping what was said. Admin only, off until somebody turns it on.

What changes: the hub starts writing down what was transcribed, what was typed, what came back, which tools the assistant called, and which commands actually reached a device — so you can read a conversation afterwards and see what it really did, rather than inferring it from a chart.

Kind of lineWhat it holds
typedSomebody typed it, or tapped a choice. The words are the words
heardSomebody said it and a transcriber turned it into text — kept apart from typed, because a transcript is a reading of what was said and can be wrong in ways typing cannot
repliedWhat the assistant answered
askedWhat it asked back, when it wanted something settled first
toolA tool it called, with which device, which capability, and what value
ranA command that actually reached a device because of this turn, with the device name and a summary of the arguments
failedThe turn ended badly

Each line also carries when, which conversation and turn it belongs to, which microphone or screen it arrived on, and whether it worked.

Text is capped at 2,000 characters, applied on the way in. Truncating on the way out would mean the whole of a pasted document sitting in the database with a screen politely not showing it — which is exactly the shape of “we kept more than we said we did”. What somebody says to a light switch is a sentence; a caller that posts four thousand words gets the first two thousand kept and an ellipsis.

Only commands a conversation caused are recorded. A rule firing at sunset, a tile somebody pressed, an activity powering the gear down, a poll asking a projector how it is — none of that is session data, and recording it would turn an opt-in record of conversations into a log of everything the house does.

90 days by default. The choices are 7 days, 30 days, 90 days, a year, or Keep everything.

Ninety rather than thirty because a household investigating something odd very often notices it late: the pattern is “the lights have been doing this for a while”, not “the lights did this an hour ago”. Two years — the horizon the counts use — would be wrong here, because the questions this record answers are asked within days and essentially never in year two.

“Keep everything” is a labelled choice on the dropdown, not a blank field. A box that happens to mean forever when you clear it is a behaviour only discoverable by somebody who already went looking. Choosing it prints, under the dropdown: “Nothing is deleted on a schedule. What is kept stays until you switch this off and choose to delete it, or empty it by hand below.”

Anything else prints “Anything older than 90 days is deleted automatically, whether or not anybody is looking.” — and that is what happens: expired lines are deleted on the write path and on the read path, so the horizon does not depend on somebody having the page open.

Changing the horizon prunes immediately rather than at the next sweep.

Switching it off asks what to do with what it holds

Section titled “Switching it off asks what to do with what it holds”

Turning the switch off does not silently do either thing. It opens a dialog offering both, because stopping and keeping and stopping and deleting are two different intentions, and the moment somebody reaches for the switch is the moment they know which one they have:

  • Stop and keep what’s there — nothing more is written; what exists stays, and stays subject to the horizon.
  • Stop and delete it all — everything goes.

There is also an Empty it now button that deletes everything without touching the switch. Nothing else in the product can clear this log: a record that empties for reasons its owner cannot account for is the opposite of what it is for.

“Your sessions” means your account’s

Section titled ““Your sessions” means your account’s”

Read this before switching it on. It is the sentence people find uncomfortable, and it is true.

  • Any admin account on this hub can read every conversation on it, including ones they were not part of.
  • Everybody else reads only conversations carrying their own account id — which on this product means the console assistant and nothing else.

That second line is narrower than it sounds, and the reason is the same one that makes voice unattributable above: source names a microphone, not a person. browser is the literal that every dashboard and every tab in the house sends. A handset on a coffee table has no session and no principal. So a sentence somebody spoke into the den handset is not theirs in any sense the hub can act on — it belongs to the handset, and it is therefore nobody’s to review but an admin’s.

The two alternatives were both worse:

  • Attributing a handset to whoever was last signed in somewhere else would be a claim about a person that nothing in the house could support, and it would look identical on screen to a true one.
  • Showing every unattributed conversation to everybody would mean each person in a household reading every word anybody had spoken to the box — the same privacy failure arrived at from the other side.

So most of what is kept is admin-readable, and if that is not a posture your household wants, the switch is the control — not the audience rules.

The review page counts what it is not showing you. A screen that cannot tell “nothing was said” from “none of it was yours” reads as broken, so it says how many conversations in the period are being withheld. It is at /sessions, reachable by anybody signed in — deliberately, because gating it by role would mean a household could be recorded without a single person in it being able to see the record.

Not to reMaestro Cloud, not in the backup bundle, not in the encrypted copy the cloud holds if you use cloud backup. Nothing is billed on it and nothing about it is ours.


Guaranteed

  • No transcript, prompt, reply, filename, language hint or room name can enter the counts-and-costs record. The constructor is private, reflection over the types rejects anything else, and a behavioural test hunts for a planted phrase.
  • The session log is excluded from the backup, in both directions, including the import direction that a weaker test would have missed.
  • Conversation logging is off until somebody switches it on, and the switch is read before every write rather than captured at startup — so turning it off takes effect on the next line, not on the next restart.
  • A failure to read the setting is answered “off”. The failure mode of a privacy switch has to be “kept nothing”, never “kept everything because the database was busy”.
  • Cost cannot be recorded against a destination whose price the hub could not know. The forcing happens where the record is built, so a caller that worked out a figure for a gateway call cannot get it into a row.
  • The settings card and the review page must both keep stating who can read what.
  • Retention horizons are enforced by deletion, and the tests assert that rows go.
  • The setup page and /usage both keep stating the two-year horizon, and both are pinned to the constant that does the deleting rather than to each other — so a page still saying “two years” after the horizon moved is a build failure. The same test pins that setup asks for exactly three fields, which is what keeps the disclosure from quietly growing a tickbox.

Merely likely

  • That the two-year horizon on the counts record is one you want. It is still the only thing on this page you are not asked about — there is no switch and there is no dropdown. What changed is that you are now told, on the setup page, instead of only here; being told is a weaker thing than being asked, and it is named as the weaker thing rather than dressed up as a choice.
  • That a household reads the disclosure on the settings card before switching logging on. It is on the card, in bold, and nothing makes anybody read it.
  • That pruning is actually keeping up. It runs at most once an hour per process, on the read and write paths; if the database refuses, text accumulates past the horizon until it succeeds. Nothing reads past the horizon, so this is a privacy matter rather than a correctness one — and it is logged rather than silent.

Unguarded

  • A conversation while it is still open. GET /api/voice/conversation/{source} returns the live five-minute conversation to anyone signed in, or to any read-scoped API key, whichever way the conversation log is set. The phone app reads it, and source cannot be narrowed to a person, so it is deliberate rather than missed. The event feed and the Activity page no longer carry the words.
  • Which conversations are whose. source is supplied by the caller and never checked against who is signed in, so it identifies a microphone and cannot be relied on to identify anything else.
  • Anything you build on an event. A rule or a webhook that fires on somebody speaking can send those words wherever you pointed it, and nothing here constrains that.
  • A plugin you installed, and everything it is handed — including who is in the household. This is the one entry on this page about a party we cannot name in advance, because you choose it and it does not exist when this page is written. A plugin is an ordinary program running with the hub’s own privileges: there is no sandbox, no permission prompt and no capability grant, and no amount of packaging or signing changes that — signing says who published this, never this is safe.
    What that means concretely, because “unbounded” is not a useful sentence on its own. When you configure a plugin on its own page, the hub hands that plugin what you typed and the name your account goes by, so it can address you rather than a row. It can log those names, keep them, or send them anywhere, and nothing in the product observes or constrains it. The same is true of everything else it can reach: your devices, their state, and whatever it does with the network. The hub does its own part — a setting you declared as a credential is encrypted in the database and in your backups, and a plugin’s own settings belong to the person who typed them rather than to the house — but all of that stops at the moment the plugin is handed the value.
    Nothing about who is signed in reaches anything else below the interface. A command, a device and the AI assistant all still run with no idea who asked; this is the one boundary a person crosses, and it crosses only to a plugin you installed on purpose.
  • The update check, which is one of the three requests this box makes to us. Unless it is switched off in Settings → Updates, the hub asks releases.remaestro.app on a timer for the release manifest. That carries the box’s internet address, the channel in the URL (stable.json or beta.json), and the running version in the User-Agent — no hub id, no account, no device list, and nothing derived from anything on this page. It is named here because “nothing on this page is sent to us” is a statement about records and not about sockets, and the difference is the sort of thing a reader is entitled to be told rather than to work out. Switching automatic checks off stops the request.
  • The plugin list, which is the second, and which is not on a timer. Opening Plugins → Marketplace asks extensions.remaestro.app for the signed list of published plugins; installing one from that list asks for that plugin’s own signed document. The page draws the copy it saved last time immediately and revalidates behind it, so it never waits on us — the request is unchanged, and it is caused by the navigation rather than by a clock. Those carry the box’s internet address and which plugin was asked about, because it is in the URL — no hub id, no account, no list of what is installed, and, unlike the update check, not the running version: the hub fetches the whole document and works out at home which build it can run. Nothing fetches this on a schedule, so a household that never opens that page never makes the request at all, and installing a plugin from a link somebody handed you does not involve us in any way. The archive itself comes from wherever its publisher keeps it, which is usually somebody else entirely.
  • What people thought of those plugins, which is the third, and which goes to a machine of ours. The other two are static files on a file host; this one is cloud.remaestro.app, and the difference is worth stating rather than glossing. Ratings cannot live in the signed plugin list — that list is signed when it is published, and an average that moves when somebody clicks would mean re-signing the whole feed each time, which turns the signature from “we published this” into “this was true at 14:03”. So opening Plugins → Marketplace asks that host for the ratings, straight after asking for the list. That read carries the box’s internet address and nothing else — no account, no hub id, no version, and not which plugin is being looked at, because it is one request for every rating there is and the hub matches them up locally. Nothing fetches this on a schedule either, and no record of the request is kept. Leaving a rating does need a reMaestro Cloud account, and on a linked hub the press sends which plugin and how many stars under the hub’s token — one rating per plugin per account, changeable, and a withdrawal deletes it rather than hiding it. There are no written reviews: there is nowhere to type one and no column that could hold one, and a test fails the build if either grows. On a hub with no account the buttons do not appear, the ratings still show, and a failure to fetch them costs one quiet line and nothing else — browsing and installing are unaffected, and an unrated plugin reads as “no ratings yet” rather than as nought stars.
  • Plugins → Installed is not one of the three, and that is worth saying. It lists what is on the box, from the box, and makes no request to anybody — so checking whether a driver is running, or removing one, is entirely local. The split is described here rather than left as an interface detail because it is the difference between “the plugins screen talks to us” and “one of the two plugin screens does”.
  • The wire trace. Command arguments appear on the Activity page’s trace with credential-shaped keys masked — but the masking works on the argument’s name, so a value that arrived under a name like text or channel is not masked. There is a separate guard that stops the assistant authoring such a value, but the trace itself is not a redaction boundary you should rely on.