|
| 1 | +# v2.1.13-beta.3 |
| 2 | + |
| 3 | +Beta prerelease that lands the Phase 1 correctness/security audit (#499), the new |
| 4 | +`mcodex` launcher + cached statusline (#500), and a round of pre-release |
| 5 | +hardening surfaced by a whole-tree stress test (#503). It carries forward the |
| 6 | +cascade OAuth token-invalidation fix from v2.1.13-beta.2, the multi-workspace |
| 7 | +support from v2.1.13-beta.1, and the pinned-account 503 diagnostic from |
| 8 | +v2.1.13-beta.0. |
| 9 | + |
| 10 | +This is a **prerelease**. Stable `v2.1.13` will land once the issue #486 root |
| 11 | +cause is identified and patched. |
| 12 | + |
| 13 | +## Install |
| 14 | + |
| 15 | +```bash |
| 16 | +npm i -g codex-multi-auth@beta |
| 17 | +``` |
| 18 | + |
| 19 | +## mcodex launcher (#500) |
| 20 | + |
| 21 | +A lightweight launcher and cached multi-auth status display for Codex sessions. |
| 22 | + |
| 23 | +- `mcodex` — launch Codex with a cached status line printed before startup |
| 24 | + (model, reasoning effort, cwd, active account, quota usage, plan, cache age). |
| 25 | +- `mcodex --tmux` — launch inside a tmux session with mouse scrollback. |
| 26 | +- `mcodex --tmux --live-accounts` — add a live `codex-multi-auth list` monitor pane. |
| 27 | +- `mcodex --monitor` — monitor-only mode. |
| 28 | + |
| 29 | +### Status line |
| 30 | + |
| 31 | +- Reads local `quota-cache.json`, `runtime-observability.json`, and account |
| 32 | + storage; never calls OpenAI on launch. |
| 33 | +- Refreshes quota data in the background only when the cache is stale (default |
| 34 | + 10 min, `CODEX_MULTI_AUTH_STATUS_QUOTA_REFRESH_INTERVAL_MS`), behind a lock so |
| 35 | + concurrent launches don't double-refresh. Stale refresh locks recover after |
| 36 | + 10 min. |
| 37 | +- Resolves the **per-project** account pool when `perProjectAccounts` is enabled |
| 38 | + and Codex CLI sync is off (mirrors the runtime's own account scoping), falling |
| 39 | + back to the global pool otherwise. Quota/observability stay global. |
| 40 | +- Toggle with `CODEX_MULTI_AUTH_STATUSLINE=0`. |
| 41 | + |
| 42 | +### Hardening |
| 43 | + |
| 44 | +- `MCODEX_MONITOR_INTERVAL` / `MCODEX_TMUX_HISTORY_LIMIT` are validated as numeric |
| 45 | + before being interpolated into `watch` / tmux commands (no shell injection). |
| 46 | +- `--monitor` / `--live-accounts` fail fast with a clear message when `watch` |
| 47 | + isn't installed instead of spawning a broken pane. |
| 48 | +- The status path resolves `~` correctly on Windows (`path.sep`, not a hardcoded |
| 49 | + `/`), and reads the account pool without blocking the event loop. |
| 50 | + |
| 51 | +## Phase 1 audit remediation (#499) |
| 52 | + |
| 53 | +Security and correctness hardening across the runtime, storage, and prompt layers. |
| 54 | + |
| 55 | +### Security |
| 56 | + |
| 57 | +- **Prompt cache integrity.** Cached Codex instructions are SHA-256 verified; a |
| 58 | + tampered cache is discarded, and a legacy entry with no recorded digest is |
| 59 | + treated as unverified — it is never fast-path served and never drives a |
| 60 | + conditional `304` revalidation (which could otherwise launder un-vetted bytes). |
| 61 | + A full fetch mints the first digest; old bytes are kept only as an offline |
| 62 | + fallback. |
| 63 | +- **Path-traversal defense in recovery.** Stored message/part records are |
| 64 | + validated before their ids are used to build filesystem paths; a |
| 65 | + parseable-but-unsafe id (e.g. `../poison`) or a non-numeric `time.created` is |
| 66 | + quarantined instead of escaping into a traversal read or a `NaN` sort. |
| 67 | +- **Loopback-only egress.** The runtime rotation proxy and local bridge bind |
| 68 | + loopback-only with no opt-out, and never forward inbound client credentials |
| 69 | + (`authorization`, `x-api-key`, `cookie`, `proxy-authorization`) upstream |
| 70 | + alongside the managed token. IPv6 loopback (`::1` / `[::1]`) is normalized |
| 71 | + consistently for both the socket bind and the emitted base URL. |
| 72 | +- **Token/email redaction.** Log, debug-bundle, and status sinks mask tokens and |
| 73 | + emails; the debug bundle redacts the home prefix (case- and |
| 74 | + separator-insensitive on Windows), strips credentials from config values, and |
| 75 | + masks the account id. |
| 76 | + |
| 77 | +### Correctness & resilience |
| 78 | + |
| 79 | +- **No event-loop blocking.** Removed synchronous `Atomics.wait` sleeps from the |
| 80 | + config load path and the logger's directory-creation retry; both now retry |
| 81 | + without freezing the event loop. |
| 82 | +- **Bounded network reads.** Prompt and release-metadata fetches are bounded by |
| 83 | + connect+body timeouts that actually cancel a stalled body. |
| 84 | +- **Windows filesystem resilience.** Account-store WAL/temp writes, temp cleanup, |
| 85 | + backup copy/rename, quota-cache, flagged-store, and export operations retry the |
| 86 | + shared transient-lock taxonomy (EBUSY/EPERM/ENOTEMPTY/EACCES/EAGAIN) so an |
| 87 | + antivirus/indexer/concurrent-reader lock doesn't fail a valid operation or |
| 88 | + strand a secret-bearing temp file. |
| 89 | +- **Atomic, self-healing account store.** Writes go through a checksummed WAL + |
| 90 | + temp-and-rename; a torn write self-heals on the next read. |
| 91 | + |
| 92 | +### CLI |
| 93 | + |
| 94 | +- `codex-multi-auth status` / `list` gained `--json` for machine-readable output, |
| 95 | + with a stable shape whether or not accounts are configured. |
| 96 | + |
| 97 | +## Pre-release hardening (#503) |
| 98 | + |
| 99 | +- Strip inbound `cookie` / `proxy-authorization` on both egress paths. |
| 100 | +- Bound the proxy's upstream error-body read (previously unbounded on 4xx/5xx). |
| 101 | +- Persist `runtime-observability.json` owner-only (`0o600` / dir `0o700`) on POSIX. |
| 102 | +- Bump `vitest` to `^4.1.8` (dev-only) to clear GHSA-5xrq-8626-4rwp. |
| 103 | + |
| 104 | +## Verification |
| 105 | + |
| 106 | +Full test suite (4,200+ tests) green; `npm run audit:ci` clean; typecheck and |
| 107 | +lint pass. |
0 commit comments