Skip to main content
Commands, package names, and image names on this page come from the open-source project that Mibyan Desktop is built on, and can differ from the Mibyan Desktop installer. For the supported Mibyan install and update path, see Install and update.
Tier 2 platformNix and NixOS are Tier 2 platforms. The flake and NixOS module documented here are maintained on a best-effort basis only. Commits to main may break these packages at any point in time.For a supported setup, use one of the standard installation paths - either Docker or an FHS environment.
Mibyan ships a Nix flake, a NixOS module, and a Home Manager module.
What’s different from the standard installThe curl | bash installer manages Python, Node, and dependencies itself. The Nix flake replaces all of that — every Python dependency is a Nix derivation built by uv2nix, and runtime tools (Node.js, git, ripgrep, ffmpeg) are wrapped into the binary’s PATH. There is no runtime pip, no venv activation, no npm install.For non-NixOS users, this only changes the install step. Everything after (mibyan setup, mibyan gateway install, config editing) works identically to the standard install.For NixOS module users, the entire lifecycle is different: configuration lives in configuration.nix, secrets go through sops-nix/agenix, the service is a systemd unit, and CLI config commands are blocked. You manage mibyan the same way you manage any other NixOS service.

Runtime pins

PM’s tool lock is also a Nix build input. nix/npm-pinned.nix reads the npm pin, and nix/pm-packages.nix exposes matching archives as pm-NAME derivations:
These outputs unpack the pinned archives. They are not the complete Mibyan wrapper or a guarantee that each archive runs without platform integration. The application still uses the uv2nix environment and Nix wrapper. nix/pythonLock.nix reads Python’s major/minor from pm/lock.json. The uv2nix environment, package overrides, plugin packages, and developer shell use that interpreter family. If the pinned nixpkgs lacks that family, evaluation stops instead of selecting a different Python. Native Nix evaluation and builds remain CI gates. Update through Nix. Do not repair a Nix store path with pip.

Prerequisites

  • Nix with flakes enabled — Determinate Nix recommended (enables flakes by default)
  • API keys for the services you want to use (at minimum: an OpenRouter or Anthropic key)

Quick Start (Any Nix User)

No clone needed. Nix fetches, builds, and runs everything:
After nix profile install, mibyan, mibyan-agent, and mibyan-acp are on your PATH. From here, the workflow is identical to the standard installation — mibyan setup walks you through provider selection, mibyan gateway install sets up a launchd (macOS) or systemd user service, and config lives in ~/.mibyan/.
Messaging platforms (Discord, Telegram, Slack)The default package includes ALL libraries mibyan-agent might need. if you want a smaller variant, check the other flake outputs.The default package adds ~700 MB to the closure. If you only need messaging platforms, #messaging adds just ~33 MB.

NixOS Module

The flake exports nixosModules.default — a full NixOS service module that declaratively manages user creation, directories, config generation, secrets, documents, and service lifecycle.
This module needs NixOS. Mibyan is an agent for one person. If you want an agent for one person and not a system service, use the Home Manager module. That module runs on NixOS and on each other system that Home Manager supports.

Add the Flake Input

Minimal Configuration

That’s it. nixos-rebuild switch creates the mibyan user, generates config.yaml, wires up secrets, and starts the gateway — a long-running service that connects the agent to messaging platforms (Telegram, Discord, etc.) and listens for incoming messages.
Secrets are requiredThe environmentFiles line above assumes you have sops-nix or agenix configured. The file should contain at least one LLM provider key (e.g., OPENROUTER_API_KEY=sk-or-...). See Secrets Management for full setup. If you don’t have a secrets manager yet, you can use a plain file as a starting point — just ensure it’s not world-readable:
addToSystemPackagesSetting addToSystemPackages = true does two things: puts the mibyan CLI on your system PATH and sets mibyan_HOME system-wide so the interactive CLI shares state (sessions, skills, cron) with the gateway service. Without it, running mibyan in your shell creates a separate ~/.mibyan/ directory.

Container-aware CLI

When container.enable = true and addToSystemPackages = true, every mibyan command on the host automatically routes into the managed container. This means your interactive CLI session runs inside the same environment as the gateway service — with access to all container-installed packages and tools.
  • The routing is transparent: mibyan chat, mibyan sessions list, mibyan --version, etc. all exec into the container under the hood
  • All CLI flags are forwarded as-is
  • If the container isn’t running, the CLI retries briefly (5s with a spinner for interactive use, 10s silently for scripts) then fails with a clear error — no silent fallback
  • For developers working on the mibyan codebase, set mibyan_DEV=1 to bypass container routing and run the local checkout directly
Set container.hostUsers to create a ~/.mibyan symlink to the service state directory, so the host CLI and the container share sessions, config, and memories:
Users listed in hostUsers are automatically added to the mibyan group for file permission access.Podman users: The NixOS service runs the container as root. Docker users get access via the docker group socket, but Podman’s rootful containers require sudo. Grant passwordless sudo for your container runtime:
The CLI auto-detects when sudo is needed and uses it transparently. Without this, you’ll need to run sudo mibyan chat manually.

Verify It Works

After nixos-rebuild switch, check that the service is running:

Choosing a Deployment Mode

The module supports two modes, controlled by container.enable: To enable container mode, add one line:
Container mode auto-enables virtualisation.docker.enable via mkDefault. If you use Podman instead, set container.backend = "podman" and virtualisation.docker.enable = false.
Cron on a native install needs a lingering service userScheduled cron jobs are launched in a transient systemd-run --user --scope so a gateway restart cannot kill a running job. That needs a systemd user manager for the service uid, which a system service only gets when the uid lingers. With createUser = true the module sets users.users.<user>.linger = true (nixpkgs ≥ 25.05), orders the gateway after linger-users.service, and waits briefly for /run/user/<uid>/bus before starting. If you declare the user yourself (createUser = false), set linger = true on it or run sudo loginctl enable-linger <user> once; otherwise cron degrades to unscoped workers (or fails closed under cron.require_restart_safe_scope: true).

Configuration

Declarative Settings

The settings option accepts an arbitrary attrset that is rendered as config.yaml. It supports deep merging across multiple module definitions (via lib.recursiveUpdate), so you can split config across files:
Both are deep-merged at evaluation time. Nix-declared keys always win over keys in an existing config.yaml on disk, but user-added keys that Nix doesn’t touch are preserved. This means if the agent or a manual edit adds keys like skills.disabled or streaming.enabled, they survive nixos-rebuild switch.
Model namingsettings.model.default uses the model identifier your provider expects. With OpenRouter (the default), these look like "anthropic/claude-sonnet-4" or "google/gemini-3-flash". If you’re using a provider directly (Anthropic, OpenAI), set settings.model.base_url to point at their API and use their native model IDs (e.g., "claude-sonnet-4-20250514"). When no base_url is set, Mibyan defaults to OpenRouter.
Discovering available config keysRun nix build .#configKeys && cat result to see every leaf config key extracted from Python’s DEFAULT_CONFIG. You can paste your existing config.yaml into the settings attrset — the structure maps 1:1.

Escape Hatch: Bring Your Own Config

If you’d rather manage config.yaml entirely outside Nix, use configFile:
This bypasses settings entirely — no merge, no generation. The file is copied as-is to $mibyan_HOME/config.yaml on each activation.

Customization Cheatsheet

Quick reference for the most common things Nix users want to customize:

Secrets Management

Never put API keys in settings or environmentValues in Nix expressions end up in /nix/store, which is world-readable. Always use environmentFiles with a secrets manager.
Both environment (non-secret vars) and environmentFiles (secret files) are merged into $mibyan_HOME/.env at activation time (nixos-rebuild switch). Mibyan reads this file on every startup, so changes take effect with a systemctl restart mibyan-agent — no container recreation needed.

sops-nix

The secrets file contains key-value pairs:

agenix

OAuth / Auth Seeding

For platforms requiring OAuth (e.g., Discord), use authFile to seed credentials on first deploy:
The file is only copied if auth.json doesn’t already exist (unless authFileForceOverwrite = true). Runtime OAuth token refreshes are written to the state directory and preserved across rebuilds.

Documents

Mibyan reads files from two directories. Thus there are two options. Use the option for the directory that the file must go into. documents installs into the working directory of the agent, which is workingDirectory. The agent reads its project context from that workspace:
documents needs an explicit workingDirectoryThe module refuses documents until you set workingDirectory. The default of that option is different on each module. It is your home directory on Home Manager, and ${stateDir}/workspace on NixOS. Thus an unset default puts the files in a directory that you did not select. A directory with the same path as the default is a correct selection, and it satisfies the rule.
mibyanHomeFiles installs into mibyan_HOME. Mibyan reads the identity file and the memory files of the agent from that directory. SOUL.md and memories/ work only from there. A SOUL.md in documents makes a workspace file. Mibyan does not load that file as the identity:
Each value is a string or a path. A key in either option can contain subdirectories, and the module makes the parent directories. Each activation installs the files again. mibyanHomeFiles needs no workingDirectory, because the module owns the mibyan_HOME directory. Most users want mibyanHomeFiles.

MCP Servers

The mcpServers option declaratively configures MCP (Model Context Protocol) servers. Each server uses either stdio (local command) or HTTP (remote URL) transport.

Stdio Transport (Local Servers)

Environment variables in env values are resolved from $mibyan_HOME/.env at runtime. Use environmentFiles to inject secrets — never put tokens directly in Nix config.

HTTP Transport (Remote Servers)

HTTP Transport with OAuth

Set auth = "oauth" for servers using OAuth 2.1. Mibyan implements the full PKCE flow — metadata discovery, dynamic client registration, token exchange, and automatic refresh.
Tokens are stored in $mibyan_HOME/mcp-tokens/<server-name>.json and persist across restarts and rebuilds.

Sampling (Server-Initiated LLM Requests)

Some MCP servers can request LLM completions from the agent:

Managed Mode

When mibyan runs via the NixOS module, the following CLI commands are blocked with a descriptive error pointing you to configuration.nix: This prevents drift between what Nix declares and what’s on disk. Detection uses two signals:
  1. The mibyan_MANAGED environment variable. The service sets it, and the gateway process reads it.
  2. The .managed marker file in mibyan_HOME. The activation script writes it, and an interactive shell reads it. Thus the CLI also blocks a command such as docker exec -it mibyan-agent mibyan config set ....
Both signals hold the name of the system that manages the install. Thus the refusal names the correct rebuild command. The NixOS module gives sudo nixos-rebuild switch. The Home Manager module gives home-manager switch.

Home Manager Module

The flake also exports homeManagerModules.default. Mibyan is an agent for one person. The credentials, the memory, the sessions and the cron jobs all belong to that person. Thus a user service is the correct shape on a personal machine. It runs on each distribution that Home Manager supports, and not only on NixOS. The option set is the same set that the NixOS module uses. It is services.mibyan-agent, with the same settings, environmentFiles, documents, mcpServers, extraPlugins and backend options. Each example above works here without a change. Only the necessary parts are different:

Add the Flake Input

Then import the module into your Home Manager configuration. The configuration can be standalone. It can also be under home-manager.users.<name> in a NixOS or nix-darwin configuration:
home-manager switch makes ~/.mibyan, writes config.yaml, builds .env and starts the gateway as a user service.
Enable linger, or the service stops at logoutCAUTION: Enable linger for your account. Without linger, systemd stops the user manager when your last session ends, and the gateway stops with it. Home Manager cannot set linger, because linger is a property of the account:
macOS has no equivalent option. A launchd agent with RunAtLoad starts at login and continues to run.

Running the Desktop / Dashboard Backend

gateway.enable runs the messaging gateway for Telegram, Discord, Slack and the other platforms. Mibyan Desktop and the web dashboard connect to a different process, which is mibyan serve or mibyan dashboard. backend.mode runs that process with the gateway:
serve runs without a user interface. It gives the /api/ws and /api/pty sockets that Mibyan Desktop connects to, and it does not build the web application. dashboard gives all of that, and also serves the browser admin panel. Both processes use one mibyan_HOME with the gateway. Thus the sessions, the skills, the memory and the cron jobs are the same for all of them. backend.mode works in the same way on the NixOS module, but not in container mode.
Binding to an address other than loopbackThe default address is 127.0.0.1. Each other address starts the authentication gate of the dashboard. The server also refuses each request with a Host header that is different from the address that the server bound to. This is a defence against DNS rebinding. Bind to the name or the address that your client uses.

Verify It Works


Container Architecture

This section is only relevant if you’re using container.enable = true. Skip it for native mode deployments.
When container mode is enabled, mibyan runs inside a persistent Ubuntu container with the Nix-built binary bind-mounted read-only from the host:
The Nix-built binary works inside the Ubuntu container because /nix/store is bind-mounted — it brings its own interpreter and all dependencies, so there’s no reliance on the container’s system libraries. The container entrypoint resolves through a current-package symlink: /data/current-package/bin/mibyan gateway run --replace. On nixos-rebuild switch, only the symlink is updated — the container keeps running.

What Persists Across What

The container is only recreated when its identity hash changes. The hash covers: schema version, image, extraVolumes, extraOptions, and the entrypoint script. Changes to environment variables, settings, documents, or the mibyan package itself do not trigger recreation.
Writable layer lossWhen the identity hash changes (image upgrade, new volumes, new container options), the container is destroyed and recreated from a fresh pull of container.image. Any apt install, pip install, or npm install packages in the writable layer are lost. State in /data and /home/mibyan is preserved (these are bind mounts).If the agent relies on specific packages, consider baking them into a custom image (container.image = "my-registry/mibyan-base:latest") or scripting their installation in the agent’s SOUL.md.

GC Root Protection

The preStart script creates a GC root at ${stateDir}/.gc-root pointing to the current mibyan package. This prevents nix-collect-garbage from removing the running binary. If the GC root somehow breaks, restarting the service recreates it.

Plugins

The NixOS module supports declarative plugin installation — no imperative mibyan plugins install needed.

Directory Plugins (extraPlugins)

For plugins that are just a source tree with plugin.yaml + __init__.py (e.g., mibyan-lcm):
Plugins are symlinked into $mibyan_HOME/plugins/ at activation time. Mibyan discovers them via its normal directory scan. Removing a plugin from the list and running nixos-rebuild switch removes the symlink.

Entry-Point Plugins (extraPythonPackages)

For pip-packaged plugins that register via [project.entry-points."mibyan_agent.plugins"] (e.g., rtk-mibyan):
The package’s site-packages is added to PYTHONPATH in the mibyan wrapper. importlib.metadata discovers the entry point at session start.

Optional Dependency Groups (extraDependencyGroups)

For optional extras declared in mibyan-agent’s pyproject.toml, use extraDependencyGroups to include them in the sealed venv at build time. This is required for any extra not in the default [all] set — on Nix, runtime installation into the read-only store is not possible.
These groups join the core dependency resolution at build time. Conflicting requirements can still fail that resolution. The table lists common groups; pyproject.toml is authoritative for the complete list and platform markers. Memory providers that live in the plugin catalog rather than in the Mibyan tree (e.g. Hindsight) are not extras. Install them like any catalog plugin with mibyan plugins install hindsight, or declaratively via extraPlugins pointing at the plugin’s source tree. Or use the pre-built #messaging or #full flake packages instead of per-extra configuration (see Quick Start). When to use which:

Combining Both

A directory plugin with third-party Python dependencies needs both options:

Using the Overlay

External flakes can override the package directly:

Plugin Configuration

Plugins still need to be enabled in config.yaml. Add them via the declarative settings:
A build-time collision check prevents plugin packages from shadowing core mibyan dependencies. If a plugin provides a package already in the sealed venv, nixos-rebuild fails with a clear error.

Development

Dev Shell

The flake provides an editable Python environment with the lock-derived interpreter and the dev dependency group. mibyan_PYTHON points to its interpreter. It does not install Python dependencies into a repository-local .venv. The shell also provides Node.js and runtime tools. Its npm hook refreshes JS workspaces when their inputs change.
The included .envrc activates the dev shell automatically:

Flake Checks

The flake includes build-time verification that runs in CI and locally:

Options Reference

Core

Configuration

Secrets & Environment

Documents

MCP Servers

Service Behavior

Backend (mibyan serve / mibyan dashboard)

This option runs the process that Mibyan Desktop and the web dashboard connect to, with the gateway. You cannot use it with container.enable.

Home Manager only

programs.mibyan-agent (Home Manager only)

Home Manager separates “install this application for me” from “run this daemon”. services.mibyan-agent keeps the state, the configuration and the daemons. programs.mibyan-agent installs what you use, and reads mibyanHome and the backend address from the services.
The launcher carries mibyan_HOME itself. A desktop menu reads no shell profile, so the value that programs.mibyan-agent.enable exports with home.sessionVariables reaches an interactive shell only. Without the value in the launcher, the application opens ~/.mibyan while the services use mibyanHome, and you see no sessions and no keys. With backend.sessionTokenFile, the application connects to the backend of the service instead of starting one of its own. Both sides read the file at start time, so the token enters no Nix store path. Without the option, each side runs its own backend. services.mibyan-agent.installPackage was removed by this split. A configuration that still sets it gets an error that names the replacement.

Container (NixOS only)


Directory Layout

Native Mode

Home Manager

Container Mode

Same layout, mounted into the container:

Updating

In container mode, the current-package symlink is updated and the agent picks up the new binary on restart. No container recreation, no loss of installed packages.

Troubleshooting

Podman usersAll docker commands below work the same with podman. Substitute accordingly if you set container.backend = "podman".

Service Logs

Container Inspection

Force Container Recreation

If you need to reset the writable layer (fresh Ubuntu):

Verify Secrets Are Loaded

If the agent starts but can’t authenticate with the LLM provider, check that the .env file was merged correctly:

GC Root Verification

Common Issues