diff --git a/.github/settings.yml b/.github/settings.yml new file mode 100644 index 0000000..c3a0d12 --- /dev/null +++ b/.github/settings.yml @@ -0,0 +1,23 @@ +branches: + - name: main + protection: + required_pull_request_reviews: + required_approving_review_count: 1 + require_code_owner_reviews: false + dismiss_stale_reviews: true + required_status_checks: + strict: true + checks: + - context: "CI / Test on Node.js 20.x" + - context: "CI / Test on Node.js 22.x" + - context: "CI / Coverage Gate" + - context: "CI / Lint" + - context: "CI / Codex Compatibility Smoke" + - context: "CI / Cross-Platform Smoke (windows-latest)" + - context: "CI / Cross-Platform Smoke (macos-latest)" + - context: "CodeQL / Analyze" + - context: "Secret Scan / Gitleaks" + - context: "Supply Chain / Dependency Review" + - context: "Supply Chain / SCA and License Gate" + enforce_admins: true + restrictions: null diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3c4f0b..3df09be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,12 +6,17 @@ on: pull_request: branches: [main] +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: test: name: Test on Node.js ${{ matrix.node-version }} runs-on: ubuntu-latest strategy: + fail-fast: false matrix: node-version: [20.x, 22.x] @@ -23,7 +28,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - cache: 'npm' + cache: npm - name: Install dependencies run: npm ci @@ -44,15 +49,48 @@ jobs: - name: Run type check run: npm run typecheck - - name: Run tests with coverage - run: npm run coverage - - name: Build run: npm run build + - name: Upload build artifact + if: matrix.node-version == '20.x' + uses: actions/upload-artifact@v4 + with: + name: dist-node20 + path: dist/ + + - name: Run tests + run: npm test + + coverage-gate: + name: Coverage Gate + runs-on: ubuntu-latest + needs: test + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download build artifact + uses: actions/download-artifact@v4 + with: + name: dist-node20 + path: dist/ + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run tests with coverage threshold gate + run: npm run test:coverage + lint: name: Lint - runs-on: ubuntu-latest steps: @@ -63,7 +101,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 20.x - cache: 'npm' + cache: npm - name: Install dependencies run: npm ci @@ -83,10 +121,41 @@ jobs: uses: actions/setup-node@v4 with: node-version: 20.x - cache: 'npm' + cache: npm - name: Install dependencies run: npm ci - name: Run Codex compatibility tests run: npm run test -- test/codex.test.ts test/host-codex-prompt.test.ts test/request-transformer.test.ts test/fetch-helpers.test.ts + + cross-platform-smoke: + name: Cross-Platform Smoke (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run smoke typecheck + run: npm run typecheck + + - name: Build + run: npm run build + + - name: Run smoke tests + run: npm run test -- test/runtime-paths.test.ts test/codex-bin-wrapper.test.ts test/file-lock.test.ts test/background-jobs.test.ts diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml new file mode 100644 index 0000000..c596741 --- /dev/null +++ b/.github/workflows/secret-scan.yml @@ -0,0 +1,28 @@ +name: Secret Scan + +on: + pull_request: + branches: [main] + push: + branches: [main] + schedule: + - cron: "0 5 * * 1" + +jobs: + gitleaks: + name: Gitleaks + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run gitleaks scanner + uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml new file mode 100644 index 0000000..5a461d6 --- /dev/null +++ b/.github/workflows/supply-chain.yml @@ -0,0 +1,81 @@ +name: Supply Chain + +on: + pull_request: + branches: [main] + push: + branches: [main] + schedule: + - cron: "0 4 * * 1" + +env: + CODEX_LICENSE_DENYLIST: "GPL-2.0,GPL-3.0,AGPL-3.0" + +jobs: + dependency-review: + name: Dependency Review + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Dependency review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: high + fail-on-scopes: runtime + deny-licenses: ${{ env.CODEX_LICENSE_DENYLIST }} + + sca-and-license: + name: SCA and License Gate + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run vulnerability policy gate + run: npm run audit:ci + + - name: Run license policy gate + run: npm run license:check + + sbom: + name: Generate SBOM + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Generate CycloneDX SBOM + run: npm run sbom + + - name: Upload SBOM artifact + uses: actions/upload-artifact@v4 + with: + name: sbom-cyclonedx + path: sbom.cdx.json diff --git a/README.md b/README.md index e254c6a..8445939 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ codex auth doctor --fix | `codex auth fix --dry-run` | Preview safe repairs | | `codex auth fix --live --model gpt-5-codex` | Run repairs with live probe model | | `codex auth doctor --fix` | Diagnose and apply safe fixes | +| `codex auth rotate-secrets --json --idempotency-key ` | Re-encrypt stored secrets with safe retry semantics for automation | --- diff --git a/docs/README.md b/docs/README.md index 2accdd9..2cb64b5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -59,6 +59,7 @@ Canonical documentation map for `codex-multi-auth`. | [development/REPOSITORY_SCOPE.md](development/REPOSITORY_SCOPE.md) | Ownership map by repository path | | [development/TESTING.md](development/TESTING.md) | Validation gates and test matrix | | [development/TUI_PARITY_CHECKLIST.md](development/TUI_PARITY_CHECKLIST.md) | Dashboard UX parity checklist | +| [runbooks/README.md](runbooks/README.md) | Operations and incident response playbooks | | [benchmarks/code-edit-format-benchmark.md](benchmarks/code-edit-format-benchmark.md) | Benchmark methodology and outputs | --- diff --git a/docs/configuration.md b/docs/configuration.md index 172296c..6e13083 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -68,6 +68,12 @@ These are safe for most operators and frequently used in day-to-day workflows. | `CODEX_TUI_GLYPHS=ascii|unicode|auto` | Glyph mode selection | | `CODEX_AUTH_FETCH_TIMEOUT_MS=` | HTTP request timeout override | | `CODEX_AUTH_STREAM_STALL_TIMEOUT_MS=` | Stream stall timeout override | +| `CODEX_AUTH_ENCRYPTION_KEY=<32-byte-random-key>` | Enable at-rest encryption for stored account secrets (high-entropy key material only) | +| `CODEX_AUTH_PREVIOUS_ENCRYPTION_KEY=<32-byte-random-key>` | Previous high-entropy key used during staged secret rotation | +| `CODEX_AUTH_ROLE=admin\|operator\|viewer` | CLI authorization role baseline | + +For `CODEX_AUTH_ENCRYPTION_KEY` and `CODEX_AUTH_PREVIOUS_ENCRYPTION_KEY`, use 32-byte +random key material from a secret manager. Do not use user-memorable passwords. --- @@ -81,6 +87,14 @@ Use these only when debugging, controlled benchmarking, or maintainer workflows. - `CODEX_CLI_ACCOUNTS_PATH` - `CODEX_CLI_AUTH_PATH` - refresh lease tuning variables (`CODEX_AUTH_REFRESH_LEASE*`) +- `CODEX_AUTH_BREAK_GLASS` +- `CODEX_AUTH_ABAC_READ_ONLY` +- `CODEX_AUTH_ABAC_DENY_ACTIONS` +- `CODEX_AUTH_ABAC_DENY_COMMANDS` +- `CODEX_AUTH_ABAC_REQUIRE_INTERACTIVE` +- `CODEX_AUTH_ABAC_REQUIRE_IDEMPOTENCY_KEY` +- `CODEX_AUTH_REDACT_JSON_OUTPUT` +- retention tuning variables (`CODEX_AUTH_RETENTION_*`) Full inventory: [development/CONFIG_FIELDS.md](development/CONFIG_FIELDS.md) diff --git a/docs/development/CONFIG_FIELDS.md b/docs/development/CONFIG_FIELDS.md index 9a3ee4c..54350ed 100644 --- a/docs/development/CONFIG_FIELDS.md +++ b/docs/development/CONFIG_FIELDS.md @@ -195,6 +195,21 @@ Used only for host plugin mode through the host runtime config file. | `CODEX_TUI_GLYPHS` | TUI glyph mode | | `CODEX_AUTH_FETCH_TIMEOUT_MS` | Request timeout override | | `CODEX_AUTH_STREAM_STALL_TIMEOUT_MS` | Stream stall timeout override | +| `CODEX_AUTH_ENCRYPTION_KEY` | Primary high-entropy 32-byte key for at-rest secret encryption | +| `CODEX_AUTH_PREVIOUS_ENCRYPTION_KEY` | Previous high-entropy 32-byte key for staged secret rotation | +| `CODEX_AUTH_ROLE` | Authorization role baseline (`admin`, `operator`, `viewer`) | +| `CODEX_AUTH_BREAK_GLASS` | Emergency authorization bypass toggle | +| `CODEX_AUTH_ABAC_READ_ONLY` | Deny mutating actions while allowing read-only command paths | +| `CODEX_AUTH_ABAC_DENY_ACTIONS` | Comma-separated action denies (`accounts:write`, etc.) | +| `CODEX_AUTH_ABAC_DENY_COMMANDS` | Comma-separated command denies (`rotate-secrets`, etc.) | +| `CODEX_AUTH_ABAC_REQUIRE_INTERACTIVE` | Comma-separated actions that require interactive terminal | +| `CODEX_AUTH_ABAC_REQUIRE_IDEMPOTENCY_KEY` | Comma-separated actions that require idempotency key context | +| `CODEX_AUTH_REDACT_JSON_OUTPUT` | Redact sensitive values in JSON command output | +| `CODEX_AUTH_RETENTION_LOG_DAYS` | Log retention window | +| `CODEX_AUTH_RETENTION_CACHE_DAYS` | Cache retention window | +| `CODEX_AUTH_RETENTION_FLAGGED_DAYS` | Flagged-account file retention window | +| `CODEX_AUTH_RETENTION_QUOTA_CACHE_DAYS` | Quota cache retention window | +| `CODEX_AUTH_RETENTION_DLQ_DAYS` | Dead-letter queue retention window | | `CODEX_MULTI_AUTH_SYNC_CODEX_CLI` | Toggle Codex CLI state sync | | `CODEX_MULTI_AUTH_REAL_CODEX_BIN` | Force official Codex binary path | | `CODEX_MULTI_AUTH_BYPASS` | Bypass local auth handling | diff --git a/docs/development/TESTING.md b/docs/development/TESTING.md index 9292b90..6695f74 100644 --- a/docs/development/TESTING.md +++ b/docs/development/TESTING.md @@ -24,6 +24,8 @@ npm run typecheck npm run lint npm test npm run build +npm run audit:ci +npm run license:check ``` Optional: @@ -42,8 +44,19 @@ npm run bench:edit-formats:smoke 1. `npm run typecheck` 2. `npm run lint` 3. `npm test` -4. `npm run build` -5. run docs command checks for newly documented command paths +4. `npm run coverage` +5. `npm run build` +6. `npm run audit:ci` +7. `npm run license:check` +8. run docs command checks for newly documented command paths + +### Upgrade Notes (PR #32) + +- Gate ordering was updated so `npm run coverage` runs before `npm run build`. +- Two required supply-chain checks were added to the standard local sequence: + - `npm run audit:ci` + - `npm run license:check` +- If you maintain local CI wrappers or pre-push scripts, update them to use the order above and rerun once to refresh baselines. * * * diff --git a/docs/index.md b/docs/index.md index 9a59b2d..ecc3dbf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -46,4 +46,5 @@ Legacy package/path guidance is documented in [upgrade.md](upgrade.md) and [refe - Command flags and hotkeys: [reference/commands.md](reference/commands.md) - Settings and overrides: [reference/settings.md](reference/settings.md) - Storage path matrix: [reference/storage-paths.md](reference/storage-paths.md) -- Full docs portal: [README.md](README.md) \ No newline at end of file +- Operations runbooks: [runbooks/README.md](runbooks/README.md) +- Full docs portal: [README.md](README.md) diff --git a/docs/privacy.md b/docs/privacy.md index 4fa1534..4acf565 100644 --- a/docs/privacy.md +++ b/docs/privacy.md @@ -20,6 +20,7 @@ | Accounts | `~/.codex/multi-auth/openai-codex-accounts.json` | Primary saved account pool | | Flagged accounts | `~/.codex/multi-auth/openai-codex-flagged-accounts.json` | Accounts with hard auth failures | | Quota cache | `~/.codex/multi-auth/quota-cache.json` | Cached quota snapshots | +| Background DLQ | `~/.codex/multi-auth/background-job-dlq.jsonl` | Failed background jobs after retry exhaustion | | Logs | `~/.codex/multi-auth/logs/codex-plugin/` | Optional diagnostics | | Prompt/cache files | `~/.codex/multi-auth/cache/` | Cached prompt/template metadata | | Codex CLI state | `~/.codex/accounts.json`, `~/.codex/auth.json` | Official Codex CLI files | @@ -48,6 +49,35 @@ Current external destinations: Raw body logs may contain sensitive payload text. Treat logs as sensitive data and rotate/delete as needed. +`CODEX_AUTH_REDACT_JSON_OUTPUT=1` redacts sensitive values from JSON command output for automation logs. + +--- + +## Secret Encryption and Rotation + +- Account refresh/access tokens can be encrypted at rest when `CODEX_AUTH_ENCRYPTION_KEY` is set. +- Key rotation supports staged migration with `CODEX_AUTH_PREVIOUS_ENCRYPTION_KEY`. +- Both key variables should be 32-byte high-entropy key material (not passwords). +- Rotation command: + +```bash +codex auth rotate-secrets --json +``` + +Store encryption keys in a secret manager or CI secret store, not in repository files. + +--- + +## Retention + +Startup retention cleanup removes expired local artifacts based on: + +- `CODEX_AUTH_RETENTION_LOG_DAYS` +- `CODEX_AUTH_RETENTION_CACHE_DAYS` +- `CODEX_AUTH_RETENTION_FLAGGED_DAYS` +- `CODEX_AUTH_RETENTION_QUOTA_CACHE_DAYS` +- `CODEX_AUTH_RETENTION_DLQ_DAYS` + --- ## Data Cleanup diff --git a/docs/reference/commands.md b/docs/reference/commands.md index 43c877f..4cd736e 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -38,6 +38,7 @@ Compatibility aliases are supported: | `codex auth report` | Generate full health report | | `codex auth fix` | Apply safe account storage fixes | | `codex auth doctor` | Run diagnostics and optional repairs | +| `codex auth rotate-secrets` | Re-encrypt account secrets using current encryption key | --- @@ -45,13 +46,16 @@ Compatibility aliases are supported: | Flag | Applies to | Meaning | | --- | --- | --- | -| `--json` | verify-flagged, forecast, report, fix, doctor | Print machine-readable output | +| `--json` | verify-flagged, forecast, report, fix, doctor, rotate-secrets | Print machine-readable output | | `--live` | forecast, report, fix | Use live probe before decisions/output | | `--dry-run` | verify-flagged, fix, doctor | Preview without writing storage | | `--model ` | forecast, report, fix | Specify model for live probe paths | | `--out ` | report | Write report output to file | | `--fix` | doctor | Apply safe repairs | | `--no-restore` | verify-flagged | Verify only; do not restore healthy flagged accounts | +| `--page-size ` | list, status (`--json`) | Page size for JSON list output (1-200) | +| `--cursor ` | list, status (`--json`) | Cursor token for JSON list pagination | +| `--idempotency-key ` | rotate-secrets | Safe retry key for automation | --- @@ -108,6 +112,7 @@ Repair and recovery: codex auth fix --dry-run codex auth fix --live --model gpt-5-codex codex auth doctor --fix +codex auth rotate-secrets --json --idempotency-key "$CI_JOB_ID" ``` --- diff --git a/docs/reference/error-contracts.md b/docs/reference/error-contracts.md index 62694d4..2196f1a 100644 --- a/docs/reference/error-contracts.md +++ b/docs/reference/error-contracts.md @@ -31,17 +31,24 @@ Examples: The following commands support `--json` and produce pretty-printed JSON objects: +- `codex auth list --json` - `codex auth forecast --json` - `codex auth report --json` - `codex auth fix --json` - `codex auth doctor --json` - `codex auth verify-flagged --json` +- `codex auth rotate-secrets --json` Compatibility guarantees: - Output is valid JSON. - `command` field identifies the command family. +- `schemaVersion` is required for machine-consumable contracts. - Documented top-level sections remain stable unless a migration note is provided. +- Optional redaction mode (`CODEX_AUTH_REDACT_JSON_OUTPUT=1`) masks sensitive fields without changing schema shape. +- Paginated list output uses `pagination.{cursor,nextCursor,hasMore,pageSize}`. + +For `rotate-secrets`, automation may provide `--idempotency-key ` to avoid duplicate side effects on retried runs. --- diff --git a/docs/reference/public-api.md b/docs/reference/public-api.md index 865189f..d916a37 100644 --- a/docs/reference/public-api.md +++ b/docs/reference/public-api.md @@ -63,6 +63,24 @@ Positional signatures are preserved for backward compatibility. --- +## API Standards Baseline + +Where this repository exposes machine-readable command output or future HTTP endpoints, use these defaults: + +- Versioning: + - include `schemaVersion` in JSON command output. + - increment schema version only when contract shape changes. +- Idempotency: + - mutating automation flows should support caller-provided idempotency keys. + - repeated requests with the same idempotency key should not duplicate side effects. +- Pagination: + - list-style payloads should prefer cursor-based pagination (`nextCursor`, `hasMore`) over offset-only paging. + - response envelopes should include stable paging metadata even for empty result sets. + +This project currently applies the versioning baseline to JSON command outputs and documents idempotency/pagination standards for future API expansion. + +--- + ## Semver Guidance - Breaking Tier A change: `MAJOR` diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 1466374..42689b1 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -128,6 +128,12 @@ Common operator overrides: - `CODEX_TUI_GLYPHS` - `CODEX_AUTH_FETCH_TIMEOUT_MS` - `CODEX_AUTH_STREAM_STALL_TIMEOUT_MS` +- `CODEX_AUTH_ENCRYPTION_KEY` +- `CODEX_AUTH_PREVIOUS_ENCRYPTION_KEY` +- `CODEX_AUTH_ROLE` + +Encryption key variables must be high-entropy 32-byte key material (for example, +from a secret manager). Do not use passwords. --- @@ -141,6 +147,14 @@ Maintainer/debug-focused overrides include: - `CODEX_CLI_ACCOUNTS_PATH` - `CODEX_CLI_AUTH_PATH` - refresh lease controls (`CODEX_AUTH_REFRESH_LEASE*`) +- `CODEX_AUTH_BREAK_GLASS` +- `CODEX_AUTH_ABAC_READ_ONLY` +- `CODEX_AUTH_ABAC_DENY_ACTIONS` +- `CODEX_AUTH_ABAC_DENY_COMMANDS` +- `CODEX_AUTH_ABAC_REQUIRE_INTERACTIVE` +- `CODEX_AUTH_ABAC_REQUIRE_IDEMPOTENCY_KEY` +- `CODEX_AUTH_REDACT_JSON_OUTPUT` +- retention controls (`CODEX_AUTH_RETENTION_*`) Full inventory: [../development/CONFIG_FIELDS.md](../development/CONFIG_FIELDS.md) @@ -175,4 +189,4 @@ codex auth forecast --live - [commands.md](commands.md) - [storage-paths.md](storage-paths.md) -- [../configuration.md](../configuration.md) \ No newline at end of file +- [../configuration.md](../configuration.md) diff --git a/docs/reference/storage-paths.md b/docs/reference/storage-paths.md index bae76b8..ca409f8 100644 --- a/docs/reference/storage-paths.md +++ b/docs/reference/storage-paths.md @@ -26,6 +26,7 @@ Override root: | Accounts WAL | `~/.codex/multi-auth/openai-codex-accounts.json.wal` | | Flagged accounts | `~/.codex/multi-auth/openai-codex-flagged-accounts.json` | | Quota cache | `~/.codex/multi-auth/quota-cache.json` | +| Background job DLQ | `~/.codex/multi-auth/background-job-dlq.jsonl` | | Logs | `~/.codex/multi-auth/logs/codex-plugin/` | | Cache | `~/.codex/multi-auth/cache/` | | Codex CLI accounts | `~/.codex/accounts.json` | diff --git a/docs/runbooks/README.md b/docs/runbooks/README.md new file mode 100644 index 0000000..7b7d530 --- /dev/null +++ b/docs/runbooks/README.md @@ -0,0 +1,12 @@ +# Runbooks + +Operational runbooks for `codex-multi-auth`. + +## Runbook Index + +- [operations.md](operations.md): routine operational checks, release gates, and maintenance tasks. +- [incident-response.md](incident-response.md): severity model, containment flow, and post-incident process. + +## Scope + +These runbooks cover plugin-owned local state under `~/.codex/multi-auth` (or `CODEX_MULTI_AUTH_DIR`) and repository-level CI/security controls. diff --git a/docs/runbooks/incident-response.md b/docs/runbooks/incident-response.md new file mode 100644 index 0000000..230f8e2 --- /dev/null +++ b/docs/runbooks/incident-response.md @@ -0,0 +1,99 @@ +# Incident Response Playbook + +Incident response workflow for `codex-multi-auth`. + +--- + +## Severity Levels + +- `SEV-1`: active secret exposure, auth bypass, or broad production outage. +- `SEV-2`: major functionality degraded, high failure rate, or persistent data corruption risk. +- `SEV-3`: contained bug with workaround, no ongoing security impact. + +--- + +## Response Timeline + +### 1. Detect and Declare (0-15 min) + +1. Open an internal incident channel. +2. Assign incident commander and communications lead. +3. Record: + - first detection timestamp + - affected command flows + - impacted storage paths/environment variables + +### 2. Contain (15-60 min) + +1. For credential exposure: + - rotate affected OAuth/session credentials + - set new `CODEX_AUTH_ENCRYPTION_KEY` + - run `codex auth rotate-secrets --idempotency-key ` +2. For unauthorized command execution: + - downgrade role to `CODEX_AUTH_ROLE=viewer` where possible + - enable `CODEX_AUTH_ABAC_READ_ONLY=1` until containment is complete + - deny high-risk commands with `CODEX_AUTH_ABAC_DENY_COMMANDS=rotate-secrets,fix` + - reserve `CODEX_AUTH_BREAK_GLASS=1` for explicit emergency changes +3. For filesystem instability: + - pause mutation commands (`login`, `switch`, `fix`) + - inspect lock files and dead-letter entries + +### 3. Eradicate and Recover (within 24h) + +1. Patch root cause and merge behind required CI checks. +2. Validate: + - `npm run typecheck` + - `npm run lint` + - `npm test` + - `npm run audit:ci` +3. Re-enable normal command paths and monitor audit logs. + +--- + +## Communication Template + +Use this internal status template: + +```text +Incident: +Severity: +Start Time: +Current Status: +Impact: +Mitigation: +Next Update: