npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bex-co/bex-security

v0.1.27-bex.1

Published

Agent-portable security scanning CLI and TypeScript SDK built on Codex Security and ACP

Readme

@bex-co/bex-security

Open-source TypeScript SDK and CLI for running evidence-backed Bex Security scans with Codex, Claude Code, Kimi Code, Muse Code, Qwen Code, MiMo Code, and additional ACP agents. The ESM-only package includes TypeScript declarations, the bex-security and codex-security executables, the generated security plugin, and the matching Codex runtime. Both executable names run the same CLI; codex-security remains available for compatibility.

[!NOTE] Versions use upstreamVersion-bex.N, so the upstream Codex Security baseline and each Bex release on that baseline remain visible. The public API may change between minor versions before 1.0.0.

Host-review diagnostics

When an ACP agent cannot delegate workers, Bex manages review assignments and saves diagnostics inside the existing scan output directory once the review inventory has been generated and validated. Inventory/setup failures happen before this assignment-diagnostic stage.

| Artifact | Contents | | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | artifacts/01_context/host-review/inventory.json | Registered review inventory. | | artifacts/01_context/host-review/assignment-N-attempt-M/request.json | Files requested, saved before agent startup. | | artifacts/01_context/host-review/assignment-N-attempt-M/result.json | Attempt phase, completed read evidence, claimed files, response validity, terminal status, unvalidated candidates and safe failure reason. | | artifacts/01_context/host-review/assignment-N.json | Combined assignment outcome, remaining files and number of attempts. | | artifacts/01_context/host-review/incomplete-review.json | Partial coverage, missing files, assignment history and unvalidated candidates after failure or cancellation. | | incomplete-report.md | Readable incomplete-scan summary linking to the diagnostics. |

Complete host reviews still produce review.json for the coordinator. Only the normal validation/reporting workflow produces the canonical report.md. An incomplete summary does not turn failure into success: SDK calls still reject, CLI commands still exit nonzero, and coverage requirements remain unchanged. Candidates from incomplete or failed attempts are evidence for later investigation, not confirmed findings. Repeated candidates may appear in the attempt history.

Requests and terminal results are saved independently so completed attempts survive a later failure. A request without a result has no recorded terminal outcome; it does not prove the model ran or that files were read. Abrupt process termination may prevent the final JSON and Markdown summary from being written. Diagnostic write failures are warnings and do not replace the original scan error or stop an otherwise successful review. SDK users receive these notices through onWarning; the CLI displays them with its existing scan warnings.

Use the existing --output-dir (SDK: outputDir) to select a persistent directory outside the target repository, or keep the existing default persistent scan-state location. An explicit temporary directory can be removed by the operating system; Bex does not back up diagnostics elsewhere. Container users should retain the existing output/state volume. These local files can contain target paths and candidate details; review them before sharing, as with other scan artifacts.

Install

npm install @bex-co/bex-security
npx @bex-co/bex-security --version

Use Node.js 22.13.0+ (22.x), 24.x, or 26.x on macOS, Linux, or Windows. Policy drafting, scans, exports, scan history, and saved findings also need Python 3.10+ (plus tomli on Python 3.10).

To run from a source checkout instead of the published npm package, install both dependency trees before building the bundled plugin and CLI:

git clone https://github.com/bex-co/bex-security.git
cd bex-security
pnpm --dir sdk/typescript install --frozen-lockfile
npm ci --prefix plugins/codex-security/mcp-app --no-audit --no-fund
pnpm --dir sdk/typescript run build:plugin
pnpm --dir sdk/typescript run build
./bex-security scan /path/to/repository --agent claude

Run a scan from TypeScript

Sign in with npx @bex-co/bex-security login or set OPENAI_API_KEY or CODEX_API_KEY. Then create a client and scan a repository you own or have permission to assess:

import { CodexSecurity } from "@bex-co/bex-security";

const security = new CodexSecurity();

try {
  const result = await security.run("/path/to/repository", {
    outputDir: "/path/outside/repository/results",
  });

  console.log(result.reportPath);
  console.log(result.findings.findings.length);
} finally {
  await security.close();
}

result.findings contains this scan's findings; repositoryFindings also includes earlier open findings when available. Matching earlier findings can make extra model calls; see Progress and cost.

Keep results outside the repository and restrict access: reports can contain source code, vulnerability details, and reproduction steps.

Validate an existing finding

const security = new CodexSecurity();
try {
  const result = await security.validate({
    repositoryPath: "/path/to/repository",
    finding: {
      title: "Possible SQL injection",
      location: "src/query.ts:42",
    },
    outputDir: "/path/outside/repository/validation",
  });
  console.log(result.disposition);
  console.log(result.report);
} finally {
  await security.close();
}

Pass literal text or a JSON-serializable object as finding, not a file path. Validation uses the client's settings and credentials without changing repository files or adding a scan to history.

To disable Codex usage analytics and built-in metrics, create the client with new CodexSecurity({ codexOverrides: { analytics: { enabled: false } } }). This setting also applies to scans run by the same client.

Results include disposition (reportable, suppressed, not_applicable, or deferred), a Markdown report, threadId, and evidence outputDir. reportable may rely on static analysis; deferred means insufficient evidence. Failed, incomplete, or malformed responses reject the promise.

outputDir must be empty and outside the Git worktree; it defaults to validations/ under the state directory. Pass auth to select credentials or signal to cancel.

Import GitHub code scanning alerts

Import alerts, including third-party SARIF uploads, and validate them against the matching local checkout:

import {
  CodexSecurity,
  importGitHubCodeScanningAlerts,
} from "@openai/codex-security";

const findings = await importGitHubCodeScanningAlerts({
  repository: "example/repository",
  alertNumbers: [12, 18], // Omit to list all open alerts on the default branch.
  githubToken: process.env["GH_TOKEN"],
});

const security = new CodexSecurity();
try {
  for (const finding of findings) {
    const result = await security.validate({
      repositoryPath: "/path/to/repository",
      finding,
    });
    console.log(finding.url, result.disposition, result.outputDir);
  }
} finally {
  await security.close();
}

Each result contains source, repository, number, url, and the full upstream alert. Import is read-only and does not start Codex or check out code.

Without alertNumbers, state filters alerts and defaults to "open". It also accepts "closed", "dismissed", "fixed", and "all". Exact alert numbers ignore state and reject a nondefault state. Use ref for another branch or pull-request reference.

Supply githubToken or use your gh auth token credentials, including GitHub CLI token environment variables. githubHost defaults to GH_HOST or github.com. The token needs read access to code scanning alerts; access failures reject the import. Pass signal to cancel.

SDK configuration and scan options

Constructor options:

| Option | Description | | ---------------- | ---------------------------------------------------------------------------------- | | agent | Select "codex" (default), "claude", "kimi", "muse", "qwen", or "mimo". | | claudeProvider | Select "zai" or "kimi" to route the Claude agent through that provider. | | pluginPath | Use a Codex Security plugin directory or ZIP instead of the bundled plugin. | | pythonPath | Select the Python interpreter before consulting PYTHON. | | codexOverrides | Deep-merge supported settings into the isolated Codex configuration. |

Options for security.run(repository, options) and security.preflight(repository, options):

| Option | Description | | ------------------------------------------- | ----------------------------------------------------------------------------------- | | auth | Credential source: "auto", "chatgpt", or "api-key". | | safetyIdentifier | Stable hashed end-user ID for model requests; requires API-key authentication. | | target | Repository, repository-relative paths, committed diff, or working-tree diff. | | mode | "standard" or "deep"; deep mode supports repositories and paths. | | knowledgeBasePaths | Architecture documents, security policies, threat models, or directories. | | scanPrompt / scanPromptFile | Additional scan instructions as text or a local file. | | validationPrompt / validationPromptFile | Custom validation instructions as text or a local file; not Deep. | | postScanPrompt / postScanPromptFile | Follow-up instructions as text or a local file. | | outputDir | Artifact directory outside the enclosing Git worktree. | | archiveExisting | Archive existing results in outputDir before scanning. | | maxCostUsd | Stop when estimated model cost exceeds this positive USD amount. | | stopAfterConsecutiveErrors | Stop deep discovery after this many consecutive errors (default: 3). | | maxTimeHours | Deep-scan discovery limit in hours: greater than zero, up to 96. | | failureSeverity | Severity threshold recorded in the recipe; the SDK caller decides how to handle it. | | parentScanId | Parent scan ID for a rerun. | | expectedPluginVersion | Required original plugin version when replaying a scan. | | signal | AbortSignal to cancel a scan. |

Follow scans with onWorkerStatus and onReconnect. onSessionEvent receives saved events with thread IDs and worker numbers. Deep scans can additionally use onDeepProgress for durable independent-review counts: completed, active, and maximum. The maximum is a configured cap, not a percentage denominator. ScanOptions lists all callbacks.

preflight and CLI --dry-run check local inputs without starting Codex or using the network. They don't authenticate, verify model access, resolve Python, inspect the plugin, or run scan-lifecycle callbacks. Dry runs print effective settings. Deep preflight includes all six resolved deep settings and their origins in deepScanSources. Applicable legacy deep configuration is validated during preflight rather than after runtime startup.

ScanSettings is the shared settings type. ScanOptions adds callbacks, cancellation, workflow, and runtime controls. Load the same project file used by scan -c through the SDK:

import { CodexSecurity, loadProjectConfig } from "@openai/codex-security";

const { config, options } = await loadProjectConfig("codex-security.yaml");
await using security = new CodexSecurity(config);
const result = await security.run(repository, options);
if (
  options.failureSeverity !== undefined &&
  result.hasFindingsAtOrAbove(options.failureSeverity)
) {
  process.exitCode = 1;
}

resolveProjectConfig(input, directory?) accepts a typed ProjectConfigInput object with the same snake_case keys as YAML/JSON and returns the same { config, options } pair and an immutable sources map. Resolved context, prompt, and output paths have the AbsolutePath type. loadProjectConfig(file, directory?) resolves the selected file from directory, which defaults to the current directory; paths inside the file are relative to that file. Object paths are relative to the supplied directory. Scope paths remain relative to the selected repository. Neither helper starts a scan, reads prompt contents, or discovers another configuration file. preflight and run apply the existing local checks and remaining legacy deep defaults. Project-file keys follow Codex's configuration convention; SDK options keep their existing camelCase names, and CLI flags keep kebab-case.

Override resolved SDK options with { ...options, maxCostUsd: 5 }, or add callbacks there. Direct SDK prompt-file paths use the current directory; inline text takes precedence over its matching file. Files use the same regular-file protections as the CLI. The SDK records failureSeverity without throwing or changing process status. hasFindingsAtOrAbove() uses the CLI's severity ordering and leaves the findings unchanged.

Authentication

Codex remains the default agent and uses the authentication methods below. To run the same Bex scan workflow through Claude Code instead, use:

npx @bex-co/bex-security scan . --agent claude

Claude Code owns authentication and advertises its available models and reasoning levels over ACP. --auth, --codex, and --safety-identifier are Codex-only options. Claude supports its native provider, Z.AI, and Kimi; other --provider values remain Codex-only. When selecting a native Claude model or reasoning level, pass values advertised by the installed Claude agent with --model and --effort.

To run the Claude ACP agent with GLM through Z.AI:

export ZAI_API_KEY="<your-zai-api-key>"
npx @bex-co/bex-security scan . --agent claude --provider zai

The Z.AI path defaults to glm-5.3[1m]; use --model glm-5.3 (or another GLM model supported by your Z.AI account) to override it. The provider settings and credential apply only to the Claude ACP subprocess. See the Z.AI Claude Code guide for account and model availability details.

To run Kimi models through the Claude ACP agent, set a Kimi API key. This path defaults to kimi-for-coding and accepts --model k3-256k or another model available to the account:

export KIMI_API_KEY="<your-kimi-api-key>"
npx @bex-co/bex-security scan . --agent claude --provider kimi

The provider credential and Anthropic-compatible settings are scoped to the Claude subprocess and are not written to Claude Code configuration. See the Kimi Claude Code guide.

Kimi Code can also run as the ACP agent itself. Install it, put kimi on PATH, and authenticate once:

kimi login
npx @bex-co/bex-security scan . --agent kimi
npx @bex-co/bex-security scan . --agent kimi --model kimi-code/k3-256k --effort high

Native Kimi uses its saved provider configuration and reports its negotiated model and thinking level over ACP. See the Kimi ACP reference.

Muse Code can run through the Bex-maintained @bex-co/muse-code-acp adapter. The adapter ships with this package. Install Muse Code, authenticate it, and select it for the scan:

muse login
npx @bex-co/bex-security scan . --agent muse
npx @bex-co/bex-security scan . --agent muse --model muse-spark-1.2-contributor --effort high

Bex forwards explicit Muse model IDs to the adapter, including when its initial model catalog is deferred. Configuration updates from the running host update the scan's model metadata. Sequential turns in the same thread reuse the ACP connection; review assignments retain separate sessions and close them when the attempt ends. Cancellation and scan completion close owned connections.

Unless XDG_DATA_HOME is explicitly set, registered scans use a persistent Muse data directory under the Bex state directory, keyed by scan ID. This keeps unrelated personal session history out of native startup work. Authentication and Muse configuration paths remain unchanged. Retain the scan's agent data to restore its sessions; switching data directories does not migrate existing sessions. Native history traversal within a large scan still depends on Muse.

Invalid model configuration stops review recovery immediately. Structured startup failures that confirm no submission receive at most two attempts; possibly submitted turns stop with their original error. Smaller-file recovery continues for incomplete read evidence, without relaxing coverage checks.

Muse owns authentication and model selection. Bex passes its stdio security workbench through ACP and uses noninteractive approval handling for automated assignments. When the adapter reports that delegated workers are unavailable, Bex uses capability-selected host review assignments and accepts progress only from completed, non-truncated file reads.

Qwen Code can run through its native ACP mode. Install it, start qwen, and use /auth once to configure a provider:

npm install --global @qwen-code/qwen-code@latest
qwen
npx @bex-co/bex-security scan . --agent qwen
npx @bex-co/bex-security scan . --agent qwen --model qwen3-coder-plus --effort high

Bex starts qwen --acp; Qwen owns authentication and advertises available models and reasoning levels over ACP.

MiMo Code can likewise run through its native ACP mode. Install it and finish its first-run provider setup before scanning:

npm install --global @mimo-ai/cli
mimo
npx @bex-co/bex-security scan . --agent mimo
npx @bex-co/bex-security scan . --agent mimo --model xiaomi/mimo-v2.5-pro --effort high

Bex starts mimo acp and preserves MiMo's current primary agent mode. MiMo publishes reasoning choices as model variants, so --effort selects the matching advertised variant or fails with the available choices.

For local use, sign in with ChatGPT:

npx @bex-co/bex-security login
npx @bex-co/bex-security scan .

Use device authentication on remote or headless machines:

npx @bex-co/bex-security login --device-auth

For CI, set OPENAI_API_KEY or CODEX_API_KEY. To save a key, pass it on stdin:

printenv OPENAI_API_KEY | npx @bex-co/bex-security login --with-api-key

Environment API keys apply to the current command; only login --with-api-key saves them. Pass Codex access tokens on stdin to login --with-access-token. Access-token environment variables are not scan API keys.

SDK callers can select native command authentication through codexOverrides.model_providers.<id>.auth and model_provider (including a selected profile). Scans, comparisons, and deduplication reviews preserve that selection without requiring an API key or replacing it with a stored login. Codex executes the helper and renews its token. Helper paths and relative auth.cwd values resolve from the supplied CODEX_HOME (default ~/.codex), not the source checkout; an absolute auth.cwd is preserved. Comparisons and reviews also honor the selected command provider in that home's config.toml. Configuration is passed to Codex for validation, including profile support.

For other inference providers:

export OPENROUTER_API_KEY="<your-openrouter-api-key>"
npx @bex-co/bex-security scan . --provider openrouter --model anthropic/claude-sonnet-4.5

export FIREWORKS_API_KEY="<your-fireworks-api-key>"
npx @bex-co/bex-security scan . --provider fireworks --model accounts/fireworks/models/qwen3-235b-a22b

export AWS_BEARER_TOKEN_BEDROCK="<your-bedrock-api-key>"
export AWS_REGION="us-east-2"
npx @bex-co/bex-security scan . --provider amazon-bedrock --model openai.gpt-5.6-luna

Bedrock also accepts AWS access keys, profiles, web identity, container credentials, and the default AWS credential chain. Set AWS_REGION and choose a Bedrock model with --model; OpenAI models such as openai.gpt-5.6-luna support --max-cost.

Bedrock scans, including Deep Scan workers, default to model_reasoning_summary = "none" because some Bedrock models reject reasoning.summary. This leaves reasoning effort unchanged. Explicit summary settings in --codex overrides or the selected Codex profile take precedence. For standard scans on older CLI versions, append --codex 'model_reasoning_summary="none"' to your scan command if Bedrock reports that reasoning.summary is unsupported. Deep scans require a CLI version that forwards this setting to workers.

On Windows, set the API key in PowerShell:

$env:OPENAI_API_KEY = "<your-api-key>"
npx @bex-co/bex-security scan C:\code\repository

Check or remove the stored sign-in with npx @bex-co/bex-security login status and npx @bex-co/bex-security logout. Login, logout, scans, validation, patching, and fix verification share a private credential home for stored OpenAI credentials, including custom providers with requires_openai_auth = true: $CODEX_SECURITY_STATE_DIR/codex-home, or $CODEX_HOME/state/plugins/codex-security/codex-home. Keep this credential home outside the target directory and every enclosing Git worktree, including when running a command from a subdirectory. On managed Windows devices, inherited access for SYSTEM and local Administrators is preserved while protecting the home against future changes to its parents. Other users and broad groups are rejected, and PowerShell Constrained Language Mode is supported. Codex carries cli_auth_credentials_store, forced_login_method, and forced_chatgpt_workspace_id from the ambient configuration into this home, including removing settings that are no longer present in the ambient configuration. Each command carries its selected provider into this home. Patching and fix verification also synchronize the ambient home's project-trust decisions and project-root markers, preserving which project configuration Codex loads. They hold the credential-home lock until the app-server thread is ready, then release it before model execution. Managed-device policies still apply. If this home has no credentials, it imports an existing file-based Codex sign-in. Logout disables imports until you log in again.

Finish operations using older versions before upgrading. Runtime preparation holds the credential-home lock through pauses; exit or crash releases it. Compatibility heartbeats protect active locks from older heartbeat-only clients, but those clients can replace a paused client's lock.

Keep .codex-security-scan.sqlite3 between operations; never remove it during an operation. PID reuse can make old PID-only locks appear active and block recovery. Stop all operations using this home before removing an old .codex-security-scan.lock directory manually.

If ChatGPT credentials cannot be refreshed, run login status. Retry if the sign-in recently changed; otherwise run logout, then login.

Interactive scans ask whether to use ChatGPT or an environment API key when both are available. The choice applies to that scan. Noninteractive scans, including CI, JSON output, and dry runs, prefer the API key. Choose with --auth:

npx @bex-co/bex-security scan . --auth chatgpt
npx @bex-co/bex-security scan . --auth api-key

--auth also works with validate, patch, and verify-fix. These commands use the same stored login as scan, including a sign-in created with codex-security login --device-auth:

npx @openai/codex-security patch OCCURRENCE_ID --auth chatgpt
npx @openai/codex-security verify-fix OCCURRENCE_ID --auth api-key

--auth chatgpt ignores environment API keys. --auth api-key requires OPENAI_API_KEY or CODEX_API_KEY. The default is --auth auto; noninteractive commands prefer OPENAI_API_KEY, then CODEX_API_KEY, then stored credentials. Patch follow-up assessment uses the same selection, and scan --patch keeps the scan's choice. Environment API keys do not replace the saved login. The SDK uses the same auth option on run, validate, and preflight. Codex may still need ChatGPT credentials to load workspace-managed policies when using an API key.

Some cybersecurity requests and protected findings require Trusted Access for Cyber approval. Apply or check your access at chatgpt.com/cyber.

Generate a security policy

policy drafts SECURITY.md guidance for future scans. It does not run a vulnerability scan, change application settings, or install the draft in the checkout. It uses the scan runtime and authentication, requesting read-only access to the selected repository or component and required tools. Network access, web search, apps, and MCP servers are disabled. Drafts stay outside the checkout. The host resolves inherited guidance once and includes each checked descendant policy separately. Descendant policy links must stay within the selected component. Inherited and reporting-policy links may also resolve to ancestor SECURITY.md files or the checkout's .github/SECURITY.md and docs/SECURITY.md. The model cannot read sibling components or Git metadata. Policy turns deny access to the resolved Git metadata and markers, including those inside the selected source tree, nested bare repositories, and associated alternate object stores. Policy shell tools inherit only Codex's core environment; custom shell environment settings, login shells, and shell snapshots are disabled for these turns. Knowledge-base text stays with the private review artifacts during generation and is removed afterward.

Known limitation: policy preflight and generation currently fail on Unix directories with non-UTF-8 names.

On macOS, the pinned Codex runtime does not fully enforce write restrictions under /tmp (including /private/tmp). Keep the repository and artifacts outside that tree when read-only enforcement is required. See the upstream sandbox limitation.

npx @openai/codex-security policy .
npx @openai/codex-security policy . --path services/api
npx @openai/codex-security policy . --knowledge-base architecture.md --model gpt-5.6-terra --effort high
npx @openai/codex-security policy . --dry-run --json

The repository defaults to the current directory. --path selects a component, which inherits policies from its Git root, with the closest policy taking precedence. Linked worktrees and initialized submodules use their own roots. Targets and policy links must stay in the selected checkout, outside Git metadata; ancestor links cannot widen a component policy's scope.

For an intentional separate Git directory, set core.worktree to the checkout's absolute path. Use git worktree repair for moved linked worktrees.

Generation uses three Codex stages: describe the system, build a threat model, then draft the policy. The first two documents support review; they are not additional approval steps or policies to install. In a terminal, it asks about facts the source cannot establish and shows the exact diff. If both ChatGPT and API-key credentials are available, it asks which to use; --auth chatgpt or --auth api-key selects one explicitly.

| Invocation | Calls Codex? | Result | | ---------------------------- | ------------ | ----------------------------------------------------------------------- | | policy . | Yes | Ask owner questions, save documents, and preview the draft. | | policy . --headless --json | Yes | Save documents without prompts and return their paths and review notes. | | policy . --format md | Yes | Generate a draft and write its Markdown to stdout. | | policy . --dry-run --json | No | Check local inputs and show the resolved target and settings. |

None of these commands installs SECURITY.md in the repository. Output formats change presentation; they do not turn generation into a saved-draft read.

Review the draft

Review the saved SECURITY.md before copying it to the reported target. Check links from .github/SECURITY.md or docs/SECURITY.md: copying can change their guidance too. Preserve reporting instructions and obtain owner approval for exclusions, accepted risks, and severity decisions. Later scans read this policy.

Generation and preview check for changes to the selected or inherited policies. If governing guidance changes during generation, completed documents remain for inspection, but no completed-draft manifest is written. Other source files are not frozen; regenerate if relevant source or neighboring policies change. A failed terminal preview reports a warning and the saved draft paths. Explicit output formats return the draft directly without running a diff preview.

Use --headless or an explicit output format to skip questions. Unanswered questions remain in the review notes. Drafts default to the Codex Security state directory; --output-dir selects an empty directory outside every enclosing Git checkout and its Git metadata.

npx @openai/codex-security policy . --path services/api \
  --headless --output-dir /path/outside/repository/api-policy --json

The artifact directory contains:

| File | Purpose | | ---------------------- | --------------------------------------------------------- | | SECURITY.md | Editable policy draft. | | THREAT_MODEL.md | Detailed threat model with source references. | | project-spec.md | System description and security boundaries. | | previous-SECURITY.md | Original policy used for the diff. | | policy-draft.json | Target, policy hashes, revision, model, and review notes. |

Keep supporting documents private until reviewed for disclosure. A generated threat scenario is neither owner approval nor a confirmed vulnerability.

--format md writes the draft to stdout. --json returns paths, review notes, status, and estimated cost. Global filters and token options work with these formats. Progress goes to stderr. --full-output reports failures with ok: false. --max-cost applies to the whole generation. If a stage cannot inspect required source evidence, generation stops and preserves completed documents. Fix the reported problem and use a new output directory to retry.

Generate a policy from TypeScript

import { CodexSecurity } from "@openai/codex-security";

const security = new CodexSecurity();
try {
  const draft = await security.generatePolicy("/path/to/repository", {
    path: "services/api",
    knowledgeBasePaths: ["/path/to/architecture.md"],
    onStage: (stage) => console.error(stage),
  });

  console.log(await security.previewPolicy(draft));
  // Open draft.draftPath in an editor to review the saved policy.
} finally {
  await security.close();
}

preflightPolicy() checks local inputs without starting Codex. previewPolicy() previews the supplied in-memory draft, uses the client's Python setting, and makes terminal control characters visible. Editing the saved file does not change that object. The standalone securityPolicyDiff() returns a raw diff for files or other non-terminal uses; pass an interpreter explicitly if needed. generatePolicy() accepts auth, path, knowledgeBasePaths, outputDir, maxCostUsd, signal, and progress and cost callbacks. An optional answerQuestions callback receives each group of up to three owner questions and a cancellation signal. Without it, the questions remain unresolved.

CLI

npx @bex-co/bex-security policy . --path services/api
npx @bex-co/bex-security scan
npx @bex-co/bex-security scan /path/to/repository
npx @bex-co/bex-security scan /path/to/repository --headless
npx @bex-co/bex-security scan /path/to/repository --patch
npx @bex-co/bex-security scan /path/to/repository --patch --patch-severity high --json
npx @bex-co/bex-security scan /path/to/repository --patch --patch-severity high --create-pr
npx @bex-co/bex-security scan /path/to/repository --model gpt-5.6-terra
npx @bex-co/bex-security scan /path/to/repository --model gpt-5.6-terra --effort high
npx @bex-co/bex-security scan /path/to/repository --path src --path tests
npx @bex-co/bex-security scan-components /path/to/repository --component apps/api --component apps/web --output-dir /path/outside/repository/results
npx @bex-co/bex-security scan-components /path/to/repository --auto --output-dir /path/outside/repository/results
npx @bex-co/bex-security scan /path/to/repository --knowledge-base /path/to/threat-models --knowledge-base /path/to/architecture.pdf
npx @bex-co/bex-security scan /path/to/repository --scan-prompt-file scan.md --post-scan-prompt-file follow-up.md
npx @bex-co/bex-security scan /path/to/repository --diff origin/main --json
npx @bex-co/bex-security scan /path/to/repository --output-dir /path/outside/repository/results
npx @bex-co/bex-security scan /path/to/repository --output-dir /path/outside/repository/results --archive-existing
npx @bex-co/bex-security scan /path/to/repository --verbose
npx @bex-co/bex-security scan /path/to/repository --dry-run
npx @bex-co/bex-security scan /path/to/repository --fail-on-severity high
npx @bex-co/bex-security scan /path/to/repository --max-cost 5
npx @bex-co/bex-security scan /path/to/repository --mode deep --workers 2 --subagents 0 --stop-after-no-new 3 --max-discovery-runs 10 --max-time-hours 1.5
npx @bex-co/bex-security install-hook
npx @bex-co/bex-security bulk-scan
npx @bex-co/bex-security bulk-scan --model gpt-5.6-terra --effort high
npx @bex-co/bex-security bulk-scan --workers 4 --mode deep --max-attempts 3 --max-cost 25
npx @bex-co/bex-security bulk-scan repositories.csv --output-dir /path/outside/repositories/security-scans --workers 4 --knowledge-base /path/to/threat-models --knowledge-base /path/to/architecture.pdf
npx @bex-co/bex-security bulk-scan repositories.csv --output-dir /path/outside/repositories/security-scans --scan-prompt-file scan.md --post-scan-prompt-file follow-up.md
npx @bex-co/bex-security scans list /path/to/repository
npx @bex-co/bex-security scans list --scan-root /path/outside/repository/results
npx @bex-co/bex-security scans show
npx @bex-co/bex-security scans show SCAN_ID
npx @bex-co/bex-security scans logs
npx @bex-co/bex-security scans logs SCAN_ID
npx @bex-co/bex-security scans rerun
npx @bex-co/bex-security scans rerun SCAN_ID
npx @bex-co/bex-security scans match PREVIOUS_SCAN_ID CURRENT_SCAN_ID
npx @bex-co/bex-security scans match --all
npx @bex-co/bex-security scans compare
npx @bex-co/bex-security scans compare PREVIOUS_SCAN_ID
npx @bex-co/bex-security scans compare PREVIOUS_SCAN_ID CURRENT_SCAN_ID
npx @bex-co/bex-security findings
npx @bex-co/bex-security findings list /path/to/repository
npx @bex-co/bex-security findings false-positive OCCURRENCE_ID --reason "The route already checks permissions"
npx @bex-co/bex-security export
npx @bex-co/bex-security export /path/outside/repository/results --export-format sarif --output /path/outside/repository/results.sarif
npx @bex-co/bex-security export /path/outside/repository/results --export-format csv --output /path/outside/repository/findings.csv
npx @bex-co/bex-security export /path/outside/repository/results --export-format json --output /path/outside/repository/findings.json
npx @bex-co/bex-security publish scan /path/outside/repository/results --to linear --linear-team TEAM_ID
npx @bex-co/bex-security publish scan --to linear --linear-team TEAM_ID
npx @bex-co/bex-security validate /path/outside/repository/findings.json "Possible SQL injection in src/query.ts:42"
npx @bex-co/bex-security validate "Possible SQL injection" --effort high
npx @bex-co/bex-security verify-fix --linear-issue SEC-123 --json
npx @bex-co/bex-security verify-fix --linear-project "Security backlog" --linear-filter '{"state":{"type":{"eq":"completed"}}}' --json
npx @bex-co/bex-security verify-fix --scan SCAN_ID --severity high --json
npx @bex-co/bex-security patch /path/outside/repository/findings.json "Missing authorization check in src/routes.ts:18"
npx @bex-co/bex-security patch "Missing authorization check" --effort high
npx @bex-co/bex-security patch OCCURRENCE_ID
npx @bex-co/bex-security patch --scan SCAN_ID --severity high --json
npx @bex-co/bex-security patch --scan SCAN_ID --severity high --create-pr
npx @bex-co/bex-security patch --resume-pr codex-security/patch-SCAN_ID
npx @bex-co/bex-security patch --scan latest --severity medium
npx @bex-co/bex-security patch --linear-issue SEC-123 --linear-issue SEC-124
npx @bex-co/bex-security patch --linear-project "Security backlog" --linear-filter '{"labels":{"name":{"eq":"security"}}}'

Run npx @bex-co/bex-security --version for the installed CLI version or npx @bex-co/bex-security info --json for the package, bundled plugin, Codex runtime, default model, reasoning effort, and first-scan command. A scan with --dry-run also reports its effective model and reasoning effort, including --codex overrides, without starting Codex or contacting the network.

Project files

Use scan -c FILE / scan --config FILE to load reusable scan settings:

bex-security scan . -c codex-security.yaml --dry-run --json
bex-security scan . -c codex-security.json --model gpt-5.6-terra
bex-security init
bex-security info -c codex-security.yaml --json

Select one .yaml, .yml, or .json file. scan, bulk-scan, scan-components, and info accept -c. They also accept an operator-set CODEX_SECURITY_PROJECT_CONFIG; an explicit -c wins. Without either, no file is loaded or discovered. The repository still comes from the command's target selection. SDK run() and saved reruns do not load project files automatically.

The selected file is trusted like CLI options and SDK codexOverrides. Native settings can start configured MCP server processes and select model-service destinations. Do not select configuration controlled by an untrusted repository or pull request; keep CI scanner configuration outside the checkout being assessed.

init [file] writes codex-security.yaml by default and never overwrites an existing file. YAML starters show defaults as comments; JSON starters contain the editor schema hint, relative to the chosen file and the invocation directory's local package installation. info reports effective model details and native key sources without dumping raw native values.

# yaml-language-server: $schema=./node_modules/@bex-co/bex-security/schemas/project-config.schema.json
scan:
  mode: standard
  scope:
    paths: [src]
codex:
  model: gpt-5.6-sol
  model_reasoning_effort: xhigh
policy:
  fail_on_severity: high

All settings are optional; {} uses the existing defaults. JSON files can use a root $schema string pointing to the same packaged schema. Schema hints are for editors; the CLI uses its bundled validator without fetching URLs, coercing values, or dropping unknown keys. Native codex settings retain their existing checks and profile semantics. CLI scan --schema --json describes command arguments.

Settings use built-in defaults, applicable legacy deep defaults, the file, then explicit CLI values. Lists and scope variants are replaced. --head can refine a file diff and --base a file working-tree scope. A selected native profile can still override root model/effort values. Existing native alias-conflict checks and the behavior of --provider openai are unchanged.

File context, instruction, validation, and output paths resolve from the file's directory. CLI file paths resolve from the invocation directory; scope paths resolve from the repository. The file cannot select a different repository or enable automatic patching/publication. The loader does not evaluate code, interpolate environment values, include remote files, or merge multiple files.

Dry-run output adds projectConfig.path and projectConfig.sources, selected prompt paths, and the finding policy without dumping raw native configuration. Missing or invalid selected files exit 2. Help, version, and command schema output do not load project files. Existing scan and finding-policy exit codes remain unchanged.

Scan options and output

--path scopes a scan to one or more paths, --diff scans committed changes, and --working-tree scans staged and unstaged changes. Deep scans support repository and path targets.

Bulk scans use clean, shallow checkouts and support repository or path scopes. They reject configured diff or working-tree scopes before starting unless each affected CSV row supplies its own path scope.

Working-tree snapshots include files from untracked nested Git repositories. Initialized submodules must be clean and checked out at the commit recorded by the parent repository.

Repeat --knowledge-base PATH for UTF-8 text files with any extension (including JSON and SARIF), PDF, or Word (.docx) files. Directories are searched recursively, skipping other binary files. Explicitly supplied unsupported binary files are rejected. Bulk scans share these documents with every repository.

Use an empty output directory outside the scanned directory and enclosing Git worktree. On macOS/Linux, existing directories must be private to you (chmod 700). --archive-existing moves previous results to <output-dir>.previous-<timestamp>-<id>; add --dry-run to preview the move. SARIF output, when produced, is at <scan-dir>/exports/results.sarif.

Scans are report-only by default. Set --fail-on-severity high to exit with 1 if a completed scan finds high or critical issues. Incomplete scans exit with 2, writing available results to stdout and a coverage warning to stderr.

Import findings as a saved scan

Import an existing findings CSV or JSON file into local scan history and SQLite:

codex-security scan import --csv /path/to/findings.csv
codex-security scan import --json /path/to/findings.json --format json
codex-security scan import --csv /path/to/findings.csv --dry-run

Supply exactly one of --csv PATH or --json PATH. CSV uses the existing findings CSV template, including the optional candidate_id column. JSON accepts a complete codex-security.findings document or { "findings": [...] }, with each finding matching the existing findings schema. On scan import, --json selects the input file; use --format json for JSON output. Other commands retain their existing --json output flag. The selected input must be a regular file, and its path must not traverse symbolic links or directory junctions. Use the direct filesystem path when the file or a parent directory is linked.

Each import creates one completed scan using the configured CODEX_SECURITY_STATE_DIR. The target is a retained copy of the input dataset, independent of the current repository. Every source occurrence remains a separate finding, including duplicate reports. Original identifiers are preserved in extensions.import; the original file is sealed under artifacts/import/. JSON writeup paths are retained as source metadata without reading external files.

Completion means the import finished. Coverage is unknown and the report states that no security analysis was performed. Importing requires no model calls or authentication. --dry-run validates without saving a scan. --output-dir and --archive-existing control saved output, and scans rerun SCAN_ID reimports the retained input.

Generate mock scan results

Use --mock to populate a Standard scan with synthetic test data in seconds, without Codex authentication or any LLM calls:

codex-security scan /path/to/repository --mock
codex-security scan /path/to/repository --mock --output-dir /path/outside/repository/mock-results

The SDK equivalent is await security.run(repository, { mock: true }). Mock mode is off by default. It uses normal target validation, scan registration, artifact finalization, reports, and local scan/finding history. Output directories, archiving, JSON output, exports, and --fail-on-severity work as usual. --dry-run only validates inputs; --mock saves a completed scan.

Each run contains 12 findings across all severity levels: eight stable findings recur on subsequent scans of the same repository, and four have new identities on every run. Two pairs describe the same root causes with different titles and identities, providing inputs for deduplication testing. Mock scans skip automatic LLM matching; existing identity-based history indexing still runs. Separate comparison or deduplication commands retain their usual model behavior.

Titles, provenance, artifact metadata, and reports identify the results as synthetic. Paths and code snippets are fictional and are never written into the repository. Completion means fixture generation finished, not that the repository was audited. Results enter the selected local state just like other scans; set CODEX_SECURITY_STATE_DIR to a separate directory when creating disposable data. Token usage is zero. scans rerun preserves mock mode.

Mock mode supports repository, path, and diff targets in Standard mode. It cannot be combined with --dry-run, --patch, Deep mode, custom validation, or post-scan prompts. Scan prompts and knowledge-base inputs do not change the fixtures, and mock scans do not offer interactive patching.

Attribute scans to end users

When scanning on behalf of users, pass each user's stable hashed ID:

await security.run("/path/to/repository", {
  auth: "api-key",
  safetyIdentifier: hashedUserId,
});
codex-security scan /path/to/repository --auth api-key --safety-identifier hashed-user-id

Use a nonblank ID of 1 to 64 characters without NUL or personal data such as email addresses. It applies to the scan, workers, retries, and follow-up work without changing shared configuration. Supply it again for reruns.

The runtime needs native --safety-identifier support, and the plugin must forward it to workers. The bundled runtime doesn't support it yet; choose a compatible build with CODEX_CLI_PATH. The SDK checks the ID's format, not runtime or plugin compatibility. Older versions may omit the ID.

Scan project components

scan --path runs one scan across selected paths. To scan each local project component separately (standard mode by default), use scan-components:

npx @bex-co/bex-security scan-components /path/to/project \
  --component apps/api --component apps/web --component packages/shared \
  --workers 4 --output-dir /path/outside/project/results

Use -c FILE to share settings, including scan.mode: deep, context and prompt files, per-scan deep workers, cost limits, and severity policy. Component plans override the file's scope. output.directory supplies the results directory when --output-dir is omitted. A configured severity threshold returns exit 1 after completed scans; failures or incomplete results return 2.

Use --auto instead of --component for a proposed split. Save a plan to review or edit, then run it with a new output directory:

npx @bex-co/bex-security scan-components /path/to/project \
  --auto --plan-only --output-dir /path/outside/project/plan
npx @bex-co/bex-security scan-components /path/to/project \
  --components-file /path/outside/project/plan/components.json \
  --output-dir /path/outside/project/results

For large repositories, automatic planning splits inventories into separate calls that fit Codex's input character limit. It preserves directory boundaries where possible and subdivides oversized packages and flat directories as needed. Each call uses a fresh context and can select only paths within its batch. Omitted files are retained in Other files components within those same boundaries. Large repositories can therefore require more planning calls and produce more components. Review or edit the saved plan before scanning with --components-file.

Components use repository-relative paths:

{
  "components": [
    { "name": "API", "paths": ["apps/api", "packages/auth"] },
    { "name": "Web", "paths": ["apps/web"] }
  ]
}

Automatic planning respects Git ignore rules and groups omitted files under Other files. Each proposed path must contain an inventoried file. Planning leaves source files unchanged.

Each component saves artifacts under component-N/. Combined findings.json merges high-confidence root-cause matches, keeping the highest severity and original IDs. Uncertain matches stay separate. summary.json records coverage and matching status; report.md links to component reports. Export and publish from the individual scan folders, not the combined summary.

Large comparisons use bounded batches that cover every earlier/later finding pair. Overlapping confirmed groups are joined in code. Finding text is not truncated; pairs above Codex's input limit leave matching incomplete.

Use an empty output directory outside the project. Failed components don't stop others, but failures, incomplete coverage, or failed matching exit with 2. Retry failed or incomplete components with --components-file retry-components.json and a new output directory. The retry report covers only those components; it does not update the original combined report.

--max-cost applies per component, excluding planning and matching. --model and --effort also apply to matching; --auth applies throughout. Planning and matching reject an ambient command provider that conflicts with explicit --auth chatgpt or --auth api-key. A command provider explicitly selected through SDK codexOverrides retains its authentication configuration. Use --knowledge-base, --scan-prompt-file, and --post-scan-prompt-file as for bulk scans.

From TypeScript, use runComponentScans({ repository, outputDir, components }). Use auto: true for planning, planOnly: true to save the plan without scans, and scanOptions.auth to select credentials.

Configure deep scans

For scan --mode deep, --workers sets discovery concurrency and --subagents sets subagents per worker. --stop-after-no-new stops after that many runs without new issues. --max-discovery-runs and --max-time-hours cap discovery runs and duration. SDK equivalents:

await security.run("/path/to/repository", {
  mode: "deep",
  workers: 2,
  subagents: 0,
  stopAfterNoNew: 3,
  stopAfterConsecutiveErrors: 2,
  maxDiscoveryRuns: 10,
  maxTimeHours: 1.5,
});

Set defaults in $CODEX_HOME/codex-security/config.toml:

[deep_scan]
workers = 4
subagents = 3
stop_after_no_new = 4
stop_after_consecutive_errors = 3
max_discovery_runs = 40
max_time_hours = 96

CLI and SDK options override these defaults. Project files can use scan.deep.stop_after_consecutive_errors, and SDK calls can use stopAfterConsecutiveErrors; there is no new CLI flag for it. --codex cannot configure this section. Worker and run counts must be positive integers; subagents can be zero. Legacy workers = "auto" means four workers. Unknown keys are rejected.

max_time_hours accepts positive values up to 96, including fractional hours. At the deadline, discovery stops; the scan combines and returns completed findings.

scan --workers controls discovery workers within one deep scan; bulk-scan --workers controls how many repositories are scanned concurrently.

The project-file deep block uses subagents_per_worker for the existing SDK/CLI subagents setting. A valid deep block can remain inactive in standard mode; explicit deep CLI options require deep mode. All six active values are resolved before runtime preparation and saved in new recipes. Complete saved values are independent of later changes to the legacy TOML file.

Runtime configuration and worker limits

Scans use these isolated Codex defaults instead of your user or repository configuration:

approval_policy = "on-request"
approvals_reviewer = "auto_review"
cli_auth_credentials_store = "auto"
model = "gpt-5.6-sol"
model_reasoning_effort = "xhigh"
model_reasoning_summary = "detailed" # "none" for amazon-bedrock
show_raw_agent_reasoning = true

[features]
plugins = true
goals = true

[features.multi_agent_v2]
enabled = true
max_concurrent_threads_per_session = 9

[windows]
sandbox = "unelevated"

Use --model to choose a model and --effort minimal|low|medium|high|xhigh|max for reasoning effort. Repeat --codex KEY=VALUE for other TOML settings:

npx @bex-co/bex-security scan . \
  --model gpt-5.6-terra \
  --effort high \
  --codex features.multi_agent_v2.max_concurrent_threads_per_session=4

The thread limit of 9 includes the parent and up to eight delegated workers. It is separate from deep-scan and bulk-scan worker counts.

Quote string values as TOML, for example --codex 'model_reasoning_effort="high"'. Do not pass both --model and --codex 'model="..."', or both --effort and --codex 'model_reasoning_effort="..."': conflicting or repeated keys are rejected.

Choose plugins with --plugin-path. Overrides of plugins, marketplaces, or features.plugins are rejected, including in profiles. Multi-agent v2 must stay enabled: agents.max_threads and features.multi_agent_v2.enabled=false are rejected.

validate, patch, and verify-fix accept --auth, --effort, and the model, model_reasoning_effort, and analytics.enabled keys in --codex, but no other runtime overrides.

Use --codex 'analytics.enabled=false' to disable Codex usage analytics and built-in metrics for a command:

npx @openai/codex-security validate "Candidate finding" --codex 'analytics.enabled=false'
npx @openai/codex-security patch "Security issue" --codex 'analytics.enabled=false'
npx @openai/codex-security verify-fix "Security issue" --codex 'analytics.enabled=false'

The same setting works for scan and bulk-scan. An explicit setting is preserved when scan --patch starts remediation and when patch --assess-patch-risk starts its follow-up assessment. Boolean true is also accepted; omitting the setting preserves the command's existing configuration and Codex defaults. Validation ignores user configuration. For stored OpenAI credentials, patching and verification read configuration from the shared credential home. API-key commands and custom providers that use their own credentials retain their ambient Codex configuration. Patching and verification preserve project trust from the ambient home; explicit --codex settings apply to the command and its patch-risk assessment.

This setting does not control explicitly configured OpenTelemetry log or trace exporters, authentication, integrations, or CLI update checks.

See Local security model for approval and filesystem restrictions.

Environment variables

| Variable | Effect | | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | OPENAI_API_KEY, CODEX_API_KEY | Scan credentials; OPENAI_API_KEY wins if both are set. | | CODEX_SECURITY_EMBEDDINGS_URL | Findings service endpoint; see Embeddings and storage. | | CODEX_SECURITY_LINEAR_TEAM, CODEX_SECURITY_LINEAR_PROJECT | Default team and project for completed-scan publication. | | CODEX_SECURITY_LINEAR_API_KEY | Personal API key for Linear patching and direct publication. | | CODEX_SECURITY_LOG_LEVEL | CLI-only; debug enables verbose diagnostics. | | LOG_LEVEL | CLI-only fallback when CODEX_SECURITY_LOG_LEVEL is unset. | | CODEX_SECURITY_STATE_DIR | Private scan-history, workbench, and default artifact directory. | | CODEX_SECURITY_PROJECT_CONFIG | Trusted project file for scan, bulk-scan, scan-components, and info; -c wins. Unset by default. | | CODEX_HOME | Ambient Codex home for file-based sign-in and default state; defaults to ~/.codex. | | CODEX_CLI_PATH | Codex executable for authentication, plugin setup, scans, and workers. | | PYTHON | Python interpreter when --python or SDK pythonPath is unset. | | GH_HOST | GitHub Enterprise host for interactive bulk-scan discovery. | | CODEX_SECURITY_NO_UPDATE_NOTICE, NO_UPDATE_NOTIFIER | Either variable disables interactive update notices. | | CODEX_SECURITY_NPM_REGISTRY, npm_config_registry, NPM_CONFIG_REGISTRY | Update-check registry, in precedence order. | | CI | Disables interactive update notices. | | NO_COLOR, TERM | Disables colored scan history when NO_COLOR is defined or TERM=dumb. |

Custom Codex executables need thread source attribution for exec and app-server (Codex 0.149.1+). On Windows, use a native .exe or .com; command shims such as codex.cmd fall back to the bundled executable.

Python lookup order: --python (on scan, bulk-scan, or export) or SDK pythonPath, then PYTHON, the managed Codex runtime, and python3 or python on PATH (py also works on Windows). CODEX_SECURITY_STATE_DIR overrides CODEX_HOME for state storage. Keep state and results outside the repository.

Progress and cost

Interactive scans show full-screen progress; CI, redirected output, and --headless use plain status lines. Results go to stdout, progress and diagnostics to stderr. Add --verbose for diagnostics. Check logs for sensitive information before sharing them.

The token summary shows uncached input, cache reads, cache writes, output, and total tokens. Total tokens include all input plus output; cache reads and writes are subsets of input, not extra tokens. When cache-write usage is missing, the summary shows uncached input and cache writes as unavailable. The final summary preserves missing-data information from a matching session log. If the Codex runtime converts an omitted count to zero before recording it, the CLI cannot distinguish that zero from reported usage.

Cost displays show a range using standard API prices, because runtime usage does not identify which requests received long-context pricing. The minimum assumes short-context pricing; the maximum assumes long-context pricing. These are token-cost estimates for the observed usage, excluding other processing tiers, fees, surcharges, and account-specific pricing.

JSON results, scan history, and bulk-scan receipts preserve cost.estimatedUsdRange: min, max, and context: "unknown". A null maximum means an upper estimate is unavailable, including models without verified long-context rates. cost.pricing records the price source, verification date, processing tier, short-context rates, and verified long-context rates when known. Models without known short-context prices still have no cost estimate.

For compatibility, cacheWriteInputTokens remains the reported token subtotal. cacheWriteInputTokensReported: false means at least one included usage record did not report cache writes. Raw usage uses cache_write_input_tokens_reported. In that case, the range minimum prices unclassified input as ordinary input, and the maximum allows it to be cache writes. Token counts remain unchanged. Older saved records remain readable and display a labeled legacy estimate; they are not repriced using current rates.

For compatibility, cost.estimatedUsd retains the short-context baseline used by existing spending limits. cost.pricing.context: "short" describes that baseline, not observed request contexts. Use estimatedUsdRange for cost reporting. This change does not change when spending limits stop scans.

--max-cost USD stops the scan and its workers when estimated cost exceeds the limit, though in-flight requests can finish above it. If deep-scan discovery has finished, the scan returns a sealed partial report without more model calls and lists unvalidated candidates as follow-up work. Bulk scans apply the limit per repository attempt.

With --max-cost, automatic finding-history matching makes at most one extra model call. If it needs more context, the completed scan is kept and a warning directs you to run scans match --all explicitly.

For a single scan in the interactive dashboard, reaching 80% of the limit offers a higher total USD limit. Enter a larger amount to approve it, or press Enter with an empty input or Escape to keep the current limit. The scan continues running while you decide, and the existing limit remains enforced until the increase is saved. Increases keep the same scan and accumulated cost; they do not restart work or extend time or discovery limits. CI, JSON/JSONL, --headless, and --verbose scans do not offer budget increases. If usage crosses the limit before an increase is approved, the scan still stops.

SDK callers can supply onBudgetApproaching({ maxCostUsd, cost, signal }) and return a higher total limit, or undefined to keep the current limit. The callback runs once per limit at 80% usage without blocking tracking or execution. Its signal aborts when the scan stops or finishes model work; late answers are ignored. Invalid increases or failures to save them leave the existing limit in place and report a warning. onCost(cost, maxCostUsd) reports the current limit, including after an approved increase.

These amounts estimate API-equivalent model usage, not ChatGPT subscription allowance. Post-scan prompts run after scan cost tracking ends and are outside this limit.

Run npx @bex-co/bex-security scan --help or npx @bex-co/bex-security bulk-scan --help for the complete CLI references.

Bulk scans

Sign in with gh auth login, then run npx @bex-co/bex-security bulk-scan to discover GitHub