Remote & Cross-Device Connections

Applies to: ChengOS v0.2.0+ | Last verified: 2026-09-25 | Source: crates/cheng-cli/src/config.rs, crates/cheng-cli/src/main.rs, crates/cheng-cli/src/auth, crates/cheng-local-executor

The cheng CLI does not have to run on the same machine as the ChengOS server. When it does not, one question decides everything else: when a workflow reads a file, whose disk is it?

Two workspace modes

ModeFile tools runSees
server_localOn the API hostThe server's filesystem
client_localIn this CLI processYour directory

client_local is what makes a remote server useful for local work: the server orchestrates, your machine executes, and your files never leave it.

The default is chosen for you

If you do not specify a mode, the CLI derives it from the server URL's host:

localhost | 127.0.0.1 | ::1   →  server_local
anything else                 →  client_local

That default is right almost always. A local server sharing your filesystem should just use it; a remote server obviously cannot.

Override per session with --workspace-mode server_local|client_local, or permanently with workspace_mode in the config file. An unsupported value is rejected outright rather than silently falling back.

cheng back is always client_local and deliberately has no --workspace-mode flag, even when the server is on localhost: the product promise is that the selected CLI process owns and serves its launch directory.

Connecting

cheng chat --server https://chengos.example.com

Use the server's public HTTPS address. A hardened server binds its API to 127.0.0.1, so http://<host>:19225 is unreachable from another machine by design; the reverse proxy forwards /api and /ws on 443.

Precedence, highest first: command-line flags → environment variables → the config file → built-in defaults. The environment variables are CHENG_SERVER_URL and CHENG_TOKEN; the default server is http://localhost:19225.

The config file lives at $CHENG_CONFIG or ~/.config/cheng/config.json:

{
  "server_url": "https://chengos.example.com",
  "token_file": "~/.config/cheng/token",
  "default_workspace": "Default Workspace",
  "default_workflow": "main_chat",
  "request_timeout_secs": 30,
  "auth_storage": "auto",
  "workspace_mode": "client_local",
  "workspace_tools": { "shell_enabled": true, "git_enabled": true }
}

Credentials

Bearer tokens are never echoed, never written to REPL history, and never written to project files. Persisting one is an explicit choice, made in the user-scoped config file — or better, via token_file, a pointer to a file whose trimmed contents are the token.

Storage policy (auth_storage, or --auth-storage):

PolicyStores the credential
auto (default)OS keyring; on failure, ask on a TTY or error when non-interactive
keyringRequire the OS credential service
host-boundAES-256-GCM file encrypted with a host/user-derived key — transparent, no master password
encrypted-fileAES-256-GCM file encrypted with a master password
filePlaintext file — requires explicit opt-in
noneIn-memory only; lost on exit

host-bound is the pragmatic choice on a headless box: encrypted at rest, no keyring daemon, no password prompt in a script.

Note that auto errors rather than downgrading when there is no TTY. A CI job with no keyring fails loudly instead of quietly writing a plaintext token.

Credentials are server-scoped, so one machine can hold credentials for several ChengOS servers and switching --server switches identity with it.

Local tools in client-local mode

In client_local mode the CLI executes workspace tools itself, through cheng-local-executor — the same execution shell used by other local clients, so file and process semantics do not drift between them.

The workspace_tools config section controls what is permitted:

SettingDefaultMeaning
shell_enabledonAllow shell commands
shell_allow_shell_stringoffAllow raw sh -c strings — high risk
shell_default_timeout_ms / shell_max_timeout_msCommand timeouts
shell_env_allowlistemptyExtra environment variables commands may set
shell_deny_patternsemptyExtra denied command substrings, defence in depth
git_enabledonGit operations
tests_enabledonTest runner
markdown_enabledonMarkdown tooling
preview_enabledonPreview
code_index_enabledonCode index

shell_allow_shell_string is off by default because a raw shell string defeats argument-level policy: once the command is one opaque string, the deny patterns are the only line left. Turn it on only when you genuinely need shell features such as pipes.

File changes are decided by the server, described by you. Before a mutating file operation runs, the server's approval policy asks the executor that owns the files for a preflight: a side-effect-free description of the planned change (canonical paths, whether targets exist, sizes, hashes). In client_local mode that executor is this CLI. Small, scoped, undoable changes then run without a prompt; everything else asks. The facts are pinned, and the CLI refuses the call with stale_precondition if a file changed in between. An older CLI that cannot answer the preflight still works, but every file change asks for approval (preflight_unavailable). See Tool-call Approval Policy.

Read-only sessions are the safest way to point the CLI at a directory you do not want touched:

cheng chat --read-only --cwd ~/projects/my-repo

Sessions across devices

cheng sessions                     # your CLI sessions on the server
cheng chat --resume <session-id>   # rejoin one
cheng history                      # durable conversations, incl. paused work
cheng chat --resume-conversation <uuid>

Sessions live on the server, so a session started on your laptop is listed from your desktop. --resume-conversation exists for when the old CLI session has expired but the conversation is still there; it conflicts with --resume, --workspace, and --workflow because those would contradict the conversation's own binding.

Resuming a client-local session checks that the stored root label matches your current directory — a resumed session should serve the same tree it was serving before. Labels longer than the server's display cap are stored truncated with a leading ellipsis, and the comparison accounts for that.

Background agents

cheng back                  # serve this directory as a client-local workspace
cheng ps [-a]               # list background agents
cheng logs <pid|session>    # where its logs go
cheng stop|restart|rm <id>
cheng prune                 # clear stopped and stale metadata

cheng back runs headless and serves the current directory to the Web UI: you keep working in the browser while your files stay on your machine. --status, --stop, and --logs act on the agent recorded for the current directory, which is usually easier than looking up a pid.

To serve several projects from one agent, declare the roots — on the command line with repeatable --root PATH[:ro], or once in the config file:

{ "back_roots": ["/home/me/works/api", "/home/me/works/docs:ro"] }

Use absolute paths in the config file. Each root gets its own sandbox and session; roots must not nest. --root overrides back_roots, and with neither the launch directory is served.

A remote setup that works

// ~/.config/cheng/config.json
{
  "server_url": "https://chengos.example.com",
  "auth_storage": "host-bound",
  "workspace_mode": "client_local",
  "default_workspace": "Default Workspace",
  "default_workflow": "main_chat",
  "workspace_tools": {
    "shell_enabled": true,
    "shell_allow_shell_string": false,
    "git_enabled": true,
    "tests_enabled": true
  }
}

Then cheng chat in any project directory connects to the remote server while executing file and shell work locally.

Troubleshooting

SymptomCause
File tools see the wrong filesWrong workspace mode — check the auto-selection rule above
Auth fails in CIauto errors without a TTY; use host-bound or CHENG_TOKEN
Shell commands are refusedshell_enabled off, a deny pattern matched, or the session is --read-only
--resume refusesThe stored root does not match your current directory
cheng --version differs from the serverIt reports the ChengOS release version; a mismatch means genuinely different builds
Connection refused on port 19225The server's API is loopback-only; use its HTTPS address
Every small edit asks for approvalThis CLI predates preflight (preflight_unavailable); upgrade it
cheng back refuses the rootsTwo roots nest; serve their common parent as one root

Next steps