Skip to content

relay 0.4.0

Choose a tag to compare

@HarperZ9 HarperZ9 released this 26 Sep 21:58
· 23 commits to main since this release
ac4d798

The session store stays inside itself, and every program relay starts gets an
environment allowlist and, for the claude and codex tiers, a proven isolation
profile. This was scoped as a 0.3.1 patch. It takes the minor number instead
because it changes what existing setups do, the rule 0.3.0 set for a pre-1.0
break:

  • run, test_cmd and check no longer inherit the server's whole
    environment. A command that reads a variable outside the allowlist, such as a
    test suite that needs DATABASE_URL or a provider key, sees it unset until
    RELAY_CHILD_ENV names it.
  • On Windows a shell no longer finds a program in the root by bare name. Call it
    as .\tool.
  • The claude tier runs with --tools "" and the codex tier with a read-only
    sandbox, both in a new empty folder. A setup that used either tier as a
    sub-agent acting on the project gets a model reply only.
  • With RELAY_SESSION_DIR unset, local_agent_sessions reads a per-user folder
    instead of the folder the server started in.
  • local_agent_sessions refuses a session_id that is not a bare name.
  • MCP file tools refuse writes into agent and editor configuration folders.

The MCP tool names, the relay.mcp-run-request/v2 binding and the shape of
relay.status are unchanged. relay.doctor and the session listing gain
fields.

Source version metadata is not release availability proof; release availability
is established only by the accepted Git tag, uploaded GitHub Release assets, and
matching hash readback. Do not publish or recommend the bare PyPI name
relay-agent; that public namespace belongs to an unrelated project and is not
the HarperZ9 Relay distribution.

Security

  • local_agent_sessions joined the caller's session_id onto the session
    store with no check. "../outside/private" or an absolute path returned the
    transcript of any ledger file the server's user could read, with no grant.
    Affected: 0.2.0 through 0.3.0. Fixed: a session_id is a bare name, and a file
    whose real path leaves the store is neither opened nor listed.
  • The claude and codex CLI tiers started the CLI by bare name from the
    server's folder, with every variable and no isolation flags. A claude.exe
    planted in that folder ran instead of the installed one, and a
    .claude/settings.json there could run its hooks. Reaching a tier needed the
    exec grant. Affected: every release through 0.3.0. Fixed below.
  • run, test_cmd, check and bisect checks inherited every provider key, so a
    model with exec could print them into the ledger it returns. Affected: every
    release through 0.3.0.

Changed

  • The CLI tiers start through a vendored copy of safe_spawn 1.0.0
    (src/relay/_vendor/safe_spawn.py, SHA-256 recorded in VENDORED.sha256): an
    absolute executable, a new empty working folder, an environment allowlist, the
    prompt on stdin, and the isolation profile the probes proved. For claude
    2.1.251 that is --setting-sources user --strict-mcp-config --tools "". For
    codex 0.144.6 it is exec --ignore-user-config --ignore-rules --ephemeral --skip-git-repo-check --sandbox read-only --disable hooks --disable plugins --disable memories --disable apps -c project_doc_max_bytes=0 -c skills.include_instructions=false. RELAY_CLAUDE_CLI and
    RELAY_CODEX_CLI override the executable and must be absolute paths. The
    codex tier now runs on a stock Windows npm install, where 0.3.0 could not
    find codex.cmd. The tiers still need exec.
  • A CLI tier whose isolation profile is not proven is refused with
    EXEC_NOT_GRANTED unless the launch also names it in RELAY_ALLOW_EXEC_CLI.
    No such tier ships today.
  • relay.doctor adds cli_tiers, one row per CLI tier: PASS only when the
    CLI resolves, exec is granted, the profile is proven and --version reports
    the tested version, and WARN with a setup code (CLI_NOT_FOUND,
    EXEC_NOT_GRANTED, CLI_PROFILE_UNPROVEN, CLI_VERSION_UNTESTED,
    CLI_VERSION_UNKNOWN) otherwise. It starts --version only under exec and
    never reports the resolved path.
  • Shell children get the platform base, a fixed set of toolchain variables
    (VIRTUAL_ENV, JAVA_HOME, CARGO_HOME and similar) and the names in
    RELAY_CHILD_ENV. PATH keeps absolute entries only, and on Windows
    NoDefaultCurrentDirectoryInExePath=1 stops cmd.exe searching the root.
  • RELAY_CHILD_ENV, RELAY_ALLOW_EXEC_CLI, RELAY_CLAUDE_CLI and
    RELAY_CODEX_CLI are launch-only. The remote entrypoint never takes them from
    its env file and names any it finds there.
  • The session store defaults to a per-user folder: %LOCALAPPDATA%\relay\sessions
    on Windows, ~/Library/Application Support/relay/sessions on macOS, and
    $XDG_DATA_HOME/relay/sessions or ~/.local/share/relay/sessions elsewhere.
    MCP runs cannot write it.
  • The session listing skips a file that will not load, a dangling link or a
    name that is not a valid id, and counts them in skipped. A missing session
    carries "code": "NOT_FOUND" beside its error text.
  • The local_agent_sessions description and session_id schema say the id is
    a bare name.
  • The stdio relay.doctor reads an env file only when RELAY_ENV_FILE names
    one. The remote entrypoint still reads .env from its folder.
  • MCP file tools refuse writes into .claude, .codex, .cursor, .vscode,
    .gemini, .agents, .opencode and .mcp.json.
  • --auto-commit resolves git to an absolute path, so a git.exe in the
    working folder never runs.

Limits

  • The profiles are proven for the tested CLI versions only, by one probe run per
    scenario on Windows. The Linux half of the probes has not run.
  • codex has no flag that removes its shell tool. Under the profile it can still
    run read-only commands in its empty folder. Whether the CLI tiers become
    model-only backends that no longer need exec is an open decision.
  • The shell is still not path-confined, and write is still a route to code
    execution outside the refused folders.