Maisnie

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 tzdata in 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 --lan flag 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 changing compose.yml’s host-side port line from 127.0.0.1:${MAISNIE_PORT:-8080}:... to 0.0.0.0:... (or your LAN address) and re-running docker compose up -d — never before you’ve claimed the box.

Next

Continue to first boot to bring the box up for the first time.