On this page
JSON and SARIF output
Every command that takes --json prints one JSON document on stdout and nothing on stderr when it runs. When it cannot run (a directory that does not exist, a policy that breaks its schema, a lock that cannot be read), it prints the usual error on stderr and nothing on stdout, with exit code 2 for wrong input and 1 otherwise, so a reader should check the exit code before parsing. Each document starts with schemaVersion, which grows only when a change would break a reader: a field removed, renamed or given another meaning. New fields can appear without a new version, so readers should ignore fields they do not know. The JSON Schemas are in the package under schemas/ and at https://unpkg.com/agentcurfew/schemas/<name>.json; the tests check the real output of each command against them.
No output ever holds the content of a scanned file or a secret in full. Text that comes from scanned files (names, messages) has control and invisible characters written as \u escapes.
| Command | Schema | Version |
|---|---|---|
scan --json | schemas/scan.json | 3 |
scan --inventory --json | schemas/inventory.json | 1 |
verify --json | schemas/verify.json | 1 |
doctor --json | schemas/doctor.json | 1 |
scan --sarif | SARIF 2.1.0 | 2.1.0 |
Every document also has producer: name (agentcurfew), version and knowledge, the date of the newest fact in the knowledge base. It holds no time, so two runs on the same project give the same document.
Component ids. Each component has id, stable within its kind and file. For most kinds it is the name: the key of an MCP server, the dotted path of a permission or setting, the name of a file or skill folder. A hook's name is its position (PreToolUse#0), which shifts when a hook is added before it, so its id is its event, its matcher (* when it has none) and the first 10 hex digits of a sha256 over the handler with its secrets redacted, as in PreToolUse:Bash:3f2a9c1e07; an editor task without a label has task: and the same kind of hash. Two identical handlers of one event get ~2, ~3 in order. A matcher or event longer than 64 characters, or with spaces or invisible characters, is written as m- or e- and 8 hex digits of its hash. An id never confirms a secret: a change of a secret's value alone keeps it.
scan --json (version 3)
failOn: the threshold in effect, from--fail-on, the policy or the defaulthigh.components: what was scanned:kind(skill,mcp-server,hook,auto-run,context-file,permission,subagent,command,command-setting,setting),id,agents,scope(project,user,managed),file(relative to the project, or starting with~/),name,bytes,hash(the checksumagentcurfew.lockholds for it), andfiles(path,bytes): for a skill its files, relative to its folder; for a hook, a task or a command setting the scripts of the project it runs, withfilesRootprojectbecause those paths are relative to the project.findings:rule,level(critical,high,medium,low,info),kind,file,name,componentId(theidof the component, absent for a finding about the whole project with no component),message, and when knownlineandcolumn(1-based, columns in code points),tag(a kind of hit within a rule, such asnetwork) andpolicy(the setting ofagentcurfew.policy.jsonthat set the level).suppressed: findings accepted by an exception of the policy:finding,reason,until.problems: places the scanner could not fully read:code(outside-project,too-large,too-deep,too-many-entries,unreadable,unparsable,not-followed) andfile.too-large,too-deep,too-many-entriesandunreadablefailscanandverify.depcurfew: present whenmcp-packagedid not check the packages of MCP servers:status(missing,unsupported,no-policy,invalid-policy),reasonandservers, the number of servers it would have checked.
Version 2 had no suppressed, tag or policy; version 1 is the inventory.
scan --inventory --json (version 1)
components and problems as above, without rules.
verify --json (version 1)
ok: true when the project matchesagentcurfew.lock.changes:type(added,removed,changed),kind,file,name,id,details(what changed, with secrets redacted). Components are matched byidwhen the lock has ids, so a hook added before others is oneaddedchange; an edited hook, whoseidchanges, is matched by its place and reported aschanged.lockDigest: the sha256 of theagentcurfew.lockthatagentcurfew lockwould write now, to compare states without the lock itself.lockWithoutIds:truewhen the lock was written by agentcurfew 1.0, before ids; it is then compared by name, and the text output asks for oneagentcurfew lock.unknownKinds: the number of components of kinds this version does not know, present only when there are any: a newer agentcurfew wrote the lock, and those components were skipped, not compared. The rest is compared as usual.skillsLock: findings of the comparison withskills-lock.json, which never changeok.problems: as in scan.
doctor --json (version 1)
failOn,ok.checks:id,title,status(passed,failed,not applicable),fix, andhitswithfile,message,level(error,warning,info) andsources(https addresses of the facts behind the finding).
scan --sarif
SARIF 2.1.0 for GitHub Code Scanning and other tools:
tool.driver.ruleslists every rule with its description, fix, a link todocs/rules.md, tagssecurityandexternal/owasp-ast/astNN, andsecurity-severity(critical 9.5, high 7.5, medium 5.0, low 3.0, info 1.0).level: critical and high areerror, mediumwarning, low and infonote; the agentcurfew level is kept inproperties.level.- Locations are relative to the project with
uriBaseId%SRCROOT%; columns are code points (columnKindunicodeCodePoints). partialFingerprints["agentcurfew/v1"]is a hash of rule, kind, file, component name and message, so a finding keeps its identity when lines move; it is the same as in 1.0, so existing alerts in GitHub Code Scanning are kept.partialFingerprints["agentcurfew/v2"]uses the component'sidinstead of its name, so a finding of a hook also keeps its identity when a hook is added before it.properties.componentIdholds theid.- Findings accepted by the policy are included with a
suppressionsentry (kindexternal, the exception's reason asjustification). - No time stamps and no absolute paths, so the same project gives the same file.