Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
488c9de
fix(codex): refuse unverifiable effective storage across native layers
abruption Oct 14, 2026
fba08c2
fix(codex): check storage before wake reservations and refine boundaries
abruption Oct 14, 2026
394cfb3
fix(codex): leave shared daemon routing to native Codex
abruption Oct 15, 2026
bc36558
fix(codex): preserve translated consumer compatibility and stat failures
abruption Oct 15, 2026
79516d1
test(codex): use platform absolute fixture directories
abruption Oct 15, 2026
aec0310
fix(codex): match native platform storage and project boundaries
abruption Oct 15, 2026
54572a6
fix(handoff): apply Codex storage execution plan before queueing
abruption Oct 15, 2026
7074e3e
chore(build): regenerate standalone after storage review fixes
abruption Oct 15, 2026
f1b3ad6
fix(codex): preserve native marker and directory edge cases
abruption Oct 15, 2026
cf77503
fix(codex): reject drive-unbound native directory results
abruption Oct 15, 2026
2f6dae1
test(codex): isolate mocked Darwin inventory from native libraries
abruption Oct 15, 2026
7f109fb
test(codex): pass selected home to project fixture discovery
abruption Oct 15, 2026
f812a0f
fix(codex): check effective macOS preferences for supported wake
abruption Oct 15, 2026
09efcc2
fix(codex): bind preference policy and storage checks to native execu…
abruption Oct 15, 2026
cca3808
test(codex): tighten storage fixture isolation and clarify preflight …
abruption Oct 15, 2026
ba884e6
fix(codex): keep ambiguous storage discovery metadata-only
abruption Oct 15, 2026
1272b80
test(codex): distinguish discovery configuration diagnostics
abruption Oct 15, 2026
f01f23e
Merge branch 'main' into fix/268-effective-storage-contract
abruption Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ jobs:
with:
python-version: ${{ matrix.python }}
- run: python -m pip install '.[relay,mcp]' --requirement .github/requirements/build.txt
- run: python -m unittest discover -s tests/relay -t . -v
# Preserve Python stacks if a native dependency/process segfaults;
# this is diagnostic only, not a workaround for an unexplained crash.
- run: python -X faulthandler -m unittest discover -s tests/relay -t . -v
- run: python -m build
- name: Verify optional transport packaging
env:
Expand Down
20 changes: 17 additions & 3 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -483,13 +483,27 @@ prove the queue can accept it.

### SQLite storage boundary

Standalone `list` remains metadata-only: it does not execute Codex or query its version, and `--codex-bin` does not establish the version for discovery. On macOS, discovery therefore conservatively checks ordinary as well as forced preferences before opening a candidate SQLite database. This blocks stale-home results for older loaders, but can also refuse a valid newer environment whose native loader ignores ordinary values. A refusal is an error, never a successful empty list or a switch to another database; valid results from other agents remain available with the incomplete-discovery error. The diagnostic identifies the storage-configuration ambiguity and asks the operator to review known configuration layers, including effective `com.openai.codex` configuration/requirements and the intended home/storage layout. Changing `--codex-home` alone does not resolve global preference ambiguity. Do not automatically remove preferences, managed profiles or databases to suppress the error. If the native loader legitimately ignores the value, discovery remains conservatively unsupported for that configuration; precise version-aware discovery is a separate improvement. Internal send dry-run discovery reuses the already selected send policy without another probe.

For sends and their internal dry-run checks, the macOS preference policy depends on the selected native executable. The exact reviewed Codex 0.162.1 loader accepts only forced values; Codex [0.154.0](https://github.com/openai/codex/blob/6b9826e3aa83b1a5947db50f4332cb9c65f1b340/codex-rs/config/src/loader/macos.rs) reads ordinary as well as forced preferences through `CFPreferencesCopyAppValue`. On macOS, ordinary queue sends and dry-runs run one bounded `--version` child in the selected home and working directory: three seconds for the query and two for cleanup, with 1 MiB stdout and 64 KiB diagnostic limits. Output is never reflected; malformed, unavailable or unreviewed versions conservatively include ordinary preferences rather than refusing solely for their version. Uncertain process cleanup refuses before queue submission, and interruption is propagated after the bounded cleanup attempt. Only the exact reviewed `codex-cli 0.162.1` selects forced-only checks; later versions are not assumed equivalent. Handoff reuses its existing version query and original deadline, then retains the selected policy through submission. The supported 0.154.0 wake path includes ordinary preferences even when an active writer only receives a queue submission. Storage and custom-marker checks repeat before the budget and child boundaries. No new version probe is added outside macOS, no results are cached, and wake support is not expanded. A refusal after known queue submission preserves that submission without resending. These are source-based guards and synthetic regressions, not new native preference or MDM validation.

Owned queue and wake children use an existing, strictly resolved physical working directory, including aliases in any ancestor. Project scanning, child `cwd`, POSIX `PWD` and wake `thread/resume.cwd` use that directory. For a saved consumer directory that still exists, both physical and distinct lexical project spellings are checked with the same marker boundaries and custom-marker fallback; this can conservatively refuse an alias configuration. A missing consumer directory still has no layers to inspect. This does not establish an already running writer's startup configuration, and alias/configuration changes after inspection remain possible. The `wake.cwd` result uses this physical spelling too; it can differ from the saved alias.

The native loader's layer-list comment mentions `<cwd>/config.toml`, but its pinned [project-layer construction](https://github.com/openai/codex/blob/092d3acd6bec3e3a14bdc7e7a2810ab628ab759d/codex-rs/config/src/loader/mod.rs#L1652) loads `.codex/config.toml` instead. This guard follows that implementation, not the stale comment. A successful Windows Known Folder lookup with an invalid or unbounded path is refused rather than silently falling back to a different directory.

Wake preflight checks known storage before reserving its budget, but a later recheck can still refuse after reservation if configuration or writer evidence changes. The translated-native-home route retains its inherited cwd and does not inspect the Windows consumer directory.

The 2026-10-09 source investigation examined native Codex [0.154.0](https://github.com/openai/codex/blob/6b9826e3aa83b1a5947db50f4332cb9c65f1b340/codex-rs/core/src/config/mod.rs) and [0.159.0](https://github.com/openai/codex/blob/687a119f0fcaace47e1f1abcc77cec6c813fd6da/codex-rs/core/src/config/mod.rs). Both resolve SQLite storage from configured `sqlite_home`, then nonempty `CODEX_SQLITE_HOME`, then `CODEX_HOME`; relative environment paths use the native resolved working directory. [Managed requirements](https://github.com/openai/codex/blob/687a119f0fcaace47e1f1abcc77cec6c813fd6da/codex-rs/core/src/config/requirements.rs) can replace configured storage, including command-line overrides. The [native queue path](https://github.com/openai/codex/blob/687a119f0fcaace47e1f1abcc77cec6c813fd6da/codex-rs/tui/src/session_queue_commands.rs) can reuse a running shared server, whose storage is not established by the new client's environment. These are source findings, not new live-delivery or platform validation.

session-peer refuses a nonempty inherited `CODEX_SQLITE_HOME` or a root `sqlite_home` setting in any inspected home's configuration before discovery, dry-run, or queue submission. This includes quoted/escaped root keys and root dotted/table forms, even when the configured path equals the selected home. Unselected nested keys are not root storage settings. Empty environment overrides are removed from native child environments; environment names are case-sensitive on POSIX and case-insensitive on Windows. The guard retains the selected native-home translation where already supported and does not add relocated-storage support.
The 2026-10-15 investigation of native Codex [0.162.1](https://github.com/openai/codex/tree/092d3acd6bec3e3a14bdc7e7a2810ab628ab759d/codex-rs) found the storage precedence to be managed requirements, then the merged configuration layers, then `CODEX_SQLITE_HOME`, then `CODEX_HOME`. Layers that can set `sqlite_home` include system files, macOS managed profiles, the user's `config.toml`, a profile file selected with `--profile`, and the project layers of the native process working directory (`.codex/config.toml` from the working directory up to the nearest default `.git` root, or only the working directory when no root is found; enabled only for trusted projects). Legacy `[profiles.*]` tables have no storage key. `codex queue` submits through a running app-server daemon when one accepts a connection on the home's `app-server-control/app-server-control.sock`, and otherwise through an embedded server. Native TUIs start such a daemon automatically by default unless excluded, for example with `--no-daemon`. A daemon's QueueStore keeps the database chosen at its startup, while `config/read` rereads the current disk layers; `initialize` reports the home and platform, but the reviewed `initialize`, `config/read` and queue APIs offer no stable attestation contract for the startup SQLite identity. A matching thread UUID or the same server connection does not establish storage identity (a copied database can hold the same UUID), and the queue API has no expected-storage or incarnation precondition. A process that consumes queued messages polls the queue in its own storage. These are source findings, not live-delivery or platform validation.

session-peer refuses a nonempty inherited `CODEX_SQLITE_HOME` or a root `sqlite_home` setting in any inspected home's configuration before discovery, dry-run, or queue submission. This includes quoted/escaped root keys and root dotted/table forms, even when the configured path equals the selected home. Unselected nested keys are not root storage settings. Empty environment overrides are removed from native child environments; environment names are case-sensitive on POSIX and case-insensitive on Windows. The guard retains the selected native-home translation where already supported and does not add relocated-storage support. It also refuses a root `sqlite_home` in the system configuration and requirements files (`/etc/codex/config.toml`, `/etc/codex/managed_config.toml`, `/etc/codex/requirements.toml`, or the Known Folder API's ProgramData directory followed by `OpenAI\Codex\config.toml` and `requirements.toml` on Windows) and in macOS managed-profile values, regardless of project trust. A queue submission starts the native process in the inspected home, so the caller's directory adds no project layers. Dry-run checks the same execution plan (home, child working directory, environment, known layers and consumer directory) without queue/app-server children (wake dry-run and ordinary macOS queue dry-run may run the native version probe), and a real send repeats the checks immediately before its effect; neither removes the time gap before native Codex reads its configuration, so a passing dry-run does not guarantee storage agreement for a later send. By user policy, a daemon control socket, live or stale, is neither a refusal reason nor probed: native Codex selects the daemon or embedded server as before, and no storage authority is claimed. Outside the translated-native-home route, dry-run, queue and wake also inspect the project layers of the saved thread directory, where the consuming writer or wake server normally starts; unreadable or malformed thread metadata is refused. Wake runs these storage checks before reserving its per-target budget. Refusal reasons are `sqlite_environment_override`, `sqlite_home_configuration`, `sqlite_home_system_configuration`, `sqlite_home_managed_configuration`, `sqlite_home_project_configuration`, `consumer_directory_unverifiable`, `storage_config_unreadable` and `storage_config_unverifiable`.

Each inspected TOML file must be a regular, non-symlink UTF-8 file of at most 1 MiB. On Windows, the system directory is obtained through `SHGetKnownFolderPath(FOLDERID_ProgramData)`, with native Codex's fixed `C:\ProgramData` fallback on API failure; the `ProgramData` environment variable is not used. On macOS, CoreFoundation synchronizes the Codex preference domain and releases owned values. The forced-only policy rechecks forced status after copying and ignores ordinary user preferences; the version-selected conservative policy also reads ordinary values, as described above. Selected base64 strings are bounded to 2 MiB and decode to UTF-8 TOML of at most 1 MiB; synchronization, type, decoding or size errors refuse. Missing TOML files or preference values are allowed. Default project markers stop at the nearest non-directory `.git` entry (metadata only) or a directory containing `HEAD`; symlink markers are conservatively refused; without a marker only cwd applies. `<cwd>/config.toml` is not a project layer. A known root `project_root_markers` override retains a conservative ancestor superset instead of claiming exact custom-marker interpretation. These APIs are covered by mocks; relocated Windows Known Folders and real forced MDM profiles have not been tested. Unreadable, changed, oversized, or lexically unclassifiable files are refused without printing their contents. The dependency-free key classifier supports Python 3.9 and is not a general TOML/schema validator; native Codex still validates other values. Queue and wake recheck this boundary before starting a child. A refusal has `status=refused`, `submitted=false`, and `retryAllowed=false`; valid ordinary outputs and writer-ownership checks remain unchanged. SSH performs this check on the destination, not on the sender's environment.

The home's `config.toml` must be a regular, non-symlink UTF-8 file of at most 1 MiB; missing files are allowed. Unreadable, changed, oversized, or lexically unclassifiable files are refused without printing their contents. The dependency-free key classifier supports Python 3.9 and is not a general TOML/schema validator; native Codex still validates other values. Queue and wake recheck this boundary before starting a child. A refusal has `status=refused`, `submitted=false`, and `retryAllowed=false`; valid ordinary outputs and writer-ownership checks remain unchanged. SSH performs this check on the destination, not on the sender's environment.
Translated native homes (a WSL relay receiver queueing to native Windows Codex) keep their existing submission contract, but storage verification is not supported for them: this POSIX process cannot inspect the Windows-side home spelling, case rules, environment, or system, managed and project layers. A future strict mode could refuse such paths before any effect; it is not implemented.

This is bounded hardening, not proof of effective native storage for every configuration. Project/system/profile layers, managed requirements, and an existing shared server are not resolved or attested by this guard; configuration can also change after the final check. Do not infer storage agreement from a successful check alone. [#268](https://github.com/abruption/session-peer/issues/268) remains open for that evidence gate and version/platform acceptance. Fake-child fixtures show refusal and environment behavior only; they do not establish actual wrong-target delivery, a security exploit, or new native compatibility. The wake version matrix remains unchanged.
This is bounded hardening, not proof of effective native storage for every configuration. Not inspected: package-supplied defaults; enterprise cloud configuration bundles; the startup storage of a running shared daemon; a `--profile` selection, `-c` overrides or the environment of a writer that is already running; a writer that started outside the saved thread directory or whose directory no longer exists; and, for a translated native home, the Windows-side system, managed and project layers. Configuration can also change after the final check. Do not infer storage agreement from a successful check alone. [#268](https://github.com/abruption/session-peer/issues/268) remains open for native platform validation of this contract. Fake-child fixtures show refusal and environment behavior only; they do not establish actual wrong-target delivery, a security exploit, or new native compatibility. The wake version matrix remains unchanged.

### Orca and multiple Codex homes

Expand Down
2 changes: 1 addition & 1 deletion docs/i18n-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"docs/ai-assistant-guide.md": "9624ec96b92fb688fae3be3c94cb3564c77985091be451fbf1f056a1af9b9d9c",
"docs/antigravity.md": "f2b5122a0ce6e6fb5bd4d8ed50ce2c8bd2dc9c98ec8ce328405cf7c2cd24229d",
"docs/architecture/agent-transports.md": "01a4e3942e87acb476595859f2e37f9653ea1e3bc19ebc68dced1adf20a6966f",
"docs/cli-reference.md": "7fbc82834883a13e91f3a31b058296eefca7117bd7dd08409e44faccc82e1f03",
"docs/cli-reference.md": "4f5e228e914ba1b1a7277e27688288a93cc9fa1dad261fa0511a895663dd6d5d",
"docs/compatibility-v1.md": "7e551aad37a369faff7e370728376763efb54c2c9e9cfd8556a0c19aabc3d103",
"docs/contracts/handoff-v1.md": "46a953010c49214bd8f969412f26713384fbe09a0ba78ebf0fef75da40d0d889",
"docs/diagnostics.md": "0949e53612eeab2cd7c7bbda498e14d71e9cb1b1189aa7722030cb8702cd8481",
Expand Down
Loading
Loading