Use scan [repository] -c FILE or scan [repository] --config FILE to load one
YAML or JSON file:
codex-security scan . -c codex-security.yaml --dry-run --json
codex-security scan . -c codex-security.json --model gpt-5.6-terraThe supported extensions are .yaml, .yml, and .json. scan, bulk-scan,
scan-components, and info accept -c / --config. An operator can set
CODEX_SECURITY_PROJECT_CONFIG instead; an explicit -c takes precedence. With
neither, no file is loaded, even if codex-security.yaml exists. SDK run() calls
and saved reruns do not read this environment variable or discover project files.
The repository comes from the command's target selection, not the config file.
Treat the selected file as trusted operator configuration, with the same
authority as CLI options and SDK codexOverrides. Native Codex settings can
start configured MCP server processes and select model-service destinations. Do not select a
file controlled by an untrusted repository or pull request. In CI, keep the
scanner configuration in an operator-controlled location outside the checkout
being assessed. Explicit selection does not make a file safe to trust.
Create a starter and inspect its settings without a repository or runtime:
codex-security init
codex-security info -c codex-security.yaml --jsoninit [file] defaults to codex-security.yaml, refuses to overwrite an existing
file, and accepts .yaml, .yml, or .json. YAML starters show current defaults
as comments so future releases can still update defaults you have not overridden.
The editor hint is relative to the chosen file and expects the package to be
installed in the invocation directory's node_modules.
For a project with a src directory:
scan:
scope:
paths: [src]
knowledge_base: [SECURITY.md, docs/architecture.md]
codex:
model: gpt-5.6-sol
model_reasoning_effort: xhigh
policy:
fail_on_severity: highAll settings are optional; {} uses the existing defaults. No version field
is needed. Unknown wrapper keys and invalid types are errors. Values are literal:
the loader does not evaluate JavaScript, interpolate environment values, include remote files,
or merge multiple files. Wrapper null values do not reset settings. Project-file
keys use snake_case, matching native Codex configuration. Keys inside codex
keep their native spelling; names and values are not converted.
YAML anchors are supported; the parser retains its guard against excessive nested
alias expansion.
For Daybreak on Codex 0.163.0 or later, set codex.daybreak to true; the toggle
defaults off. This native setting is retained in scan recipes and passed to
Deep Scan discovery and reducer workers, including resumes. An explicit
scan.cyber_access_program takes precedence for each turn. See
Daybreak opt-in.
The YAML example and equivalent JSON example select this repository's TypeScript source. After setting up the source checkout, try:
cd sdk/typescript
pnpm run build:plugin
pnpm run build
cd ../..
node sdk/typescript/bin/codex-security.mjs scan . -c docs/examples/codex-security.yaml --dry-run --json| Field | Meaning | Default |
|---|---|---|
auth |
Credential source: auto, chatgpt, or api-key; never a credential value |
auto |
scan.mode |
standard or deep |
standard |
scan.max_time_minutes |
Advisory Standard investigation allowance; enforced Deep discovery limit, in minutes | Unset in Standard; 5760 in Deep |
scan.cyber_access_program |
Experimental OpenAI program: standard, daybreak_blue, or daybreak_red |
Unset; preserve Codex defaults |
scan.scope |
Exactly one of paths: [src], diff: {base: HEAD}, or working_tree: {} |
Whole repository |
scan.knowledge_base |
Context files or directories | Empty list |
scan.instructions_file |
Additional scan instructions | Unset |
scan.validation_file |
Custom validation instructions; incompatible with active deep mode | Built-in validation |
scan.deep |
Deep discovery settings shown below | Existing deep defaults |
codex |
Native Codex settings and profiles | Existing isolated configuration |
limits.max_cost_usd_per_scan |
Estimated USD limit for one scan attempt | No limit |
policy.fail_on_severity |
Exit threshold: critical, high, medium, or low |
Report-only |
output.directory |
Artifact directory outside the scanned Git worktree | Existing private artifact location |
The file configures scan settings. Patching, PR creation, publication, post-scan actions, and machine-specific plugin or Python selection remain explicit CLI/SDK inputs.
The SDK's ScanSettings type is shared by ScanOptions, CLI resolution, and
project-file resolution. Project files and typed ProjectConfigInput objects
use snake_case; SDK options keep camelCase, and CLI flags keep kebab-case.
The resolver maps file keys to the existing SDK options:
| Project file | SDK option | CLI flag |
|---|---|---|
auth |
auth |
--auth |
scan.mode |
mode |
--mode |
scan.cyber_access_program |
cyberAccessProgram |
--cyber-access-program (scan, scan-components) |
scan.scope.paths |
target: ["src"] |
--path |
scan.scope.diff |
target: DiffTarget.refs(...) |
--diff, --head |
scan.scope.working_tree |
target: DiffTarget.workingTree(...) |
--working-tree, --base |
scan.knowledge_base |
knowledgeBasePaths |
--knowledge-base |
scan.instructions_file |
scanPromptFile |
--scan-prompt-file |
scan.validation_file |
validationPromptFile |
--validation-prompt-file |
scan.deep.workers |
workers |
--workers |
scan.deep.subagents_per_worker |
subagents |
--subagents |
scan.deep.stop_after_no_new |
stopAfterNoNew |
--stop-after-no-new |
scan.deep.stop_after_consecutive_errors |
stopAfterConsecutiveErrors |
No flag |
scan.deep.max_discovery_runs |
maxDiscoveryRuns |
--max-discovery-runs |
scan.deep.max_time_hours |
maxTimeHours |
--max-time-hours |
scan.max_time_minutes |
maxTimeMinutes |
--max-time |
limits.max_cost_usd_per_scan |
maxCostUsd |
--max-cost |
policy.fail_on_severity |
failureSeverity |
--fail-on-severity |
output.directory |
outputDir |
--output-dir |
codex |
Constructor codexOverrides |
--codex, model/provider flags |
Use an explicit file with loadProjectConfig():
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;
}For configuration already in memory, pass the same structured object to
resolveProjectConfig():
import {
resolveProjectConfig,
type ProjectConfigInput,
} from "@openai/codex-security";
const input = {
scan: { mode: "deep", scope: { paths: ["src"] } },
limits: { max_cost_usd_per_scan: 5 },
} satisfies ProjectConfigInput;
const { config, options } = resolveProjectConfig(input, process.cwd());Both return constructor config, scan options, and an immutable sources map;
the file loader also returns
projectConfig path and source metadata. The optional directory argument defaults
to the current directory. It locates a selected file or anchors an in-memory
object's context, prompt, and output paths. Files anchor those paths to their own
directory. Neither helper reads prompt contents or prepares a runtime.
Resolved context, prompt, and output paths have the SDK's AbsolutePath type;
scope paths remain relative to the selected repository.
Use security.preflight(repository, options) for the same local checks as CLI
--dry-run, including active-mode compatibility and remaining legacy deep
defaults. To override a resolved value or attach a callback, pass
{ ...options, maxCostUsd: 10, onProgress } to run().
SDK callers can use inline scanPrompt, validationPrompt, and postScanPrompt,
or the corresponding *PromptFile options. Inline text wins without reading the
matching file. Direct SDK file paths resolve from the current directory and use
the CLI's existing file protections. Post-scan instructions remain explicit
SDK/CLI options and are not part of project files.
failureSeverity records the policy in the scan recipe. The SDK does not throw or
set process status when the threshold is met; call hasFindingsAtOrAbove() and
choose the caller's response. The method uses the same ordering as the CLI and
does not filter findings. The CLI/file contract keeps its four reportable levels;
existing SDK calls also accept informational. An unknown threshold throws,
including when the result has no findings.
Settings apply in this order: built-in defaults, applicable legacy deep settings, the project file, then explicitly supplied CLI values. Schema defaults are editor hints; parsing does not insert them. Lists are replaced, not concatenated.
| Path | Relative to |
|---|---|
| Repository positional argument | Invocation directory |
File scan.scope.paths |
Selected repository |
| File context, instruction, validation, and output paths | Configuration file's directory |
| CLI context, prompt, and output paths | Invocation directory |
Native values under codex |
Existing native Codex rules |
Scope paths follow scan --path: the same config can select src in each target
repository. Context, instruction, validation, and output paths belong to the config
file and therefore stay anchored to its directory when the invocation moves.
For example, --knowledge-base context.md replaces the file's entire context list
and resolves from the invocation directory. Existing regular-file, protected-path,
credential, and outside-worktree output checks still apply.
A CLI scope selector replaces the file's scope variant: --diff HEAD discards
configured paths, and --path src discards a configured diff. --head can refine a
file diff; --base can refine a file working-tree scope. Contradictory explicit
selectors fail. --no-working-tree disables a configured working-tree scope but
does not clear a path or committed-diff scope.
There is no general CLI reset for configured context, policy, cost limit, or scope.
Edit the file, select another file, or omit -c and unset
CODEX_SECURITY_PROJECT_CONFIG. An empty context list is valid.
Native objects merge using the existing configuration code. Duplicate native CLI
assignments remain errors; overriding a file value is valid. Selected native
profiles can still override root model/effort values, including convenience flags.
--provider openai retains its existing behavior and does not clear a native
provider selected by the file.
scan:
mode: deep
max_time_minutes: 5760
deep:
workers: 4
subagents_per_worker: 3
stop_after_no_new: 4
stop_after_consecutive_errors: 3
max_discovery_runs: 40
limits:
max_cost_usd_per_scan: 10These deep settings show the existing defaults, shared with the Python plugin.
The cost limit is illustrative. Legacy user [deep_scan] TOML remains supported,
including workers = "auto". File and CLI values override individual settings.
All six effective values are resolved before runtime preparation and saved in new
recipes.
If a runtime aliases the ambient TOML file, only explicit overrides are written
back; unrelated sections and inherited defaults remain untouched. Isolated runtime
files still receive a complete snapshot.
A valid deep block can stay inactive in standard mode. Explicit deep CLI options still require deep mode. Deep diff scans and custom validation remain unsupported. Counts retain their existing bounds; zero subagents is valid, and discovery time cannot exceed 96 hours.
scan.max_time_minutes accepts positive fractional minutes: an optional advisory
allowance in Standard, or the enforced discovery limit in Deep (default and
maximum: 5760 minutes). See investigation time
for lifecycle semantics. scan.deep.max_time_hours and legacy TOML
deep_scan.max_time_hours remain deprecated Deep-only aliases.
Explicit CLI values override project values across units; project values override legacy TOML. Only the winning source's duration is used: conflicting minute/hour aliases there are errors, while equivalent durations are accepted.
max_cost_usd_per_scan has the same meaning as --max-cost: an estimated limit for one
scan attempt. In-flight work may exceed it. It is not a total budget for a batch or
follow-up actions. fail_on_severity changes the exit status without filtering the
retained findings.
codex-security bulk-scan repositories.csv -c codex-security.yaml --output-dir ../batch-results
codex-security scan-components . --component src -c codex-security.yaml --output-dir ../component-resultsoutput.directory can supply the batch output directory; --output-dir overrides
it. In the interactive bulk wizard, it supplies the proposed output directory.
Both commands use the same config precedence, path anchoring, native settings,
context, prompts, per-attempt cost and investigation limits, and severity policy
as scan. The investigation allowance applies independently to each scan, not
planning, result matching, or total campaign time.
A bulk CSV row's mode and scope override the file's defaults for that repository.
Bulk checkouts are clean, shallow snapshots, so bulk scans accept repository or
path scopes. A configured diff or working-tree scope is rejected before work
starts unless every affected row supplies its own CSV path scope. Deep settings
apply only to deep rows. Component plans select each component's
scope, overriding scan.scope; scan.mode selects standard or deep component
scans. Batch --workers controls concurrent repositories or components, while
scan.deep.workers controls discovery workers within each deep scan.
The severity policy returns exit 1 without discarding completed results or
retrying a scan just because it found issues. Execution failures take precedence
with exit 2. Bulk resume retains the saved policy outcome and checks
that the selected scan configuration still matches the campaign.
info [-c FILE] --json reports resolved settings and their sources without a scan
target, prompt-file reads, credentials checks, or runtime initialization. With no
file, it shows defaults. Active deep mode also resolves legacy TOML. It reports
effective model details and native key sources without dumping raw native values.
This is configuration inspection,
not a filesystem, target, or model-availability check.
scan ... --dry-run --json checks local inputs without starting Codex, verifying
credentials, or establishing model availability. Removing --dry-run starts a
scan and may incur model charges. Deep preflight shows all six effective settings
and deepScanSources, including when no project file was selected; invalid
applicable legacy settings now fail before runtime startup.
With -c, the output also includes projectConfig.path, per-setting sources,
selected instruction/validation file paths, and the finding policy. Native sources
identify which layer supplied a key; profile selection still determines the
effective model and effort. Raw native configuration and credentials are not dumped.
Source paths use the project-file spelling, such as
scan.deep.stop_after_no_new; existing output properties such as deepScanSources
keep their names.
The source map includes all wrapper settings, including unset values attributed
to default; resolving preflight sources does not mutate previously returned maps.
Help, version, and command schema output do not load project files. Missing,
malformed, or invalid selected files exit 2. Scan exit codes remain 0 for
completion without a policy failure, 1 for the finding threshold, and 2 for
execution or required-output failures. Signal cancellation retains 130 for
SIGINT and 143 for SIGTERM.
The generated project schema is self-contained Draft-07 and ships in the npm package. A project-root YAML file can use:
# yaml-language-server: $schema=./node_modules/@openai/codex-security/schemas/project-config.schema.json
{}JSON files can use a root $schema string with the same relative path. The CLI
uses its bundled schema and never fetches the hint. Validation does not coerce
values, insert defaults, or discard unknown wrapper keys. Completion inside
codex covers common model/provider fields; other native JSON settings retain
existing checks. CLI scan --schema --json and result artifact schemas remain
separate contracts.
New recipes retain resolved native configuration, scope, auth choice, finding policy, cost limit, context paths, and all active deep settings. Reruns do not load the project file again. Complete saved deep settings do not use the current legacy file; older partial recipes continue using applicable defaults for missing values.
Recipes do not snapshot source or context contents. Reruns use the current checkout
and current context files. Reruns require replacement scan instructions when the
original scan used them: a new recipe records that requirement, and scans rerun
refuses to omit them silently.
Use scans rerun [SCAN_ID] --scan-prompt-file FILE to supply them again. A required
replacement must not be empty. The file resolves from the invocation directory.
Custom validation keeps its existing scans rerun --validation-prompt-file FILE
requirement.
scans resume SCAN_ID and eligible bulk-scan --recover attempts continue the
original session with its saved instructions, authentication choice, and deep
settings. They do not reload the project file or require replacement instructions.
Bulk recovery still checks the selected configuration against the campaign manifest
and applies its severity policy to recovered findings.
Workflow identity records explicitly requested deep settings, not ambient values or shipped defaults, so changing those defaults does not prevent resumption. Changing the explicit request still requires a different workflow ID.