> ## Documentation Index
> Fetch the complete documentation index at: https://docs.modelcode.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-hosted Daemon

> Run the ModelDaemon on your own infrastructure to build and test projects that depend on private services

export const ProductName = "The Morph Platform";

export const productName = "the Morph Platform";

{ProductName} uses a **Self-hosted Daemon** (also called the **ModelDaemon**) to execute build, run, and test commands on your own infrastructure. This is necessary when your project depends on private services - such as internal APIs, databases, or package registries - that are not accessible from the public internet.

<Note>
  The Self-hosted Daemon is for projects that need access to private infrastructure. If your project uses publicly accessible dependencies, use the **cloud** build environment instead - {productName} will [auto-discover your lifecycle setup](/setup/build-environment/lifecycle-setup) with no manual configuration needed.
</Note>

## ModelDaemon - Designed for Real Environments

The ModelDaemon runs directly inside your existing application environment - the same machine, the same runtime, the same network context your application already uses. There is no environment recreation, no containerization requirement, and no need to replicate your infrastructure elsewhere.

This is intentional design. Enterprise applications depend on internal services, private registries, and runtime configurations that are difficult or impossible to faithfully reproduce on the public internet. ModelDaemon sidesteps that problem by sitting inside your network, where those services and credentials are already reachable — and it does so without polluting that environment with project-specific runtimes, because the agent and its dependencies live inside per-job isolated workspaces (see [How It Works](#how-it-works) below).

For teams with a stable internal network and a machine that can reach their private dependencies, ModelDaemon is the fastest and most reliable path to getting {productName} running.

## When to Use a Self-hosted Daemon

* Your app already builds and runs - no environment setup required
* Dependencies are already in place - language runtimes, package managers, internal tools
* You don't need portability - the environment is stable and not going away
* Speed matters - you want ModelCode operational with minimal infrastructure changes

V2 is a substantial rework of how the self-hosted daemon executes work. The biggest change is architectural — the AI **morph-agent now runs on your daemon host** instead of in our cloud — and that change is what makes the simpler onboarding possible:

| Aspect                     | V1                                                                                 | V2                                                                                                                                        |
| -------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Where the AI agent runs    | In {productName} cloud, sending shell commands back to the daemon                  | **On the daemon host**, inside per-job isolated workspaces                                                                                |
| Project dependencies       | You installed runtimes, package managers, and project tooling on the host yourself | **The agent discovers them automatically** and the daemon materializes them as isolated [pixi](https://pixi.sh/) environments per project |
| Workspace isolation        | All jobs ran directly in your host shell                                           | Each job runs in its own scratch directory under the daemon's working tree, with its own pinned env versions                              |
| LLM provider keys          | n/a — agent ran in cloud                                                           | The daemon proxies LLM traffic back through {productName}, so no provider API keys live on your host                                      |
| Encryption key pair        | You generated and uploaded a public key                                            | Managed automatically by the daemon — nothing to configure                                                                                |
| Lifecycle commands         | You wrote install / build / run / health-check / test scripts manually             | Auto-discovered by the agent from your codebase, the same way the cloud environment does                                                  |
| Environment variables      | You added each variable in the UI before validation                                | Discovered alongside the lifecycle and editable later in the [Lifecycle Setup](/setup/build-environment/lifecycle-setup) page             |
| End-to-end validation step | A separate "Validate Setup" wizard                                                 | Folded into normal lifecycle discovery and milestone runs                                                                                 |

The net result is that V2 onboarding is just three steps: **name your daemon, run an install command, confirm it's connected.** Everything else is handled by the agent on first use.

## How It Works

The ModelDaemon is a binary (`mcode`) that runs on a machine within your network. Once started, it:

1. **Registers** with {productName} and begins sending periodic heartbeats
2. **Receives jobs** from {productName} over an authenticated outbound connection — lifecycle discovery, spec extraction, milestone execution, code-review chat
3. **Spawns the morph-agent in an isolated workspace** for each job — a fresh per-job scratch directory with its own pinned dependency environment
4. **Streams results, logs, and AI traffic** back to {productName} through the same outbound connection

All communication is initiated by the daemon, so you do not need to open any inbound ports.

The daemon executes each command in a **new shell process** on the host machine. There is no persistent shell session between commands - each step (install, build, run, test) starts a fresh shell invocation.

In V2, the AI **morph-agent** that drives migration work runs as a process on your daemon host. The daemon is responsible for setting up a clean, reproducible environment for it, executing it, and tearing everything down when the job finishes.

### Per-job isolated workspaces

When {productName} dispatches a job, the daemon creates a fresh scratch directory under its working tree (`<workdir>/jobs/<job-id>/`) and starts a `morph-agent` HTTP server inside it. The agent operates on the repository clone in that directory, so jobs cannot see or interfere with each other's files. When the job completes, the workspace and every process spawned inside it are cleaned up.

The shell used for lifecycle scripts depends on your project's target operating system:

| Platform      | Shell      | Lifecycle command syntax                                                        |
| ------------- | ---------- | ------------------------------------------------------------------------------- |
| Linux / macOS | bash       | POSIX — use `&&` to chain commands                                              |
| Windows       | PowerShell | PowerShell — use `;` to chain commands on one line, or write multi-line scripts |

* **Environment variables** - Configure environment variables in the Modelcode UI, not via lifecycle commands.
* **Working directory** - The daemon manages the working directory and keeps it consistent across all commands within a single job. You don't need to `cd` into the project directory unless a command should run in a nested directory.
* **Concatenated commands** - On Linux/macOS, join commands with `&&`. On Windows, join with `;` or use separate lines in a multi-line PowerShell script.
* **Private repositories** - The daemon executes lifecycle commands exactly as defined. If Composer requires authentication for private repositories, those credentials must be present on the daemon host or supplied via encrypted environment variables.

<Note>
  The daemon process itself runs commands with the same permissions as the user that started `mcode`. The agent runs inside its own pixi environment, so it does not inherit your host's runtimes, but it does inherit network access, file-system permissions, and any credentials available to that user (SSH keys, kubeconfig, cloud-CLI tokens, etc.). Run the daemon as a user that has the network and credential access your project needs to reach private resources, and nothing more.
</Note>

## Setting Up the Daemon

<Warning>
  Install the ModelDaemon on the **same machine or environment where your application runs**. This is what gives it access to your internal services, private registries, and runtime dependencies. Installing it elsewhere - even a similar machine - may result in build or test failures that are difficult to diagnose.

  The environment must remain stable for the duration of your project work.
</Warning>

<Note>
  **One daemon per user.** Each team member who actively works with {productName} (approving milestones, running code review chat, using knowledge chat) should have their own ModelDaemon. The daemon handles that user's work and runs one active job at a time. If your daemon is **busy** (already running a job), new requests queue until the current job finishes. If your daemon is **offline**, {productName} cannot dispatch work for you until it reconnects.

  Multiple team members can work on the same project simultaneously — each using their own daemon — without conflicts.
</Note>

### Prerequisites

* A machine on your network that can reach your private services
* A {productName} account with an active project
* Network connectivity from the machine to `*.modelcode.ai` over HTTPS (outbound only — no inbound ports required)
* A supported host OS (see below)

You do **not** need to preinstall language runtimes (Python, Node, Java, Ruby, Go, etc.) or package managers (pip, npm, maven, gem, etc.). The agent declares what each project needs and the daemon installs it inside an isolated pixi environment.

### Supported Operating Systems

* **Linux** (amd64 or arm64) — Ubuntu, Debian, RHEL, Amazon Linux, SUSE, Alpine, and other POSIX-compatible distributions
* **macOS** (Apple Silicon or Intel)
* **Windows** (amd64 or arm64) — Windows 10/11 or Windows Server 2019+

Install the daemon on the same OS family as your application.

### Onboarding Flow

{ProductName} UI walks you through the full setup and generates the exact install command for your platform with your API key already filled in — **follow the on-screen instructions**. The flow boils down to three steps: name the daemon, run the generated install command on the daemon host, and click **Verify & Continue** so the platform confirms the daemon's heartbeat.

<Tip>
  Once installed, you can sanity-check the daemon from the host: `mcode version` confirms the binary is present, and `mcode logs` tails the most recent log lines. Run `mcode install-service` followed by `mcode start` (the UI will remind you of this) so the daemon comes back up automatically after reboots. On Windows, `install-service` requires an elevated (Run as administrator) PowerShell session; if you cannot run as administrator, use `mcode run` in foreground mode instead.
</Tip>

### Windows Setup Notes

{ProductName} onboarding UI provides a PowerShell one-liner that downloads and runs `install.ps1`. After installation:

1. **Open a new PowerShell window** so the updated `PATH` is picked up (the installer adds `mcode` to your user PATH).
2. Run `mcode version` to verify the binary is available.
3. Return to the UI and click **Verify & Continue**.

For a background daemon, run `mcode install-service` and `mcode start` from an **elevated** PowerShell prompt. Service logs appear in **Event Viewer → Windows Logs → Application** (filter by source **ModelDaemon**), or run `mcode logs` for the local log file.

## After Onboarding

Once the daemon is connected, {productName} dispatches a first job that does the following on your daemon host:

1. **Materializes an isolated pixi environment** based on the agent's analysis of your codebase (manifests, lockfiles, framework conventions). On the very first project this also installs a shared pixi base layer; subsequent projects reuse it.
2. **Discovers the lifecycle configuration** — install, build, run, health-check, and test scripts plus any environment variables — by reading the codebase and trying things in the isolated workspace. This is the same auto-discovery used for cloud projects.
3. **Surfaces the result in the UI.** You can review and edit everything on the [Lifecycle Setup](/setup/build-environment/lifecycle-setup) page at any time.

Secret environment variables are encrypted in transit and at rest; the encryption keys are managed by the daemon itself, with no manual key exchange.

## Overriding npm and PyPI registries

When the daemon needs to install its base dependencies, it uses public registries:

| Source                                      | Used for                                                |
| ------------------------------------------- | ------------------------------------------------------- |
| [npm registry](https://registry.npmjs.org/) | Global npm packages installed into the base environment |
| [PyPI](https://pypi.org/)                   | The morph-agent Python wheel                            |

Many enterprise deployments cannot use those defaults. Common reasons include:

* **Air-gapped networks** — the daemon host has no route to the public internet, so packages must come from an internal mirror.
* **Security policy** — all third-party artifacts must be vetted and served from an approved internal registry (Artifactory, Nexus, Azure Artifacts, etc.) rather than pulled directly from the public npm or PyPI indexes.

You can point the daemon at your own npm and PyPI mirrors by adding a `[registries]` table to the daemon's `config.toml` (in the same directory as the `mcode` binary — `~/.local/share/modelcode/` on Linux/macOS, or the equivalent under `%LOCALAPPDATA%` on Windows). Values you set here override {productName}'s defaults for that package manager. The daemon appends `--registry` (npm) or `--index-url` (pip) to the relevant install commands so base installs target your mirror even when the host has different package-manager defaults.

```toml theme={null}
[registries]
npm = "https://artifactory.example.com/artifactory/api/npm/npm-virtual/"
pip = "https://artifactory.example.com/artifactory/api/pypi/simple"
```

<Note>
  Registry overrides apply only to **npm** and **pip** installs during base dependency setup. It will not affect any downloads of your application.
</Note>

If your mirror requires authentication, configure credentials on the daemon host the same way you would for manual `npm` or `pip` usage (for example `.npmrc`, `pip.conf`, or network policies that allow unauthenticated access from the daemon's subnet).

## Migrating from V1

V1 daemons are deprecated, but they have not been switched off:

* **Existing V1 projects keep working.** If a project is already connected to a V1 daemon, that pairing continues to run jobs as before. There is no forced migration and no deadline you need to meet.
* **New projects require V2.** Any project created from now on can only connect to a V2 daemon. If you try to attach a new project to a V1 daemon, the connection will be rejected.

<Warning>
  **V1 and V2 cannot run side by side on the same machine.** Both versions install the `mcode` binary and config to the same location (`~/.local/share/modelcode/` on Linux/macOS, or the equivalent under `%LOCALAPPDATA%` on Windows), so running the V2 installer on a host that already has V1 will **replace the V1 daemon** — your existing V1 projects will lose their daemon and stop running jobs.

  If you still have active V1 projects and want to onboard new V2 projects, install the V2 daemon on a **different machine** (any host with the same network and credential reach to your private services — see [How It Works](#how-it-works)). Once your V1 projects are complete, you can replace V1 with V2 on the original host.
</Warning>

For reference on the V1 onboarding flow (encryption key generation, manual lifecycle commands, environment variables in the UI, validation wizard), see [Self-hosted Daemon V1 (deprecated)](/setup/build-environment/deprecated/self-hosted-daemon-v1).

## Why This Approach Works

Recreating an enterprise application environment from scratch is harder than it looks. Internal services have specific versions, configurations, and network paths that are rarely fully documented. Private registries require credentials that may be tied to specific machines or users. Runtime behavior often depends on system-level configuration that isn't captured in source control.

ModelDaemon avoids this entirely. It runs in your real environment - not a simulated one. The commands it executes are the same commands your developers run. The network paths it uses are the same ones your application uses in production. The credentials and config files are already in place.

You get the correctness of an in-environment setup (real network, real credentials, real registries) without the operational cost of treating the host as part of the project's runtime stack.

## Daemon Status

Your daemon reports its status to {productName} in real time. You can see the current status in the UI:

| Status      | Meaning                                           | What to do                                                                                                        |
| ----------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Online**  | The daemon is connected and ready to accept jobs. | Nothing — you're good to go.                                                                                      |
| **Busy**    | The daemon is running a job for you.              | Wait for the current job to finish, or check the milestone/chat that's using it. New requests will queue.         |
| **Offline** | {ProductName} hasn't received a heartbeat.        | Check that the daemon process is running and has network access. See [Troubleshooting](#daemon-shows-as-offline). |

Since each user has their own daemon, your daemon being busy does not block other team members — they use their own daemons independently.

***

## Environment Stability

ModelDaemon is designed to run on a stable host. The environment where the daemon is installed should:

* Remain accessible for the duration of the project
* Maintain consistent build and runtime behavior across sessions
* Not be decommissioned, re-imaged, or significantly reconfigured mid-project

This is a strength, not a constraint. The daemon's reliability comes directly from the stability of the environment it runs in. Teams that treat their build environment as a managed, long-lived resource get the most consistent results.

## Example: Running ModelDaemon in an Existing Application

A typical self-hosted daemon deployment looks like this:

1. The daemon binary is installed on the same server or VM where the application is already running
2. The daemon is configured with an API key and pointed at the Modelcode platform
3. The agent auto-discovers lifecycle commands from the codebase on first use; the team reviews and edits them on the Lifecycle Setup page
4. No infrastructure changes are made: no new VMs, no containers, no network modifications
5. ModelCode begins executing build and test workflows immediately, using the existing environment

This setup is in production use across applications with complex dependency chains, private Artifactory registries, and internal service dependencies. The common thread: the environment was already working, and ModelDaemon leveraged that directly.

## Troubleshooting

### Daemon Shows as Offline

The daemon appears offline when {productName} has not received a heartbeat within the last few minutes.

**Check if the daemon process is running:**

<Tabs>
  <Tab title="Linux / macOS">
    ```bash theme={null}
    ps aux | grep mcode
    ```
  </Tab>

  <Tab title="Windows">
    ```powershell theme={null}
    Get-Process mcode -ErrorAction SilentlyContinue
    ```
  </Tab>
</Tabs>

If the process is not running, start it again with `mcode run` (foreground) or `mcode start` (installed service).

**Check network connectivity:**

The daemon must be able to reach {productName} server over HTTPS.

<Tabs>
  <Tab title="Linux / macOS">
    ```bash theme={null}
    curl -I https://<YOUR_MORPH_HOST>
    ```
  </Tab>

  <Tab title="Windows">
    ```powershell theme={null}
    Invoke-WebRequest -Uri "https://<YOUR_MORPH_HOST>" -Method Head
    ```
  </Tab>
</Tabs>

If this fails, check your firewall rules, proxy settings, or VPN configuration.

**Check the daemon logs:**

Run `mcode logs` to show the most recent log lines. Logs are also written to the `logs/` directory next to the `mcode` binary. Look for connection errors or authentication failures.

**Common causes:**

* The host rebooted and `mcode install-service` was never run, so there is no service manager keeping the daemon up
* A network change (VPN disconnect, firewall rule update) is blocking outbound HTTPS traffic
* The API key was rotated in the UI but not updated in the daemon's `config.toml`

### Command Not Found

The daemon inherits the `PATH` from the shell that started it. When installed as a system service, it takes a snapshot of the current `PATH` and stores it in its config file. If you install new tools after the daemon is already running as a service, update the stored path with `mcode refresh-path` and restart the daemon.

### API Key Issues

**"Unauthorized" or "Authentication failed":**

* Verify the API key in the daemon's `config.toml` matches the one shown in {productName} UI
* If the key was rotated, update `config.toml` and run `mcode start` again

### Test Generation Failed

Test generation failures occur when {productName} cannot generate tests for your project. This typically relates to the build or run steps.

**Application failed to build:**

Check the install and build command output in the validation logs. Common causes:

* Missing dependencies that are only available on your private network
* Incorrect build command - verify it works when run manually on the same machine
* Environment variables missing - add them in the Modelcode UI

**Application failed to start:**

If the build succeeds but the run command fails:

* Check that the port your application listens on is not already in use
* Verify that required services (databases, APIs) are reachable from the host machine
* Review the run command output for startup errors

**Health check timed out:**

If the application starts but the health check never succeeds:

* Confirm the health check URL and port match your application's configuration
* Check that the application is binding to `0.0.0.0` or `localhost`, not a specific interface
* Increase the health check timeout if your application has a long startup sequence

## FAQ

### Does the ModelDaemon Support Windows?

Yes. The ModelDaemon runs on Windows 10/11 and Windows Server 2019+ (amd64 or arm64). Installation and service setup are done from PowerShell; the UI provides platform-specific commands for each step.

### Do I need to install Python, Node, Java, or other runtimes on the host?

No. The agent declares each project's dependencies in a manifest and the daemon installs them inside an isolated [pixi](https://pixi.sh/) environment under the daemon's data directory. Your host only needs to be able to reach the network locations your application reaches.

### Where does the AI agent actually run?

On your daemon host, inside a per-job pixi-managed workspace. The daemon spawns one `morph-agent` process per job, gives it an isolated working directory and pinned dependencies, and tears everything down when the job finishes. {ProductName} cloud orchestrates the work but does not execute it.

### Do you store my code or my LLM provider keys in the cloud?

Source code is read by the agent on your daemon host and never copied off it as part of normal job execution. The daemon proxies the agent's LLM calls through {productName} using your daemon's API key, so there are no provider API keys (Anthropic, OpenAI, etc.) on the host.

### Can I run multiple daemons on the same machine?

No. The installer always writes to the same data directory for the current user, so running it a second time on the same host replaces whatever daemon is already installed there — whether it's a V1, an older V2, or a V2 registered under a different name. To run more than one daemon at the same time, install each on a separate machine.

### Do I still need to configure lifecycle commands manually?

No. With a V2 daemon, the agent auto-discovers your lifecycle the same way it does for cloud projects. You can review and edit the discovered scripts and environment variables on the [Lifecycle Setup](/setup/build-environment/lifecycle-setup) page at any time.

### Do I still need to upload an encryption public key?

No. V2 daemons manage their encryption keys internally. There is no `mcode encryption generate` step and no public key field in the UI.

### How much disk space does the daemon need?

Plan for \~5 GB in the daemon's data directory for the binary, pixi, and a small project's environments. Larger monorepos or projects with many declared environments (e.g. one per service) will use more — pixi caches conda packages on disk, and the daemon keeps prior environment versions around as long as some workspace still pins them.

## Next Steps

* [Lifecycle Setup](/setup/build-environment/lifecycle-setup) - How lifecycle configurations work for cloud projects
* [Define Modernization Goals](/setup/modernization-goals) - Configure what your migration should achieve
* [Reviewing the Project Spec](/setup/reviewing-project-spec) - Review and approve your migration plan
