User guide · Software preview
Install
Requirements
- Python 3.9 or later, on Linux, macOS, or Windows. On Windows, prefer the installer from
python.org over the Microsoft Store version — the Store build silently redirects
%LOCALAPPDATA%(where Maisnie’s default data directory lives) into its own sandboxed package folder, so files Maisnie writes there won’t be where you expect to find them. - On Windows, also run
pip install tzdatain the same virtual environment right after installing Maisnie. python.org’s own Windows build of Python ships no time zone database at all, and without one, calendar events can only be approximated from the feed’s own fixed offset (no daylight-saving adjustment) instead of resolved properly — see troubleshooting for the full story. Linux and macOS normally already have one from the OS. - No other runtime dependency. Maisnie installs nothing beyond the Python standard library.
- Well under 5 MB for Maisnie’s own source; a fresh virtual environment with it installed is roughly 30 MB all in. Your notes, mail, and calendar data will need more over time, but plain text stays small for a long while.
Install with pip
You’ll be given a wheel file (a filename ending in .whl) for the preview build. Its name starts
with maisnie. Install it into a fresh
virtual environment so it doesn’t collide with anything else on your system.
Linux/macOS:
python3 -m venv ~/maisnie-env
source ~/maisnie-env/bin/activate
pip install /path/to/maisnie-<version>-py3-none-any.whl
Windows (PowerShell):
py -m venv $HOME\maisnie-env
& $HOME\maisnie-env\Scripts\Activate.ps1
pip install C:\Users\<you>\path\to\maisnie-<version>-py3-none-any.whl
If PowerShell refuses to run Activate.ps1 (“running scripts is disabled on this system”), that’s
the default execution policy blocking it — run
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once, then try activating again.
Either OS: you have to reactivate the virtual environment every time you open a new terminal
(run the source .../activate or Activate.ps1 line again) — it isn’t a one-time step, and
maisnie: command not found after closing and reopening your terminal usually just means this.
Confirm it installed:
maisnie --help
If that prints a list of subcommands (init, up, status, doctor, unclaim, unlock, run,
pause, resume), you have a working install. See troubleshooting if it
doesn’t. The same install also provides the command under its older name, majordomo, for
anything already scripted against it; this guide uses maisnie throughout.
Where things go
Maisnie keeps everything it writes under one data directory. You don’t have to choose one —
if you don’t, it picks a sensible per-OS default the first time you run maisnie init:
| OS | Default data directory |
|---|---|
| Linux | $XDG_DATA_HOME/maisnie, or ~/.local/share/maisnie if that variable isn’t set |
| macOS | ~/Library/Application Support/Maisnie |
| Windows | %LOCALAPPDATA%\Maisnie |
You can override this for any command with --data-dir <path> — placed before the subcommand,
e.g. maisnie --data-dir <path> doctor (maisnie doctor --data-dir <path>, with the flag
after, is refused as an unrecognized argument) — or by setting the environment variable
MAISNIE_HOME. Nothing is ever written to the default location unless you actually run
maisnie init or maisnie up — checking what the default would be never creates it.
If you set Maisnie up before it had this name, nothing moves: the older variable
MAJORDOMO_HOME is still honoured when MAISNIE_HOME isn’t set, and when the default folder
above doesn’t exist yet but the same place under the older name (majordomo) holds your
configuration document, Maisnie keeps using that folder. Inside the data directory, the
configuration document keeps its file name, config/majordomo.json.
Install with Docker
A Dockerfile and a compose.yml ship at the repository root — this needs the source checkout
(or whatever archive of it you were given), not just the wheel. If a wheel file is all you have,
use Install with pip instead. You don’t need Python, pip, or a virtual
environment for the Docker path itself — only Docker.
With Docker Compose (the simpler path — it builds the image for you):
docker compose up -d
docker compose up -d
(Compose’s own syntax is the same on every OS; only the environment-variable and path forms below differ between a POSIX shell and PowerShell.)
This builds an image (tagged maisnie:local), starts one container, writes a config document
inside it on first boot if one isn’t there yet (the same thing maisnie init does for a plain
install, done for you), and publishes the gatekeeper on 127.0.0.1:8080 on your own machine —
loopback, the same default a plain install starts with; nothing on your network can reach it
unless you deliberately change compose.yml’s own port line. Your data lives in ./data next to
compose.yml — a mounted folder, not something sealed inside the container — so it survives
docker compose down and a later rebuild.
Skip ahead to claiming the box — the maisnie init/doctor/
up commands in first boot are for a plain install; Compose already did their
job for you. To use a different port, set MAISNIE_PORT before bringing it up:
MAISNIE_PORT=18080 docker compose up -d
$env:MAISNIE_PORT=18080; docker compose up -d
One variable moves both the container’s own bind and the host-side port compose.yml publishes
together, so the two can’t drift apart.
Set your own time zone, or Today and every time Maisnie shows will read in UTC instead of
yours. Every time and date on the page follows the clock of the machine Maisnie is actually
running on — for a plain install, that’s your own computer’s clock already, nothing to set. A
container’s own clock defaults to UTC regardless of your host’s time zone, so compose.yml passes
through a TZ environment variable (an IANA name, such as Europe/Lisbon or
America/Los_Angeles), defaulting to UTC when you don’t set one:
TZ=America/Los_Angeles docker compose up -d
$env:TZ="America/Los_Angeles"; docker compose up -d
(or set it once in an .env file beside compose.yml, the same way you might set MAISNIE_PORT
there). This is a one-time setting the container reads at startup, not something the setup wizard
asks for.
docker compose logs -f follows the box’s own log lines (the same ones a plain maisnie up
prints to its terminal); docker compose down stops and removes the container without touching
./data. A log line warning the box is UNCLAIMED prints on every start, even after you’ve
claimed it — that check is currently a known bug reading a config key nothing ever writes, not a
real signal; go by the claim page itself (or status, below) instead.
If you ran this compose.yml before the product was called Maisnie, its service had the older
name, and Compose leaves that old container running beside the new one, still holding the port.
Bring the box up once with docker compose up -d --remove-orphans (or run docker compose down
before you update the checkout); ./data is untouched either way. The image sets
MAISNIE_HOME=/data itself, so the older MAJORDOMO_HOME variable has no effect in a container:
to move the data directory there, set MAISNIE_HOME.
With plain Docker, build and run the image yourself instead of going through Compose. The
image’s own user is a fixed non-root account, and a Linux Docker Engine creates a bind-mounted
host folder that doesn’t exist yet as root, which that non-root user then can’t write into at
all — docker run (unlike compose.yml, which starts as root for exactly this reason and drops
privilege itself) has nothing to fix that ownership for you, so maisnie init fails outright
unless you either create the folder yourself with the right owner first, or add --user root so
the image’s own entrypoint fixes it:
docker build -t maisnie:local .
mkdir -p data && sudo chown 1000:1000 data
docker run -d --name maisnie -p 127.0.0.1:8080:8080 \
--add-host=host.docker.internal:host-gateway \
-v "$(pwd)/data:/data" maisnie:local
or, letting the entrypoint do it instead of chown yourself:
docker run -d --name maisnie --user root -p 127.0.0.1:8080:8080 \
--add-host=host.docker.internal:host-gateway \
-v "$(pwd)/data:/data" maisnie:local
docker build -t maisnie:local .
docker run -d --name maisnie --user root -p 127.0.0.1:8080:8080 `
--add-host=host.docker.internal:host-gateway `
-v "${PWD}\data:/data" maisnie:local
(--add-host matters on Linux; Docker Desktop on Windows and macOS already resolves
host.docker.internal without it — see below.)
Either way, if you’re pointing a connection at a model server running on your own machine (Ollama,
llama.cpp, LM Studio — see choose a model), reach it from inside
the container as http://host.docker.internal:<port>/v1, never 127.0.0.1:<port> — inside a
container, 127.0.0.1 means the container itself, not the machine it’s running on.
Running commands in the container
Anywhere this guide says to run a maisnie command “from a terminal” (run, pause, resume,
unlock, and — see below — unclaim), the host running Docker has no maisnie on its own
PATH; run it inside the container instead:
docker compose exec --user majordomo maisnie maisnie pause
(the repeated maisnie: the compose service name, then the actual CLI command. The image’s
own default user is root under compose.yml, so --user majordomo — the image’s fixed
non-root account, which keeps Maisnie’s internal codename — avoids leaving root-owned files
behind for a command that writes anything). unclaim additionally needs the instance
stopped first (it refuses while the lock is held), so run it as its own one-off container instead
of exec-ing into the running one:
docker compose stop
docker compose run --rm maisnie unclaim --yes
docker compose up -d
A container’s port publish can reach past the claim gate that protects a plain install.
Internally, the box always sets its own bind to loopback and refuses --lan until you’ve
claimed it — but Docker’s port publishing connects to the container’s own network address, which
is not the same as loopback from Docker’s point of view, so the container is set up to answer on
every interface inside itself the moment it starts, claimed or not. compose.yml’s default
(127.0.0.1:8080 on the host side) keeps that contained to your own machine; publishing wider
than that (-p 8080:8080 instead of -p 127.0.0.1:8080:8080, or -P) exposes an unclaimed box to
whoever reaches it first. Keep the default until you’ve claimed the box.
Docker equivalents for the rest of this guide
The rest of this guide (starting with first boot) is written for a plain install; two things map differently under Docker:
- A CLI command (
run,pause,resume,unlock,unclaim): see running commands in the container, above. --lan, to reach the box from another device: there is no--lanflag to pass under Docker — the container already answers on every interface inside itself (see just above). Reaching it from elsewhere on your network, once claimed, means changingcompose.yml’s host-side port line from127.0.0.1:${MAISNIE_PORT:-8080}:...to0.0.0.0:...(or your LAN address) and re-runningdocker compose up -d— never before you’ve claimed the box.
Next
Continue to first boot to bring the box up for the first time.