Repository navigation
Run the agent as its own OS user on managed machines #162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
9506531
Run the agent as its own OS user on managed machines
molefrog 4cb854e
Show the web socket instead of the port when moi listens on one
molefrog 7fd36f6
Send every moi env command through the running server
molefrog d0a7fc7
Create the title command's folder in moi, open to every user
molefrog b19d8de
Check folder access in the CLI instead of the caller's OS user
molefrog 0df2e8d
Replace the managed-machine doc with docs/config.md
molefrog File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # Config | ||
|
|
||
| `config.json` describes how moi is deployed. A normal local install has no file and needs none. moi reads the file once at startup, so restart moi after changing it, and no API can write it. | ||
|
|
||
| It lives in moi's data folder: `MOI_DATA_DIR` if set, otherwise `~/Library/Application Support/moi` on macOS and `~/.local/share/moi` on Linux. A missing file is fine. A broken file or a wrong value prints a warning and falls back to the default, so a bad config never stops moi. | ||
|
|
||
| Some options can also be set with environment variables, which win over the file. | ||
|
|
||
| ```json | ||
| { | ||
| "cloudDemo": false, | ||
| "demoInstallUrl": "https://moi.computer", | ||
| "cloudflareAccess": { "teamDomain": "acme", "audience": ["<AUD tag>"] }, | ||
| "workspacesDir": "/data/workspaces", | ||
| "agentLauncher": "/opt/moi/bin/agent-run", | ||
| "unixSocket": "/run/moi/web.sock" | ||
| } | ||
| ``` | ||
|
|
||
| ## Demo | ||
|
|
||
| | Option | Env | Default | | ||
| | ---------------- | ---------------------- | ---------------------- | | ||
| | `cloudDemo` | `MOI_CLOUD_DEMO` | `false` | | ||
| | `demoInstallUrl` | `MOI_DEMO_INSTALL_URL` | `https://moi.computer` | | ||
|
|
||
| For the public cloud demo. `cloudDemo` blocks creating workspaces (the UI shows a promo instead) and turns off the `moi` system commands except `start` and `version`. `demoInstallUrl` is where that promo links. If the demo gate must never fail open, set `MOI_CLOUD_DEMO=1` in the environment: an unreadable file can't switch it off. | ||
|
|
||
| ## Cloudflare Access | ||
|
|
||
| | Option | Env | | ||
| | ----------------------------- | ----------------------------------- | | ||
| | `cloudflareAccess.teamDomain` | `MOI_CLOUDFLARE_ACCESS_TEAM_DOMAIN` | | ||
| | `cloudflareAccess.audience` | `MOI_CLOUDFLARE_ACCESS_AUD` | | ||
|
|
||
| For moi behind Cloudflare Access (Zero Trust). moi checks the token Access adds to each request and uses its email as the viewer's profile. `teamDomain` is `acme`, `acme.cloudflareaccess.com` or its https URL. `audience` is the application's AUD tag, or a list of them (comma-separated in the env). Both are needed; with only one, moi trusts nobody and says so. Unset, moi trusts no proxy. | ||
|
|
||
| ## Managed machines | ||
|
|
||
| For a machine that runs moi for an organization in the cloud, where moi and the agent run as different OS users. These three come from the file only, with no env override, so whoever writes the file (the machine's start script) decides them. | ||
|
|
||
| | Option | Default | | ||
| | --------------- | ----------------- | | ||
| | `workspacesDir` | `~/moi` | | ||
| | `agentLauncher` | none | | ||
| | `unixSocket` | none (port 13337) | | ||
|
|
||
| All three must be absolute paths. | ||
|
|
||
| **`workspacesDir`**: the folder that workspaces created in the UI go into. Use it when the home folder is the wrong place, for example to keep workspaces on a persistent disk. | ||
|
|
||
| **`agentLauncher`**: an executable that every agent process starts through: `agentLauncher <command> <args…>`. That covers Claude Code, Codex, function workers, applet builds, installs and chat titles. It must switch to the agent's user, drop anything that user may not have, set `HOME` to the agent's home, and `exec` the command, keeping stdin, stdout and the rest of the environment. Use it to keep the agent out of moi's data folder. When set: | ||
|
|
||
| - moi must be able to read and write the agent's files (chat histories, workspaces). The machine grants this; moi doesn't check. | ||
| - The agent can't open moi's data folder, so its `moi` CLI works through the running server. `init`, `start`, `service`, `update` and the provider `init`s refuse to run for it; `version` and `status` still work. | ||
| - New code that starts agent processes must use `agentCommand()` or `claudeCodeSpawn()` from `server/agent-launcher.ts`, or it runs as moi's user. | ||
|
|
||
| **`unixSocket`**: a socket file the web side listens on instead of port 13337. Put it in a folder only the machine's gateway can open, so nothing else on the machine, the agent included, reaches the API meant for people. | ||
|
|
||
| ## Not in the config | ||
|
|
||
| Experimental features are switched on only with `moi start` flags, such as `--experimental-collab`. User settings live in `settings.json` and change while moi runs. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // Managed machines run moi and the agent as different OS users (see | ||
| // docs/config.md). `agentLauncher` in config.json names an executable | ||
| // that every agent process starts through: it switches to the agent's user, | ||
| // drops moi's powers, then execs the command. That covers harness CLIs, | ||
| // function workers, applet builds and dependency installs. Unset (every local | ||
| // install) spawns directly, exactly as before. | ||
| import { spawn } from 'node:child_process' | ||
|
|
||
| import type { Options } from '@anthropic-ai/claude-agent-sdk' | ||
|
|
||
| import { getAppConfig } from './app-config' | ||
|
|
||
| export function agentCommand(argv: readonly string[]): string[] { | ||
| const launcher = getAppConfig().agentLauncher | ||
| return launcher ? [launcher, ...argv] : [...argv] | ||
| } | ||
|
|
||
| // Claude Code is started by the Agent SDK rather than by moi, so it goes | ||
| // through the launcher via the SDK's spawn hook. Undefined keeps the SDK's own | ||
| // spawn. stderr is inherited: the hook's process type has no stderr stream for | ||
| // the SDK to drain. | ||
| export function claudeCodeSpawn(): Options['spawnClaudeCodeProcess'] { | ||
| const launcher = getAppConfig().agentLauncher | ||
| if (!launcher) return undefined | ||
| return ({ command, args, cwd, env, signal }) => | ||
| spawn(launcher, [command, ...args], { cwd, env, signal, stdio: ['pipe', 'pipe', 'inherit'] }) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.