@codori/server
v0.15.2
Published
Codori server for Git project discovery, Codex runtime management, and bundled dashboard serving.
Maintainers
Readme
@codori/server
Codori server for Codex app-server project discovery, backend selection, fallback lifecycle management, and bundled dashboard and immersive WebXR serving.
This package owns the Codori CLI implementation and output. Most users should
install the separate codori package,
which provides the codori command as a thin launcher over this one:
npm install -g @codori/cli
codori startRunning this package directly stays fully supported and behaves identically.
Its own binary is named codori-server so a global install of both packages
cannot collide on the codori name; npx @codori/server resolves that binary
automatically.
Usage
Run Codori; projects come from the connected Codex app-server:
npx @codori/server startFor one release transition, --root is accepted but hidden from help. It scans
descendant Git repositories and registers each with app-server; it never becomes
the server's saved default:
npx @codori/server start --root ~/ProjectThe server serves the dashboard UI, immersive WebXR workspace, REST API, and
websocket proxy from the same origin. The dashboard remains at /; the
independently built @codori/webxr application is bundled under /xr/.
Static WebXR assets use /xr/assets/, while unknown nested /xr/* navigation
routes fall back to the WebXR entry document before the existing dashboard SPA
fallback. Missing asset requests and /api/* routes never fall through to
either application.
The immersive application reuses the existing same-origin project/chat REST and WebSocket routes. Codori does not add a separate WebXR RPC surface or authentication boundary.
The WebSocket proxy also resolves the Codex avatar selected on the remote host.
It supports Codex built-in pets, ~/.codex/pets/<id>/pet.json, and legacy
avatar manifests. Only validated metadata and bounded PNG/WebP bytes cross the
proxy; remote filesystem paths are never returned to the browser. Invalid or
unavailable avatars fall back to a bundled icon.
@codori/server includes a Codex CLI runtime as a safe fallback, so a separate
global codex installation is not required. When launching Codex, the server
first honors CODORI_CODEX_BIN, then scans its effective PATH for a usable
installed codex, and finally uses the bundled runtime.
Experimental realtime voice is enabled by default. The existing
--experimental-realtime-voice flag remains accepted for compatibility. To
opt out for direct launches or an installed service, set
realtimeVoice.enabled to false in ~/.codori/config.json and restart the
service. A newly started daemon or managed fallback enables
realtime_conversation; an already-running incompatible daemon is not
restarted and causes a safe managed fallback. Codori does not edit
~/.codex/config.toml.
WebXR and remote microphone access require a secure context. Localhost may use the browser's secure-context exception, but a headset opening a plain LAN HTTP address cannot enter immersive VR or start realtime voice. For remote HMD use, put Codori behind a private HTTPS ingress such as Tailscale Serve. Codori still has no built-in authentication, so do not expose that ingress publicly without an appropriate access-control layer.
Private Tailscale Serve
On a machine with a running Tailscale backend and a usable MagicDNS name, a normal direct or registered-service start binds Codori to loopback and configures persistent private HTTPS automatically:
codori startUse --tailscale-serve to require this path and fail when its prerequisites
cannot be satisfied. Use --no-tailscale-serve to disable automatic detection
and mutation. codori serve remains accepted as a deprecated alias for
codori start.
Codori inspects tailscale serve status --json, refuses to replace a
conflicting HTTPS root or Funnel listener, starts the HTTP origin only on
127.0.0.1, and applies:
tailscale serve --bg --yes --https=443 http://127.0.0.1:4310After verifying the structured status, Codori prints the private
https://<machine>.<tailnet>.ts.net/ URL. The same mapping is reused on later
launches. Unrelated path handlers are preserved; Codori never runs
tailscale serve reset.
Serve permission
tailscaled usually runs as root and allows a serve config write only from root
or a configured operator. Reading status stays permitted, so Codori detects the
node as eligible and then the write is refused:
Access denied: serve config deniedGrant the account that runs Codori ongoing control, which is what a user-scoped service needs:
sudo tailscale set --operator=$USEROr configure the mapping once with elevated privileges:
sudo tailscale serve --bg --yes --https=443 http://127.0.0.1:4310Codori keeps serving on loopback either way; only the private HTTPS URL is unavailable until Serve is configured.
The background Serve mapping persists when Codori exits. Remove the root mapping explicitly when it is no longer needed:
tailscale serve --https=443 offThis mode is tailnet-only and relies on Tailscale membership and access-control rules. It does not enable Funnel, provide Codori-owned authentication, or support public exposure.
App-server backend selection
On macOS and Linux, Codori prefers the first-party Codex remote-control daemon:
- Resolve
$CODEX_HOME/app-server-control/app-server-control.sock(CODEX_HOMEdefaults to~/.codex). - Perform a bounded WebSocket-over-Unix handshake and app-server
initializeprobe. A socket file is not treated as proof of readiness. - If needed, run the selected
codex remote-control start --jsononce across concurrent callers and probe the socket reported by the command. - Fall back to the existing Codori-managed TCP app-server for an unsupported command, inaccessible socket, failed handshake, or incompatible realtime capability.
The executable is resolved once per Codori server process and reused for both the daemon-start and managed app-server paths:
- Use
CODORI_CODEX_BINunchanged when it is set explicitly. - Search the server process's
PATHforcodexand require a successful, boundedcodex --versionprobe. Shell wrappers and version-manager shims are valid candidates. Package-local entries that resolve back to Codori's own bundled entrypoint are skipped so later installed wrappers remain eligible. - Fall back to the bundled
@openai/codex/bin/codex.jsentrypoint when PATH discovery misses, finds a non-executable entry, fails validation, or times out.
An installed service uses its own effective environment rather than an
interactive shell's current PATH; restart the service after changing that
environment. CODORI_CODEX_BIN remains the escape hatch for pinning a specific
wrapper or deliberately selecting the bundled entrypoint.
Codori does not persist the first-party daemon PID or directly reap, restart, or stop it. Stopping a logical workspace only releases Codori's reference to it. If a daemon-backed bridge disconnects, that browser RPC connection closes and the next connection performs backend selection again; Codori never migrates an active JSON-RPC session between backends.
The browser-facing route and the daemon control connection are both WebSocket. Codori's thin transport adapter opens one independent WebSocket connection over the Unix socket for each browser bridge and forwards text and binary frames without changing the JSON-RPC payload.
Codori only connects to the control socket as a client; it never binds, removes,
or claims ownership of the socket. Multiple clients, including a Codex Desktop
SSH proxy and Codori, can therefore share one daemon. If no ready socket can be
reused, however, codex remote-control start may restart a managed app-server
when it needs to change the persisted remote-control setting, disconnecting
clients of that app-server during the lifecycle transition.
If Codori cannot safely stop an already-tracked managed fallback before selecting the daemon, it retains the runtime record and continues using the managed backend. The status API reports this controlled fallback instead of orphaning the process.
GET /api/runtime/backend and Settings → Backend expose the selected backend
kind, transport, readiness, version, compact fallback reason, and resolved
Codex executable with its override, path, or bundle source. They
intentionally do not expose the Unix socket path.
The daemon integration is Unix-only and requires the Codori service user to
traverse the effective CODEX_HOME and open its socket. A container must mount
the same Codex state directory at that path and use compatible UID/GID
permissions. Codori does not relay the daemon protocol over TCP; when direct
socket access is unavailable, the managed fallback is the supported behavior.
Service Installation
Use the installed codori command as the canonical entrypoint:
codori service installThe package invocation is equivalent when nothing is installed globally:
npx @codori/server service installAvailable service lifecycle commands:
codori service install
codori service start
codori service stop
codori service restart
codori service status
codori service uninstallThe earlier install-service, setup-service, restart-service, and
uninstall-service commands remain accepted as aliases.
Every verb operates on the Codori service and reads projects from its connected app-server. Legacy root-keyed installations are rejected with migration guidance rather than selecting a remembered directory.
The installer resolves missing --port values interactively and uses 127.0.0.1 as the safe default host. Its Tailscale Serve policy defaults
to auto; --tailscale-serve stores a required policy and
--no-tailscale-serve stores a disabled policy. service install, service
start, and service restart print the verified tailnet URL whenever Serve is
active.
By default Codori installs a user-scoped service:
- macOS:
~/Library/LaunchAgents - Linux:
~/.config/systemd/user - Windows: a Task Scheduler logon task under the
\Codori\folder
Use --scope system for a machine-wide service. A system scope registers a
launchd daemon in /Library/LaunchDaemons, a systemd unit in
/etc/systemd/system, or a Windows boot task running as SYSTEM. If elevated
privileges are required, Codori stops before writing files and prints the exact
command to re-run, or the same command from an Administrator terminal on
Windows. On macOS and Linux that command preserves PATH:
sudo --preserve-env=PATH "$(command -v codori)" service install --scope systemA bare sudo codori ... is not reliable. sudo replaces PATH with its
compiled secure_path, so a per-user Node install (nvm, fnm, asdf, mise, volta)
is either invisible to sudo or, worse, the #!/usr/bin/env node shebang
resolves to a distro Node too old to run the bundle.
service start and service restart regenerate the launcher script and
service definition before launching. Launchers use the canonical
@codori/server start verb. Metadata written by older releases has no ingress
policy; the next start/restart/update treats it as auto, rewrites it to the
current schema, switches the backend listener to loopback, and restarts while
without restoring a remembered project root.
On macOS the launchd label and launcher directory include a deterministic 12-character SHA-256 prefix retained for compatibility with an existing installation. Service lifecycle commands are not scoped by a project root.
App-server projects
Codori lists and creates the projects persisted by Codex app-server. The Add project dialog accepts a name plus one or more absolute directories on the server; its folder browser uses app-server filesystem APIs, so it works when a browser connects to Codori over SSH/Tailscale. Browser-native folder pickers select the browser machine and are intentionally not used for remote projects.
GET /api/projects describes this inventory explicitly as the server-local
app-server-project-registry, including the serving host identity, whether the
registry is ready or empty, and the supported project/create registration
path. Project ids remain the opaque ids returned by app-server and are never
reconstructed from names or paths.
This registry is not the Codex App's host-aware saved-project catalog. The current public app-server protocol has no RPC that enumerates the desktop catalog, including SSH projects managed by a separate macOS Codex App. Codori therefore reports catalog synchronization as unsupported and directs users to register directories deliberately on the Codori host. It does not read desktop global state, merge projects by label/path, or fabricate remote projects. The upstream propagation limitation is tracked in openai/codex#23527.
Windows notes
Windows registration uses Task Scheduler rather than the Service Control
Manager. sc.exe expects a real service executable that calls
StartServiceCtrlDispatcher, so a plain Node process registered that way fails
to start. A user-scoped install creates a logon task at least-privilege level and
needs no administrator rights. Because Windows has no direct equivalent of
launchd KeepAlive or systemd Restart=always, the generated task definition
carries restart-on-failure settings and no execution time limit.
Updates
When a registered service starts, Codori checks the npm registry for a newer
@codori/server release and runs that bundle for the launch. This startup
adoption is silent.
While the service runs, Codori re-checks periodically. A newer release found mid-session only enables the Update affordance in the dashboard; applying it restarts the service, so it always waits for an explicit confirmation.
For the full project overview and remote access notes, see the repository README: https://github.com/comfuture/codori
