User guide · Software preview
Security
This page names the internal pieces that make Maisnie’s security posture what it is — useful vocabulary if you’re troubleshooting or reading its command-line output, not something you need to think about day to day.
The pieces
Maisnie is built from a handful of small processes. The one you actually reach in a browser is called the gatekeeper: it’s the only one bound to a network-reachable address, and the only place a login is ever checked. Behind it sit the Desk (the setup wizard) and the Surface (the Home page you read day to day), both bound to loopback only — they never bind to your network directly, and neither checks who’s asking: they trust that anything reaching them either came through the gatekeeper (which already checked) or is another process running on this same machine. That second case matters — it means any program running on this machine, under any user account (loopback ports answer any local caller regardless of who they’re running as), can read your vault through the Surface, or reconfigure the box (including writing new secrets) through the Desk, with no login of its own. The gateway is the model-routing logic the Desk calls into for the “Save and test” button and connection validation, and the same logic Ask runs as a short-lived subprocess, one per question, to actually answer something; it isn’t a network-facing piece itself — nothing binds a port for it — but it is what starts a real, possibly paid, model call, and every component above (Desk, and now Ask through the Surface) can trigger one.
This matters for one reason: there is exactly one door, and everything about claiming, sessions, and lockout below happens at that one door, not scattered across several pages each checking things their own way. It also means this preview isn’t meant for a machine other people log into — anyone with any account on it stands inside the trust boundary the gatekeeper’s login otherwise protects, so run it on hardware only you (and people you’d trust with the vault anyway) have any account on.
Claim-at-first-reach
Until the box has an owner, every request to it — any path, from any browser or script — is
redirected to the claim page. There is no way to browse, configure, or otherwise use an unclaimed
box; it has nothing to say to anyone until someone claims it. This is why you should claim the box
before you ever expose it beyond loopback (maisnie up --lan, or forwarding a port to it): an
unclaimed box on your LAN is a box anyone on that LAN can claim before you do, and once claimed by
someone else, it’s theirs.
Sessions and lockout
Claiming sets a passphrase (checked with 600,000 rounds of PBKDF2, so guessing it costs a real,
deliberate amount of time per attempt) and logs you in with a signed session cookie — stateless,
so there’s no server-side session table to protect, but that also means a session token can’t be
individually revoked before it expires; only rotating the whole signing secret invalidates every
session at once. Sessions last 30 days by default and quietly renew themselves partway through
that window as long as you keep using the box, so an active session doesn’t suddenly expire on
you mid-use. Log out, on the masthead of every page once you’re signed in, ends only the
session of the browser that clicked it — a confirmation page first (“Log out of Maisnie?”),
then a POST (carrying that session’s own CSRF token, so nothing but a form on the box’s own page
can trigger it) that clears the cookie and sends you back to the login page; Maisnie itself keeps
running and syncing your sources while you’re away. To force every session out at once instead — a
lost or stolen logged-in device, for instance — stop the box and remove session.key from the
gatekeeper’s state directory (or run maisnie unclaim --yes, which removes it along with the claim
itself and requires re-claiming the box); short of that, there’s no way to sign out a device other
than the one in front of you.
Five wrong passphrase attempts lock out further tries for 15 minutes, counted per source address and checked before the passphrase itself is even read — so a locked-out guesser can’t keep spending the box’s PBKDF2 budget while waiting out the cooldown. The lockout page never says how many attempts were made or how close any of them came; that’s a detail worth denying an attacker even by omission.
Away from home
When remote access is on, Maisnie listens a second time, on a loopback-only address that only the tunnel client running on the same computer can reach — so a request that arrives there is known to come from away by the socket it came in on, never by anything the request claims about itself. From away, three things hold whatever else you’ve set: Setup (the wizard, Settings, and the claim page) is never reachable — you’ll see “Setup is only available at home.” — the login itself is unchanged and required, and a session is read-only until you turn on Give remote sessions full access in Settings, at home: Home, Search, People and the briefing can be read, but asking a question, deciding a People suggestion and refreshing the briefing are refused with a page that says so. That judgement follows the request, not the cookie: a logged-in cookie from your home browser replayed through the tunnel is still read-only there, and a session that was ever used from away stays read-only even if it later arrives at home. The lockout counts wrong passphrases per visitor, using the address the tunnel’s edge reports rather than the tunnel itself. The tunnel’s own connection is encrypted end to end and HTTPS is terminated at its edge; the last hop, from the tunnel client to Maisnie on the same computer, is plain HTTP on loopback and crosses no network, which is why there is nothing to set up for TLS on that side.
CSRF
Every form on the box carries an anti-forgery token, plus a check that the request actually came
from the box’s own origin. Both have to hold for a form submission to be accepted; a request
forged from another site, or missing both Origin and Referer entirely, is refused. The token
itself is one of two kinds depending on whether you’re logged in yet: the claim and login forms,
and every step of the setup wizard, use a random value set as both a cookie and a hidden form
field (a cross-site attacker’s forged form can’t read your cookie to copy the value in, even
though the browser sends the cookie along); once you’re logged in, the gatekeeper’s own forms use
a token derived from your session instead, which is naturally invalidated the moment the session
is. Neither kind is single-use — the same token remains valid for repeated submissions until it
rotates.
What leaves the box
What crosses your network, and to whom, depends entirely on which model door you set up in choose a model — Maisnie itself makes no other outbound call on your behalf beyond the specific sources you turned on and the specific AI connection you configured.
| Door | What leaves your machine |
|---|---|
| Your subscription’s CLI | Whatever that official application itself sends to its provider, under the terms of your subscription — Maisnie doesn’t intercept or read the provider’s response before it does, and never touches your subscription’s own session or token. |
| Direct API key | The request Maisnie builds (your prompt and whatever vault text is included) goes straight to that provider’s API — encrypted when the base URL you entered is https:// (Maisnie accepts a plain http:// base URL too, if you type one; use the provider’s https:// URL). |
| Aggregator | The same request goes to the aggregator’s endpoint (again, encrypted only if that endpoint is https://), which then routes it to whichever underlying model you picked; the aggregator sees the request, and the underlying provider sees whatever the aggregator forwards. |
| Local model | Depends entirely on the endpoint you typed: nothing leaves your machine if it’s 127.0.0.1/localhost; your prompts cross your LAN, in the clear unless that endpoint serves https://, if it’s another device. Maisnie does not check where the endpoint actually points — see choose a model. |
Regardless of the door, Maisnie’s own telemetry never records the content of a prompt or a reply — it records that a call happened and which job asked for it, the lane, provider, model and tier that served it, input/output/cache token counts, cost, duration, outcome, and which lane (if any) it fell back from. What a provider itself retains or does with a call it receives is between you and that provider; check their own terms, especially for the subscription door, since Maisnie can’t speak for what your provider does on its side of that connection.
Secrets at rest
A private calendar link, a mail app password, a model provider’s API key — every one of these
lives in Maisnie’s configuration document on your machine. Whether it lives there sealed (as
ciphertext only your own machine can open) or as plain text depends on two things: whether the
optional cipher is installed, and what your operating system offers to protect the key. Maisnie
never claims more than what actually holds on the machine in front of you: Settings, maisnie
doctor, and maisnie secrets status all say, in one sentence, which of the rows below you are on.
The cipher is an optional install. Maisnie’s core has no dependencies at all, and Python’s standard library has no authenticated cipher, so sealing needs one extra package:
pip install "maisnie[secrets]"
The Docker image already includes it. Without it, nothing is sealed, and the document is exactly what it was in earlier releases — plain text in a file only your user account can read.
| You are running on | What protects a stored secret | What that does not protect against |
|---|---|---|
| Windows, cipher installed | The secret is sealed. The key that opens it is wrapped by Windows for your user account — a copy of the key file is useless on another machine or under another account. | A program already running as you on this machine: it can ask Windows to unwrap the key exactly as Maisnie does. An attacker with your disk and your Windows login. |
| macOS, cipher installed | The secret is sealed. The key that opens it is a generic password in your login keychain, unlocked when you sign in. | A program already running as you with your keychain unlocked. An attacker with your disk and your login password. |
| Linux and everything else, cipher installed | The secret is sealed. The key is a second file (secret.key, mode 0600) in Maisnie’s data directory beside the configuration. A backup or a copy of the configuration document alone opens nothing. |
Anyone who can read both files: you, root, or an attacker with the disk. The two files are protected by file permissions only. |
| Any system, cipher not installed | File permissions alone: the document is written mode 0600 (on Windows, where that bit means nothing, the folder’s own permissions for your account). |
Anyone who can read the file — anyone with access to the account Maisnie runs as, or a copy of your backup. |
In every row, someone who is already running programs as your user account is inside the trust boundary — the same boundary the gatekeeper’s login draws — and sealing does not change that. What sealing does change is what a copy of the configuration is worth: a backup, a document pasted into a support message, a disk image without your login.
Every reader of the configuration document — the Desk, the gateway, every connector — needs to
know about the sealed form to open one; a component from before sealing existed simply sees a
small object where it expected a string. If you’re upgrading a machine in place, make sure
everything that reads this document is on a version that knows about sealing before the first
save or secrets seal seals anything in it.
On Windows, a Windows Update servicing restart has been observed to leave the per-user key
temporarily unreadable (Windows reports “the master key is unavailable”) until the machine is
restarted a second time. Maisnie fails closed and honestly when this happens — a gateway call
reports it cannot open the store, and a Desk save of a brand-new secret fails outright — rather
than falling back to writing it as plain text; if that happens right after an update, restart
once more before doing anything else, including deleting a .bak this guide’s own advice
mentions elsewhere.
Sealing what you saved earlier
On a machine that can seal, everything you save from now on is sealed as it is written — and
that includes an ordinary save of something else entirely (a time zone, a feed address): saving
anything through the wizard or Settings seals every plain-text secret already stored, not only
Protect now. The difference is the backup: Protect now and secrets seal copy the
document to a same-day backup, still in plain text, before they touch anything; an ordinary save
does not, because it is not the save’s job to know it is also doing this. Settings says how many
values are in each state, and offers Protect now for the plain-text ones. The same thing from
the command line:
maisnie secrets status # which row above you are on, and how many values are sealed
maisnie secrets seal # seal every value still in plain text
maisnie secrets open # put every sealed value back as plain text
seal and open both copy the document to a same-day backup beside it first
(majordomo.json.<date>.bak). After seal, that backup still holds the plain text — delete it
once you’re satisfied the box runs. open exists for moving a configuration to a machine that
cannot open it: a sealed value can only be opened where its key is (your Windows account, your
macOS keychain, or the secret.key file beside it), so open it, move the document, and seal it
again there. Note that on a machine that can seal, the next save from Settings seals everything
again.
The front door’s passphrase is not on this list: it is stored as a hash, never as a secret, and is checked without the store.
Next
Continue to troubleshooting.