# Code Remote Manager

Personal, Windows-only remote control for local Codex. A Node backend launches account-scoped Codex app-server processes, and an installable React PWA controls them over localhost or a manually configured Tailscale connection.

## Current Capabilities

- Unified, searchable Chats across configured Codex homes, with project grouping, pagination, pin, rename, and archive actions.
- Explicit execution-account selection. Opening a Chat does not change the selected account; continuing it under another account creates a new account-owned Codex thread carrying only the visible conversation context.
- Desktop Codex history as either a visible execution account or a hidden history-only source.
- New and existing project selection below `projectsRoot`, including safe workspace registration and new-folder creation.
- Streaming turns, compact activity logs, plans, commands, safe reasoning summaries, elapsed time, queued messages, steering, and turn interruption.
- App-server capability discovery plus model/effort, permission, and plan-mode composer controls.
- Multi-image input, per-chat draft recovery, and browser-dependent speech input.
- Command, file-change, and additional-permission approval cards.
- Changed-file and diff views limited to the selected allowlisted workspace.
- Account quota/reset, active-thread context usage, cross-chat completion/approval notifications, and installable PWA shell.
- Per-device PC pairing by a five-minute Setup QR/code, persistent browser authentication, and
  protected HTTP/SSE control APIs.
- Mobile `100dvh` layout with an internally scrolling conversation and bottom composer.

Engineering rules: [AGENTS.md](AGENTS.md)

Current pre-release status: [docs/PRE_RELEASE_CHECKLIST.md](docs/PRE_RELEASE_CHECKLIST.md)

Product status and next work: [ROADMAP.md](ROADMAP.md)

Documentation map and release authority: [docs/README.md](docs/README.md)

Unapproved store copy and promotional screenshot draft: [docs/STORE_LISTING.md](docs/STORE_LISTING.md)

## Prerequisites

- Windows
- Node.js and npm
- Codex CLI
- At least one Codex account, authenticated either through the PWA device-code flow or against a preconfigured `CODEX_HOME`
- Tailscale only when phone access is required

## Setup

```powershell
npm.cmd install
Copy-Item config.example.json config.local.json
```

Edit ignored `config.local.json`:

```json
{
  "schemaVersion": 1,
  "deviceAuthentication": {
    "required": true
  },
  "codexCliPath": "C:\\path\\to\\codex.cmd",
  "engine": "codex",
  "bindHost": "127.0.0.1",
  "port": 43110,
  "allowedOrigins": [
    "http://127.0.0.1:43111",
    "https://your-device.your-tailnet.ts.net"
  ],
  "permissionCeiling": {
    "permissionProfile": ":danger-full-access",
    "approvalPolicy": "never"
  },
  "dataDir": "C:\\path\\to\\private-runtime-data",
  "projectsRoot": "C:\\parent\\of-project-folders",
  "accounts": [
    {
      "id": "main",
      "label": "Main",
      "codexHome": "C:\\path\\to\\account-codex-home"
    },
    {
      "id": "desktop-history",
      "label": "Desktop history",
      "codexHome": "C:\\path\\to\\desktop-codex-home",
      "historyOnly": true
    }
  ],
  "workspaces": [
    {
      "id": "example-project",
      "label": "Example project",
      "path": "C:\\parent\\of-project-folders\\example-project"
    }
  ]
}
```

- `schemaVersion`: current configuration schema is `1`. An omitted version is treated as the
  legacy pre-versioned format during upgrade; any other explicit version fails closed.
- `deviceAuthentication.required`: defaults to `true`. Set it to `false` only in ignored
  `config.local.json` for a deliberate single-operator emergency exemption. This skips the
  per-device cookie check, but exact Origin, CSRF, workspace allowlisting, the permission ceiling,
  and Tailscale network policy still apply. Restore `true` as soon as pairing is available again.
- `engine`: use `"codex"` for Codex app-server or `"fake"` for local UI testing.
- `bindHost` and `port`: Backend listener. `bindHost` must be a literal IPv4 or IPv6 loopback
  address; hostnames, wildcard addresses and LAN/tailnet addresses fail configuration validation.
  Keep the recommended `127.0.0.1` value for local and Tailscale use.
- `allowedOrigins`: optional exact browser origins allowed to mutate backend state. Local PWA origins are allowed by default; keep machine-specific Tailscale origins only in `config.local.json`.
- `permissionCeiling`: local PC maximum for chat-selected Codex permissions. Setup enables every
  built-in choice, while the composer still defaults to workspace write and `on-request`. Lower
  this ignored local value to prevent any paired browser from selecting a higher mode; the browser
  has no configuration endpoint that can raise it.
- `CODE_REMOTE_MANAGER_PWA_ALLOWED_HOSTS`: optional comma-separated Vite dev-server host allowlist for additional hostnames. The safe-runtime supervisor automatically derives HTTPS hostnames from ignored local `allowedOrigins`; direct development runs may still set this environment variable explicitly.
- `dataDir`: ignored session cache, thread preferences, and uploaded images. Accounts created in the PWA receive a managed Codex home under a sibling `accounts` directory.
- `projectsRoot`: optional default parent whose direct child folders the New Chat picker lists and where it creates named projects. The PC folder browser can register another folder selected by the operator without exposing its absolute path to the browser.
- `accounts`: account-scoped Codex homes. Set `historyOnly: true` to include local Chat history without offering that source as an execution account. History-only sources are excluded from login, account usage, capability discovery, runtime prewarming, and app-server health probes. Omit it from the Desktop Codex entry when chats created here should also be created in Desktop history.
- `workspaces`: filesystem allowlist. Workspace lists expose IDs and final-folder labels; the Changes feature exposes only paths inside the selected workspace.

Do not commit `config.local.json`, runtime data, credentials, or Codex homes. The PWA's **Sign in** action starts Codex device-code login in a temporary managed home and adds the account configuration only after login succeeds; canceled, expired, or restart-interrupted attempts are discarded. Removing an established account removes only its app configuration, not its local login or chat files.

The Windows Setup app owns only the schema marker, the detected Codex CLI path, the required
local/current allowed origins, and an explicitly selected workspace addition. Upgrades preserve
existing accounts, `historyOnly` sources, workspace entries, `projectsRoot`, user data references,
and unknown fields. Corrupt JSON, duplicate IDs, and unsupported schemas are rejected before host
changes start. Successful upgrades keep the three newest original configurations in the adjacent
`config-backups` directory and use a durable pending file, atomic replacement, validation by the
packaged Backend loader, and rollback. Uninstall removes the program and startup entry but retains
configuration, backups, authentication files, and user data.

### Pair a phone or another browser

Windows Setup prepares the local host and then creates a pairing QR and 12-character fallback
code. The QR/code expires after five minutes, is single-use, and is disabled after five failed
code attempts. Scan it in the native app, or open the PWA and enter the code. A QR scan in a new
native install sets the PC address and pairs that device in one step.

Each paired browser receives a separate 180-day credential in an `HttpOnly` cookie. The Backend
stores only a hash of that credential in `dataDir/security/device-auth.json`; pairing codes are
also hashed. Reopening the browser or installed PWA keeps the connection until the credential
expires, is rotated, or that device is revoked. The authenticated device API supports listing
devices, rotating the current credential, and revoking one device without affecting other devices,
accounts, Chats, workspaces, or Codex data.

Treat the ignored configuration, its backups, and `dataDir` as one PC backup set. Restoring the
authentication state restores the paired-device records, but not a browser cookie that was lost.
In that case, create a new local pairing and revoke the old device record. Uninstall intentionally
retains configuration, backups, authentication state, and user data.

By default, all control/data routes and both SSE streams require device authentication. A deliberate
local-only emergency exemption can skip that credential check as described above; it must never be
enabled for Funnel or a broadly reachable host. Mutating browser requests still require an exact
allowed Origin and the app's CSRF marker, and SSE still requires an exact Origin. `GET /health`,
pairing submission, and CORS preflight are the only unauthenticated surface when the exemption is
off. Origin checks are an additional browser boundary, not authentication. Pairing replay is blocked
by single-use attempts and expiry; message/approval duplicate-delivery protection remains the
separate P0-D idempotency gate.

## Run

Use separate terminals:

```powershell
npm.cmd run dev:backend
```

```powershell
npm.cmd run dev:pwa
```

Open `http://127.0.0.1:43111`.

- backend default: `127.0.0.1:43110`
- PWA default: `127.0.0.1:43111`
- Vite proxies same-origin `/api` requests to the backend

### Experimental safe self-development runtime

To keep Code Remote Manager alive while editing this repository, install an independent
runtime under `%LOCALAPPDATA%\CodeRemoteManager\safe-runtime`:

```powershell
npm.cmd run safe:install
```

The installer runs the required tests and build, stages an independent copy, and
prints the path to `start-safe-runtime.cmd`. Stop the two development terminals
once and run that command. The external supervisor then restarts crashed
backend/PWA processes.

The source-copy filter applies both explicit credential/runtime exclusions and
the repository's Git ignore rules. Keep machine-private paths ignored or outside
the source tree; `safe:install` and `safe:apply` verify the filtered staging copy
before promotion.

Source edits do not affect the running copy. Apply a verified update manually:

```powershell
npm.cmd run safe:apply
```

Safe Runtime records the repository that supplied `config.local.json` during installation. Update
requests from another Git worktree are rejected, and only one update request may run at a time.
Commit and merge work into that canonical repository before applying it.

The supervisor keeps the current release running during verification and
dependency installation. It switches only after success, checks both HTTP
endpoints, and restores the previous release if startup fails. It never resumes
or repeats interrupted Codex work automatically.

Supervisor child output is kept in bounded local logs under
`%LOCALAPPDATA%\CodeRemoteManager\safe-runtime\logs`. Health checks use the backend
`bindHost`/`port` from local config and `http://127.0.0.1:43111/` for the PWA by
default. Set `CODE_REMOTE_MANAGER_PWA_URL` before starting the supervisor when the safe
runtime PWA is served from another local URL.

## Phone Access with Tailscale

Keep both development servers bound to loopback. Share only the PWA dev server through Tailscale Serve. The browser should reach the backend only through the PWA's same-origin `/api` proxy.

```powershell
tailscale serve 43111
```

Use `tailscale serve --bg 43111` when you want Serve to keep running after the terminal closes. Check the printed `https://...ts.net` URL, then add that exact origin to `allowedOrigins` in ignored `config.local.json`:

Windows Setup reads both the device Serve/Funnel status and the configured Tailscale Services
before changing host state. If Funnel is enabled or any mapping targets the private Backend port,
Setup stops with a safe error and leaves that Tailscale configuration untouched. After adding the
PWA Serve mapping, Setup checks the exposure state again before registering startup or launching the
host. It never resets or removes an existing Tailscale mapping automatically.

```json
{
  "allowedOrigins": [
    "http://127.0.0.1:43111",
    "https://your-device.your-tailnet.ts.net"
  ]
}
```

For a direct development run, if Vite rejects the tailnet hostname, set `CODE_REMOTE_MANAGER_PWA_ALLOWED_HOSTS` to the hostname before starting `npm.cmd run dev:pwa`. Safe-runtime derives it from `allowedOrigins` automatically:

```powershell
$env:CODE_REMOTE_MANAGER_PWA_ALLOWED_HOSTS = "your-device.your-tailnet.ts.net"
npm.cmd run dev:pwa
```

Remote-access checklist:

- Keep `bindHost` as `127.0.0.1`; do not bind the backend to `0.0.0.0`.
- Serve port `43111` only. This is the PWA.
- Do not serve, proxy, or forward backend port `43110`.
- Do not use Tailscale Funnel for this app.
- Keep real tailnet hostnames only in ignored local config or local environment variables.
- Use a deny-by-default Tailnet policy and a narrow
  [grant](https://tailscale.com/docs/features/access-control/grants) from only the operator's
  user/device to the Windows host. Avoid wildcard source grants; add a device-posture requirement
  when available. Tailscale grants restrict which peers can reach Serve, while Code Remote Manager
  pairing still authenticates the app request. Neither replaces the other.

Expected checks:

- `http://127.0.0.1:43111` opens locally on the Windows host.
- The printed `https://...ts.net` URL opens the PWA from the phone.
- Browser actions work through `/api`; the phone should not need a direct `43110` URL.
- A direct tailnet URL for backend port `43110`, if one was accidentally configured earlier, should be removed with `tailscale serve reset` and then reconfigured for `43111` only.

If remote access fails, check in this order:

1. `npm.cmd run dev:pwa` is running and `http://127.0.0.1:43111` loads locally.
2. `tailscale serve status` shows only the PWA target on port `43111`.
3. `CODE_REMOTE_MANAGER_PWA_ALLOWED_HOSTS` contains the tailnet hostname when Vite rejects it.
4. `allowedOrigins` contains the exact `https://...ts.net` origin when state-changing API calls fail.
5. Backend remains local on `127.0.0.1:43110`; do not fix remote errors by exposing it.

## Known Limits

- Backend and PWA run as development processes, not managed Windows services.
- Tailscale Serve, its least-privilege grant, and the Vite host allowlist still require manual local setup.
- This is a personal single-operator system, not a public multi-user service.
- PWA caching covers the application shell, not offline Codex operation.
- Speech input depends on browser Web Speech API support.
- Service-tier, personality, and plugin controls are not exposed. Reported skills appear in the tools menu, but end-to-end skill invocation is not yet verified.
- Historical or inactive chats may show context usage as `Unavailable` until app-server reports it.
- Browser/Tailscale behavior and complete error-path redaction still require real-runtime and end-to-end verification.

## Verify

```powershell
npm.cmd test
npm.cmd --workspace apps/pwa exec vite build
git diff --check
```
