Installation
With Docker
On any machine with Docker Compose — a NAS, a home server, a Raspberry Pi 4/5 (images for amd64 and arm64):
git clone https://github.com/SoaOaoS/mav.git && cd mav
cp .env.example .env # optional: port, password, time zone, model
docker compose up -d
Open http://<your machine>:8787, choose a
password, and the welcome screen connects your model and sets up
your first routines. Update with
docker compose pull && docker compose up -d.
Ollama on the same machine is at
http://host.docker.internal:11434/v1.
With the installer
On a Linux machine with systemd — Debian, Ubuntu, Arch (Omarchy, Manjaro…) or Fedora. One command:
curl -fsSL https://raw.githubusercontent.com/SoaOaoS/mav/main/get.sh | sudo bash
or, from a clone:
git clone https://github.com/SoaOaoS/mav.git && cd mav && sudo ./install.sh
The setup asks only what it must:
- Your model provider (arrow keys), your API key — checked on the spot — and the model, picked from the provider's own list. Or Set it up later from the web app.
- Optionally, a contact email for push notifications.
Everything else gets sensible defaults; answer yes to
Advanced settings? to change the system user, the ports
or the quiet hours. Each step shows a spinner; the details go to
/var/log/mav-install.log. At the end, open the
address it prints.
Installer options
sudo ./install.sh --update # update, keep the settings
sudo ./install.sh --dry-run # show what would happen, change nothing
sudo ./install.sh --uninstall # remove the services (data is kept)
sudo ./install.sh --yes # unattended, see below
Unattended: MAV_PROVIDER (anthropic,
openai, ollama,
ollama-cloud-api, openrouter),
MAV_PROVIDER_APIKEY, MAV_OPENCODE_MODEL,
optionally MAV_PROVIDER_BASEURL and
MAV_VAPID_EMAIL. Without a provider, the model is
left to set up in the web app.
Updating
When a new version is out, the web app shows Update to vX.Y.Z in the sidebar (and in Settings → General): it shows what's new and installs it for you. From a terminal:
mav update # install the latest release, keep everything
mav update --check # only tell whether one is available
mav update --local # reinstall the local copy (offline)
mav status # what's running, the address, the model
mav doctor # check everything, with a hint for each problem
mav restart [engine|worker|web]
mav logs [engine|worker|web|install] [-n 50]
mav backup # memory, routines, settings → /var/backups/mav/
mav password # set or reset the web app password
mav reconfigure # Update / Change the model / Reconfigure / Uninstall
Versions follow
semantic versioning: every change merged into main is released
automatically, as a major (breaking change), minor (new feature)
or patch version, depending on its commit messages. What changed
in each version is on the
releases page
(latest: see releases).
mav update did
not download anything, so run the one-line installer once and
choose Update. It replaces the
mav-bot service with mav-worker and
keeps your memory (now in Postgres — the old
memory.json is imported automatically), your
routines, your model and your settings. Your existing agent files
are left untouched; the new everyday helpers are added next to
them.
Backup & restore
Settings → General → Backup → Download saves everything in
one .tar.gz: memory (every table), routines, chats,
helpers, connections, settings, API keys and the password. It works
the same with Docker and with the installer, and is how you move
Mav to another machine.
Restore… asks for your password again, checks the file
(only Mav's own folders, no unsafe path), keeps a copy of the
current state on the server (bot/backups/, the last
three), replaces the memory in one transaction, then the files, and
restarts the engine.
mav backup still works on an
installer install.
Moving to Docker
Installed with the installer and want Docker instead? One script moves everything: chats, memory, routines, helpers, connections, your model and API keys, your password and the notification keys. Run it on the same machine, from a clone:
git clone https://github.com/SoaOaoS/mav.git && cd mav
sudo ./scripts/migrate-to-docker.sh # asks before changing anything
sudo ./scripts/migrate-to-docker.sh --dry-run # only show what it would move
What it does
-
Saves a backup of the memory database to
/var/backups/mav/pre-docker-….sql.gz. -
Copies the engine settings and chat history
(
~/.config/opencode,~/.local/share/opencode), the files Mav shared with you, your routines and keys into the Docker data volume. Paths are rewritten for the container, and services on the machine itself (localhost, like Ollama) becomehost.docker.internal. -
Writes
.envso the stack reuses the same database (themav_pgdatavolume, with its password). Your memory is not copied: it is the same one. - Stops and disables the systemd services, then starts the Docker stack on the same port. Sign in with your usual password.
/etc/mav*.env stay in place, and the script prints the
three commands to go back. Once you are happy, you can remove the
old install folder by hand. Do not run
mav uninstall after the move: use
docker compose down instead.
Push notifications
The installer serves the app over HTTPS itself; the Docker stack
serves plain HTTP on port 8787. Phones only allow push
notifications over HTTPS, so put a reverse proxy in front (Caddy,
Traefik, Nginx Proxy Manager or tailscale serve), then
allow notifications again on each device.
By hand
If your database user, name or volume is not the default, the script stops and points here. The pieces to move are the same:
| Installer install | Docker volume mav-data |
|---|---|
~/.config/opencode | /data/home/.config/opencode |
~/.local/share/opencode | /data/home/.local/share/opencode |
~/workspace/mav-files | /data/home/workspace/mav-files |
~/bot (data files, not the .py) | /data/bot |
/etc/mav-server.env | /data/config/server.env |
OPENCODE_MODEL from /etc/mav.env | /data/config/mav.env |
| The database | mav backup, then restore into the postgres service |
Requirements
- Docker: any 64-bit Linux machine, NAS or Raspberry Pi 4/5 with Docker Compose v2.
-
Installer: Debian 12+, Ubuntu 22.04+, Arch-based systems (Omarchy, Manjaro,
EndeavourOS…) or Fedora, with systemd and root access. The
installer uses
apt,pacmanordnfaccordingly. - About 400 MB of RAM for Mav itself (measured at rest: engine 258 MB, Postgres 43 MB, web app 35 MB, worker 29 MB) and 1.2 GB of disk for the images — more if you run models locally with Ollama.
- An API key from a model provider, or an Ollama server.
The installer brings the rest: Python, Docker (for Postgres) and the opencode agent engine.
Raspberry Pi & ARM
Mav runs on 64-bit ARM: every change is tested on a native arm64
machine (the whole Docker stack, and the installer), and the
release images are published for arm64. A
Raspberry Pi 4 or 5 runs it comfortably: at rest
the four containers use about 365 MB of memory together.
- Use a 64-bit system (Raspberry Pi OS Lite 64-bit, Ubuntu Server arm64).
- Put the data on an SSD rather than the SD card: Postgres writes often.
- Install with Docker (above);
docker compose pullfetches the arm64 images.
Which model?
| Machine | Recommended |
|---|---|
| Pi 4 / Pi 5, 4 GB | A cloud model (Anthropic, OpenAI, OpenRouter…). The Pi only runs Mav; answers are as fast as on a big server. |
| Pi 5, 8–16 GB | A cloud model for chats, or a small local model with tool calling through Ollama (a 1.5B–3B model such as Qwen 2.5 or Llama 3.2) for privacy, accepting slow answers: on a CPU, the first word can take tens of seconds. Use it as the Model for background work (titles, memory) to keep that work at home. |
| Pi + a PC with a GPU on the network | Run Ollama on the PC and point Mav at it
(http://<pc>:11434/v1): local and quick. |
Each helper only receives the tools it uses (see Speed), which keeps prompts small: that matters most on slow hardware. The Speed card shows what your machine actually does.
On your phone
Mav is a web app you can install like a native one, with push notifications. Browsers only allow that over HTTPS, so Mav serves itself over HTTPS with a private certificate:
-
On the phone, open
https://<your-mav>/certs/ca.cerand trust the certificate. -
Open
https://<your-mav>/, then Add to Home Screen. - Turn on notifications (the bell, or Settings → General).
On your first visit Mav asks you to choose a password; every
device then signs in once (for 30 days). Forgot it? Run
sudo mav password on the machine. To reach Mav away
from home, prefer a VPN such as Tailscale or WireGuard.
Family accounts
One Mav can serve a whole household. In Settings → General → Family, the owner adds a person with a name and a password. From then on, the sign-in screen asks for a name too; the owner can leave it empty.
- Each person has their own Mav: chats, memory, routines, watched pages, notifications and phones. Nobody sees anyone else's, and Mav's memory of one person never reaches another.
- The owner keeps the keys: the model, connections, helpers, email, calendars, channels, usage, backup and updates. Members don't see those settings at all.
- Removing a person signs them out and erases their data. Files the assistant creates live in one shared folder.
Chat
The chat is the main screen. A new chat shows a greeting, the message box, four everyday suggestions and For you — your latest routine reports and alerts. Chats get a short title on their own after the first answer and are grouped by day; click a title to rename it, or pin, summarise, download or delete a chat from its header. You can attach files, dictate, and have answers read aloud.
Commands
/remember <fact> |
Save something about you to memory |
/routine <what, when> |
Create a routine (pre-filled from your words) |
/watch <url or topic> |
Keep an eye on a page, a price or a topic |
/helper <name> |
Switch who you talk to |
/new · /rename ·
/summary · /export ·
/help
|
Chat actions |
Shortcuts: ⌘K search and commands,
Alt N new chat, / focus the message box,
Esc stop an answer.
Helpers
You talk to the Assistant by default. It answers directly, and calls in a specialist when that helps:
| Researcher | looks things up on the web, checks facts, compares options — with sources |
| Writer | emails, messages, posts and letters in the right tone |
| Planner | days, trips, projects, to-do lists |
| Money | budgets, purchases, subscriptions, comparing offers |
You can also talk to one directly: pick it on the message box or
in the chat header. Who answers is always shown — on the message
box, in the header and on each answer — and each chat remembers
its helper. Create your own (a coach, a tutor…) in Settings →
Helpers. Each helper is a Markdown file in
~/.config/opencode/agent/.
Memory
Mav keeps two kinds of memory, stored in Postgres on your machine:
-
About you — facts like "vegetarian, lives in
Lyon". Always recalled when a chat starts. Add them on the
Memory page or with
/remember. - From past chats — every question and answer, recalled when relevant to a new chat (full-text search with a gentle preference for recent ones).
Answers that used memory say so ("Used 3 things from memory"). You can delete any item, or clear chat memory entirely, from the Memory page.
Daily briefing
One short message to start the day: the weather where you live,
what happened since yesterday (alerts and routine reports), what
needs you (drafts waiting for approval) and something from your
interests. Turn it on in Settings → General → Daily
briefing and pick the time; it arrives as a notification and
in its own chat, where you can ask follow-up questions. Anytime:
Brief me on the new-chat screen, or /brief.
Usage & budget
Settings → Usage shows what Mav cost this month (as your provider bills it), how many answers and tokens, a 30-day chart, and the split between chats, routines and background work.
- Monthly budget — Warn notifies you at 80 % and 100 %; Stop also pauses new answers once it is reached, until you raise it or the month ends.
- Model for background work — titles, memory and summaries run on this model (a small "mini" or "haiku" model is plenty), so they cost a fraction of your main one.
A local model (Ollama) costs nothing here.
Speed
Every answer shows how long it took next to its buttons: time to the first word, the whole answer, the number of steps and how much prompt the model read (and how much of it came from the provider's cache). Settings → Usage → Speed gives the typical answer over 30 days (medians, so one slow web search does not hide that most answers got quicker).
To keep answers quick, each helper only receives the tools it uses:
the Writer, Planner and Money helpers get a closed list, and no
helper can run shell commands. The prompt each turn sends is
47–69 % smaller than before (measured with
dashboard/tools/fake_openai.py).
Calendar
In Settings → General → Calendars, connect a CalDAV account (Nextcloud, iCloud, Fastmail, Radicale…; an app password is best) or paste a calendar's private iCal link (Google Calendar: Settings → your calendar → Secret address in iCal format; Outlook: Publish → ICS). Mav only reads it. It is tested when you add it, and the next event is shown.
- The briefing lists today's events, in order, and points out a tight gap or an early start.
- Before a meeting is a new kind of routine: 15 minutes before each event, or only events whose title or place mentions a word. Mav gets the event's details with the routine's prompt (“What do I need for it? How long is the drive?”). Each one runs once, even across a restart.
Recurring events, moved and cancelled occurrences and time zones
are handled. Passwords and iCal links are stored on your server
only (calendar.json) and never shown again.
Routines
Things Mav does on its own: "every morning at 7, tell me if I need an umbrella", "every Sunday, plan 5 dinners and a shopping list". Four ways to create one:
- Templates — the Routines page has ready-made ones (morning brief, evening recap, weekly review, monthly review, weekly planning…). Two clicks and it's yours.
- the form on the Routines page — what to do, when, and which helper;
/routinein a chat;- just say it — when a message sounds recurring, Mav offers Make this a routine? with a Create routine button. Recurrence is understood in English, French and Spanish ("every monday", "tous les matins", "cada día").
When creating one, the schedule can be:
| Every day | at a time you pick |
| Some days | the weekdays you choose, at a time |
| Once a month | fixed day(s) of the month, or the last day |
| Every few hours | an interval, in hours |
| On an event | when something arrives from another service (see Proactivity) |
Each routine runs in its own chat (Routine · name), with what Mav remembers about you, so you can open it and follow up. When there is something to report, you get a push notification and an entry in For you; an answer like "nothing to report" stays silent. Failed runs are retried once. You can snooze a routine without deleting it; it keeps its schedule and resumes on its own.
Proactivity
More proactivity is only useful if Mav can rank what it finds — otherwise it just sends more. Three rules keep it in hand.
Priority — what deserves a push
Every alert is classified, locally and instantly, as one of:
| Critical | money, security, a missed deadline — always interrupts you |
| Important | a decision, an appointment, something to answer today |
| Useful | a report, a change worth knowing |
| FYI | background — kept in history, not pushed |
How far Mav can go
Settings → General → How proactive is Mav?:
| Quiet | only what's critical reaches you |
| Normal | critical and important (the default) |
| Chatty | everything — even the smallest alert |
DIGEST_HOUR, 19:00 by default). So "more proactive"
never means "more noise".
Conditions — "only if…"
A routine can carry a condition. When it isn't met, the run is
skipped before Mav is even asked — nothing is spent, and
nothing is sent. Conditions are: text contains / matches, a number
comparison, a weekday, or "not empty". A routine can also simply
be written so it replies RAS when there's nothing to
say — that stays silent too.
source does not block a run:
the live data sources (weather, a price, an inbox marker) are not
wired into the worker yet. The evaluator is ready; for now the
"only if" is driven by what a routine can put in its own message.
Events — waking Mav from outside
A routine can be triggered by an incoming event instead of a clock. Post to the webhook:
curl -X POST https://<your-mav>/api/hooks/event \
-H 'Content-Type: application/json' \
-d '{"kind":"github","token":"<your token>","payload":{"action":"opened"}}'
Any routine with a matching on_event (a kind, and
optionally a contains substring) runs on the worker's
next tick. Requests from outside need the webhook's token:
create it in Settings → General → Webhook and send it
as "token" in the body. Event kinds offered in the form:
github, calendar, form,
payment, iot — or
custom for anything else.
Drafts & background actions
Two ways Mav does more than talk:
- Drafts — when Mav spots something that needs an action (a mail to answer, a follow-up), it writes it into the Drafts tab of the Routines page. You review, copy, send to your phone, or discard. Mav never sends anything by itself.
- Background actions — ask for something long and it runs on its own (its own chat, Action · name) while your conversation stays free. A notification brings the result when it's done.
Keep an eye on
| A page | alerts when the readable text of the page changes |
| A price | reads the price on a product page; alerts on any change, or only below your target |
| News | new headlines about a topic (Google News) |
| A feed | new items in any RSS or Atom feed: a blog, a podcast, a forum |
| Releases |
each new release of a GitHub repository
(owner/repo or its address)
|
News, feeds and releases take an optional Only if it mentions…: a few words, separated by commas. Mav then only tells you about items whose title or summary contains one of them. Several new items at once arrive as one grouped alert, never one per item.
Items are checked about every 15 minutes
(WATCH_INTERVAL). The first check only records the
current state; after that, you are notified only on a change. News
language and country follow NEWS_LANG /
NEWS_COUNTRY in /etc/mav.env (default
en-US / US).
Notifications
Web Push, on any device where you turned them on — even when Mav
is closed. Tapping a routine report opens its chat; tapping an
alert opens a chat that explains it. Quiet hours
(NOTIFY_QUIET, default 23–7) hold pushes back; the
item still lands in For you. The same alert is never sent
twice within 6 hours.
What actually gets pushed depends on your
proactivity level (see
Proactivity): below your bar, an alert
is collected into the daily recap instead. The recap runs at
DIGEST_HOUR (19:00; set 0 to disable).
ntfy, Gotify, Discord and Slack
In Settings → General → Other channels, add as many as you
like: an ntfy topic (on ntfy.sh or your own
server, with an optional token), a Gotify
application token, or a Discord or
Slack webhook. Each one is tested as soon as you
add it, and has its own Test button and on/off switch.
Tokens and webhooks are stored on your server only
(channels.json, readable by Mav alone) and never shown
again in full.
Alerts go to Web Push and every enabled channel, with the same rules: quiet hours, proactivity level, no duplicates. A routine can pick its own: open it, then Send reports to…. Fill in Address of this Mav so the notification links back to the right chat.
Model
Settings → Model: pick a provider, paste a key, press Check & list models to verify it and fill the model list, then Save. Mav restarts for a few seconds and uses the new model.
| Anthropic | Claude models · API key |
| OpenAI | GPT models · API key |
| Ollama | models on your own machine or network · no key |
| Ollama Cloud | hosted open models · Ollama API key |
| OpenRouter | many models behind one key |
| Custom | any OpenAI-compatible API (LM Studio, vLLM, Groq, Mistral…) |
Keys are stored in /etc/mav-server.env (readable by
root only) and referenced from the engine config — never sent to
the browser. The same logic is used by the installer
(dashboard/server/mav_provider.py).
Custom instructions
Settings → Custom instructions: what Mav should know and how it
should answer — language, tone, your situation. They apply to
every chat and every helper (stored as
~/.config/opencode/AGENTS.md).
Connections
Settings → Connections lets Mav use other apps and services through the MCP standard. Pick one from the catalog — read web pages, a web browser, Brave search, time zones, your files, Notion, Home Assistant, GitHub — fill in its key, and press Restart assistant in the bar that appears. Or add any MCP server with Custom…. Tokens are masked once saved; the raw configuration is in Settings → General → Advanced. Some connections need Node.js or uv on the server — Mav tells you which, and how to install them.
// online service
{ "type": "remote", "url": "https://…/mcp", "enabled": true }
// program on the server — command is an array, settings go in "environment"
{ "type": "local", "command": ["npx", "-y", "pkg"], "enabled": true }
How it works
Browser / phone (PWA)
│
▼
mav-dashboard (web app + API) ──▶ mav-server (opencode engine, 127.0.0.1:4096)
│ ▲
▼ │
Postgres (Docker, 127.0.0.1) ◀── mav-worker (routines, keep an eye on, push)
mav-dashboard |
the web app and its API (HTTP + HTTPS) |
mav-server |
the opencode agent engine, local only |
mav-worker |
runs routines and checks, sends notifications |
Chats are engine sessions; memory, alerts, notification history,
incoming events, drafts and background actions live in Postgres;
routines in ~/bot/jobs.json. Everything the worker
needs degrades gracefully: without Postgres, events, drafts and
actions fall back to JSON files in ~/bot/.
Configuration files
/etc/mav-dashboard.env |
web app: address, ports, HTTPS, database, default helper |
/etc/mav.env |
worker: model, quiet hours (NOTIFY_QUIET), watch
interval, digest hour (DIGEST_HOUR), database
|
/etc/mav-server.env |
engine: API keys (written by Settings → Model) |
~/.config/opencode/ |
engine config, custom instructions, helpers |
Edit, then mav restart.
Web app API
The web app talks to a small JSON API on the same address. It
needs the session cookie set by /api/auth/login
(except /api/auth/*, /api/health and
webhooks). Highlights:
GET /api/stream?prompt=&session=&agent=
|
answer as Server-Sent Events: start,
delta, done, error
|
GET /api/sessions · /api/session?id=
|
chats, and one chat's messages |
GET /api/jobs ·
POST /api/job/save|delete|toggle|run|snooze
|
routines (schedules, events, conditions) |
GET /api/job-templates ·
POST /api/job/template ·
POST /api/routine/detect
|
ready-made routines, and turning a sentence into a draft |
GET|POST /api/proactivity
|
the quiet / normal / chatty level and what's pending |
GET /api/drafts ·
POST /api/drafts/add|status|delete|notify
|
drafts Mav prepared for you |
GET /api/actions ·
POST /api/actions/start
|
background actions |
POST /api/hooks/event ·
GET /api/events
|
incoming events that wake routines up |
GET /api/watch ·
POST /api/watch/add|remove
|
keep an eye on (web, price,
news)
|
GET /api/memory ·
POST
/api/memory/fact/add|fact/delete|exchange/delete|forget
|
memory |
GET /api/notifications |
the For you inbox |
GET|POST /api/config/provider ·
POST /api/config/provider/test
|
model provider |
The full list is in dashboard/README.md.
Troubleshooting
"Assistant offline"
mav status
journalctl -u mav-server -n 50
Most often the model key or address is wrong: fix it in Settings → Model (Check & list models tells you what is wrong).
status=203/EXEC
The engine binary is missing. Install it and restart:
curl -fsSL https://opencode.ai/install | bash
sudo systemctl restart mav-server
Installed elsewhere? Set MAV_OPENCODE_BIN in
/etc/mav-server.env.
The installer stopped without a message
Version 1.0.0 stopped silently right after the advanced settings
on Arch-based systems. Fixed in 1.0.1 — simply run the one-line
installer again. Since 1.0.1, any unexpected error shows the line
that failed; send it with the end of
/var/log/mav-install.log when reporting a problem.
Check everything at once
mav doctor
It checks the services, the assistant, the model, the web app, the database, disk space, file permissions, the HTTPS certificate and available updates, and says what to do for each problem.
A connection broke the assistant
Settings validates connections before saving, but a server that fails at start can still block the engine. Turn it off in Settings → Connections, then Apply.
Routines or alerts don't arrive
journalctl -u mav-worker -n 50
Check that notifications are on for this device (Settings → General → Send a test) and that you are not in quiet hours.
The web app doesn't load
journalctl -u mav-dashboard -n 50
curl -k https://<your-mav>/api/status
Start over cleanly
mav uninstall
docker volume ls | grep pgdata # usually mav_mav_pgdata
docker volume rm mav_mav_pgdata # also erases the memory