Options, defaults, precedence, inspection scope, exit codes and migration. CLI readers need only the sections up to Severity, reporters, CLI and exits; library and extension contracts start at Library use.
Save siro.config.json in the directory you pass to lint. It is automatically
read as data, without executing repository code or requiring a local siro dependency:
{
"exclude": ["test/fixtures", "vendor", "dist"],
"rules": {
"files-field": "warn",
"provenance": "error",
"store-server": "off"
}
}Disable an advisory only after reviewing your policy.
Rules run at their built-in severities unless overridden. Allowed values are
'error', 'warn', 'info', and 'off'. Off disables the check, not just its
output. Unknown rule IDs are errors. Overrides apply to all selected directories;
--severity separately controls display and failure thresholds.
| Field | Value and purpose |
|---|---|
exclude |
Directory patterns; defaults to []. |
installationRoots |
Paths or { path, pm?, pmVersion? } entries; defaults to ['.']. |
rules |
Map of built-in or registered custom rule IDs to severity or 'off'. |
pms |
Nonempty array restricting cwd's PM selection; does not force detection. |
pmVersions |
Map such as { npm: '12.0.2' }; declares versions for cwd. |
projectType |
'application' or 'package'; overrides inference for all selected manifests. |
customRules |
JS/TS only: custom rules; run only at cwd. |
reporters |
JS/TS only: { name, format } reporters; can replace built-ins by name. |
For TypeScript completion, first install siro in the project with
npm install --save-dev --save-exact @pho9ubenaa/siro, then use siro.config.ts:
import { defineConfig } from '@pho9ubenaa/siro';
export default defineConfig({
rules: { provenance: 'error' },
});Run npx @pho9ubenaa/siro lint --config ./siro.config.ts to explicitly execute this
trusted code. An npx temporary installation does not make imports from your
repository's config resolvable. Without a local dependency, use JSON or an import-free
.mjs file selected with --config.
siro lint [cwd] discovers package.json and strict deno.json recursively,
including cwd. PM workspace declarations and exclusions do not select packages
or stop traversal. Use siro's exclude option to omit directories.
Each selected manifest receives applicable publication checks: files-field,
npm-format publish-access, and manifest setting-availability checks. Generic
checks run once per manifest, even with multiple PM targets. Private or unnamed
packages are normally applications. Deno's name and publish: false are judged
independently of npm metadata; parent privacy never propagates to children.
An explicit projectType applies to all selected manifests, with API/CLI options
overriding config.
Discovery does not inspect every package's installation policy. By default, only cwd receives installation checks. List other independently installed projects explicitly; siro does not infer them from lockfiles or PM declarations. For example, adapt these paths to existing directories in your repository:
{
"exclude": ["test/fixtures", "vendor", "dist"],
"installationRoots": [
".",
"tools/standalone",
{ "path": "tools/no-detection-signal", "pm": "npm", "pmVersion": "12.0.2" }
]
}npx @pho9ubenaa/siro lint . --installation-root . --installation-root tools/standalone- API arrays replace their config values. Repeated CLI
--excludeor--installation-rootvalues likewise replace the corresponding config array. - Naming a child does not automatically retain
..installationRoots: []disables installation checks while keeping manifest checks; use config or API to supply an empty array or per-root PM/version objects. - Installation paths are literal, cwd-relative directories, not globs. They must match exact spelling, including case on all hosts. Missing, excluded, symlinked or always-skipped directories are rejected. Normalized duplicates run once; conflicting duplicate options are errors.
- The
.entry cannot contain PM options; use root API/CLI options or configpms/pmVersionsinstead.
Each installation root uses its own local settings, not inherited settings.
A root without a manifest can receive installation checks if its PM is known.
provenance runs only for publishable packages at installation roots:
manifest-only children do not receive provenance-policy evaluation. Review the
returned inspection record to see the selected inputs.
Exclusions use one case-sensitive dialect on every OS:
- Use
/,*,?, and whole-component**. Braces, character classes and extglob punctuation are literal. Backslash patterns are rejected. test/fixturesandtest/fixtures/**both exclude that directory and its entire subtree before files are read.exclude: ['**']keeps only cwd.- cwd cannot be excluded with
.. Leading!reinclusion, absolute paths, parent traversal (..), NUL and patterns exceeding matcher limits are errors. .gitandnode_modulesdirectories are always skipped. Other directories, including dot directories,dist,vendorand fixtures, have no implicit exclusions..gitignore, tracked-file lists and PM workspace exclusions are not consulted.
Directory symlinks below cwd are not followed. cwd itself and file symlinks use normal native filesystem resolution; this is not a containment sandbox. Native filename spelling is preserved in results, including POSIX backslashes. See output paths before consuming them.
Selected malformed manifests, unsupported JSONC-only directories and read errors
fail the entire scan. If both deno.json and deno.jsonc exist, strict deno.json
takes precedence. Exclude intentionally broken fixtures. JSON/YAML roots must be
objects; empty YAML is accepted, but empty JSON is invalid. Consumed manifest
fields are type-checked even when publication checks are disabled; unknown fields
are not whole-schema validated. Lockfile checks establish file presence, not git
tracking, content validity or freshness.
Other PM configuration files are inspected only when needed by enabled checks. Missing optional PM configuration is treated as empty; missing lockfiles normally produce findings, not parse errors. Empty YAML/TOML/INI is accepted. This is not a whole-repository syntax audit. Parser diagnostics omit input excerpts; observed finding values are not redacted.
For a data-only inspection, use:
npx @pho9ubenaa/siro lint --no-config --strict-filesystem--strict-filesystem (rejectSymlinks: true in the API) rejects a symlink selected
as cwd and file/path symlinks below cwd, including dangling links. Symlinks above
cwd are allowed; directory symlinks below cwd are not followed. The option applies
to native data reads, including JSON siro settings, not executable config or extension
code, and not to injected filesystems, which are trusted code. It is not an atomic
containment guarantee; see the threat model for what still requires
an isolated snapshot.
Each inspection has finite caller-controlled budgets. They are not config keys:
API limits key / CLI flag |
Default |
|---|---|
maxFileBytes / --max-file-bytes |
8 MiB (8388608) |
maxTotalBytes / --max-total-bytes |
64 MiB (67108864) |
maxEntries / --max-entries |
100000 |
maxDirectories / --max-directories |
10000 |
maxDirectoryDepth / --max-directory-depth |
128 (cwd depth 0) |
maxConfigDepth / --max-config-depth |
128 (mapping/array root depth 1) |
maxFindings / --max-findings |
50000 before severity filtering |
maxOutputBytes / --max-output-bytes |
32 MiB (33554432), including line endings |
All overrides must be positive safe integers. Defaults are exported as
DEFAULT_SCAN_LIMITS; API callers can provide a partial limits object.
Budgets apply per scan, and cover input bytes and decoded UTF-8 text. Configuration
depth also counts fields no check reads, so deep-but-irrelevant manifest nesting can
still overflow. Injected filesystems must bound their own reads and allocations.
Input/evaluation overflow aborts with exit 2, without a partial success document.
Output overflow is exit 70; JSON fails instead of emitting a partial document, while
GitHub output may already contain annotations. No findings are silently truncated.
Direct built-in reporter calls use defaults, or supplied context.limits. Limits do
not sandbox trusted extensions or impose a hard CPU timeout. Isolate synchronous API
work in a subprocess/container when hard time/memory bounds are needed.
When aggregate budgets are exceeded, reduce the scan scope; scan independent projects separately where possible. Changing the target directory can change configuration and root-level checks, so splitting a shared workspace is not equivalent to checking it as a whole. Individual file-size and configuration-depth overflows instead require smaller files or simpler nesting.
Do not modify inputs during a scan; a consistent filesystem snapshot is not guaranteed.
Targets describe your intended policy; siro does not run or download a PM binary.
| Evaluation directory | PM selection | Version precedence |
|---|---|---|
| cwd | API pm / --pm, otherwise local detection; config pms restricts selection |
API pmVersion / --pm-version → config pmVersions[pm] → local exact packageManager |
| Additional installation root | Entry pm, otherwise local detection |
Entry pmVersion → local exact packageManager |
| Other discovered package | Its own manifest declaration; Deno format identifies Deno | Its own exact stable declaration only |
Manifest targets are filtered for their format: Deno for deno.json, other PMs
for package.json. Local detection combines packageManager, lockfiles and
manager-specific configuration presence. .npmrc alone identifies no PM.
pms restricts selection rather than forcing managers; version maps do not select
managers. Root options never propagate to additional roots or child manifests.
A publication-only package without a known PM still receives generic checks.
An empty target list or a target without a version means availability was not
assessed, not that its settings are safe. Installation roots and active custom
rules require a detectable or explicit PM. Without installation or active custom
checks, cwd's target can be unknown. Root PM options remain usable with
installationRoots: [] without re-enabling installation checks.
Explicit versions must be exact stable SemVer; pmVersion requires pm.
Build metadata, including Corepack hashes, is accepted. Ranges, prereleases,
tags, partial versions and v prefixes are rejected in explicit options/config.
Such manifest declarations leave the version unknown while retaining name detection
where possible.
unsupported-settings checks only the listed setting/file pairs.
Unknown targets, unlisted settings, later removals, backports and version-specific
value grammars are outside this rule. Known too-old targets receive manual upgrade
guidance rather than unsupported automatic remedies.
Checks use local files, not environment variables, user/global configuration, install/publish commands or workspace inheritance. PM-specific precedence and exceptions are described in policy and sources, including npm provenance, Deno release-age fallback and npm shrinkwrap compatibility.
The CLI automatically reads only cwd's siro.config.json, not parent, child or
additional-root siro settings. JSON takes precedence over coexisting executable
config, which remains unexecuted. Invalid JSON settings fail with exit 2; there is
no fallback to another configuration.
Use strict JSON without comments. Only the data fields above and built-in rule IDs
are accepted. customRules, reporters, module references and config inheritance
are not supported in JSON, even when selected explicitly with --config <path>.
An explicitly selected file replaces automatic configuration; files are not merged.
JSON reads share the CLI's file/total-byte budgets with inspection and honor
--max-config-depth and --strict-filesystem. In strict mode, an explicitly selected
JSON file must be inside the lint target. --no-config skips JSON settings too.
Data-only settings can still disable checks or exclude inputs; do not trust an
unreviewed configuration as your security policy.
Executable .ts, .mjs, and .js settings require --config <path>. Paths are
relative to the shell's working directory, not the lint target. Only the selected
file is loaded; settings are not merged with other files. Config runs with the
caller's privileges; see the threat model. TypeScript must use
Node-supported erasable syntax. This option is trust, not a sandbox.
When JSON is absent and --config is omitted, finding cwd's siro.config.ts,
.mjs, or .js produces a migration error (exit 2) without executing it. Move data settings to
siro.config.json, or explicitly select trusted code. Parents and child/additional-root
configs are not loaded. --no-config skips probing and loading all repository
configuration, ignoring its settings and extensions. It cannot be combined with
--config. CLI options and built-in defaults still apply; unknown custom reporter
names are errors.
Export a config object synchronously, using object literals for configuration and setting maps. Unknown keys, unknown or duplicate rule IDs, and malformed extensions are errors. All selected targets share cwd's rule overrides and reporter registration.
Install siro as a project dependency before importing it. lint is synchronous;
lintCommand evaluates and reports asynchronously. Neither implicitly loads
repository configuration. loadConfig(cwd) reads JSON automatically, but refuses
existing executable config if JSON is absent. To execute trusted configuration,
provide loadConfig(cwd, { configPath: 'siro.config.mjs' }); API paths are relative
to cwd. Changes to imported modules may require restarting the process.
loadConfig accepts caller-controlled limits for JSON reads and nesting; separate
API calls to load and lint have separate budgets.
This example assumes tools/standalone is an existing independent install project:
import { asAbsPath, lint, lintCommand, loadConfig, nodeIO } from '@pho9ubenaa/siro';
const cwd = asAbsPath(process.cwd());
const config = await loadConfig(cwd); // Reads JSON settings, never auto-executes code.
const options = {
cwd,
config,
exclude: ['test/fixtures'],
installationRoots: ['.', 'tools/standalone'],
};
const result = lint(options); // Returns findings without reporting.
const exitCode = await lintCommand({ ...options, reporter: 'json' }, nodeIO); // Evaluates again.Custom checks and reporters belong in config.customRules and config.reporters.
Use the public @pho9ubenaa/siro entry point; internal modules are not supported APIs.
The optional fs implements the exported FileSystem interface:
readDirectories(directory)is required. Return an array of immediate child directory names, not paths, and omit symlinked directories. There is no fallback to the host filesystem.readText(path)returns text orundefinedfor absence;exists(path)returns whether a regular file exists. File symlinks are accepted.- Only ENOENT means absence. Access errors and non-file entries must throw.
Use defineRule for PM-specific checks or requireConfigKey for a scalar setting.
A check receives RuleContext, including optional pmVersion and readConfig(file).
Return ok, na, a violation, or a nonempty violations group. Violations contain
message and optional file, severity, actual, expected, and remediation.
Invalid or async results are configuration errors. Rules run only at cwd.
Rule file paths are context-relative; reported file and operation paths are cwd-relative. A missing violation file uses the binding's file, if any. See JSON output for remediation shapes and path handling.
Use supported PM names and an object literal for requireConfigKey.bindings.
Its optional accept and applies functions must return a boolean synchronously.
Invalid names or return values are configuration errors.
For requireConfigKey, documentedDefault may reduce omitted-setting severity
only with defaultSafety: 'unconditional' and a default satisfying the requirement.
Omitted safety or 'conditional' keeps configured severity. defaultSatisfiedSeverity
is 'info' unless specified, and may be 'off'. A severity override cannot restore
a finding the check never emitted. VersionNote is descriptive, not a policy switch.
User rule overrides take precedence over result, binding and rule severity.
By default all findings are displayed but only errors fail. --severity changes
both thresholds. Summary counts displayed findings; filtering does not change
inspection. A reporter cannot change the already computed findings exit code,
but reporting failures reject the command. For example, enforce-strict-ssl is warn-level
by default: use --severity warn to make detected TLS policy violations fail CI.
Rule severities and command thresholds are not vulnerability severity ratings.
Built-in reporters are pretty, json and github; registered reporters may
replace them by name. Reporters receive format(result, io, { cwd }) and must be
awaited, including direct calls. GitHub annotation files are absolute paths based
on this scan cwd; API/JSON paths remain cwd-relative.
IO.stdout / stderr may return promises. Await direct writes and handle rejection.
Reporters must finish their writes before returning. lintCommand waits for reporting
to finish; output failure rejects the API and exits the CLI with 70, not the
findings exit 1.
check aliases lint. Value options must be nonempty; only --exclude and
--installation-root repeat. Boolean flags take no values. --no-config is an
explicit option, not support for arbitrary --no- variants; it and
--strict-filesystem cannot be repeated.
Help takes priority over version, and version over linting. Arguments after --
are rejected. Run npx @pho9ubenaa/siro lint --help for CLI syntax.
| Exit | Meaning |
|---|---|
| 0 | No findings meet the failure threshold |
| 1 | Findings meet the threshold |
| 2 | Invalid options/config/results or filesystem access; inspection incomplete |
| 70 | Unexpected exception, including output, custom-rule or reporter failure |
- Remove
--workspaces/workspaces, evenworkspaces: false. Discovery is now recursive; PM exclusions no longer hide fixtures or vendor/dist packages. Add siroexcludepatterns;['**']keeps only cwd. - Add independent
installationRootsdeliberately; discovery does not add them. - Stop relying on root PM/version inheritance or duplicated generic PM findings.
- Consume schema 3: optional finding PM, required directory and inspection scope; all finding and operation paths are cwd-relative.
- Injected filesystems must implement
readDirectories;resolveDirectoryis removed. - Custom
requireConfigKeydefaults withoutdefaultSafetyare conservative. - Direct reporter calls require context and awaiting:
await githubReporter.format(result, io, { cwd }). Two-argument custom implementations may ignore context, but callers must supply it. IO.stdout/stderr, includingnodeIO, may now return promises. Await direct writes and handle rejection. Finish reporter writes before returning.