verifyOS-cli is an AI agent-friendly, pure Rust CLI for scanning Apple app bundles (like .ipa, .app, Info.plist, and Mach-O binaries) for App Store rejection risks before submission. It is built for developers, vibecoders, and automation workflows that want fast, local feedback before App Store Connect becomes the bottleneck.
The App Store Connect validation step is historically a "black box" that costs developers hours of waiting. By shifting validation to your local machine, CI runner, or AI agent loop, verifyOS-cli helps teams catch rejection risks early and produce structured output an agent can use to patch issues faster. Unlike Apple's toolchain (codesign, otool), this tool is built entirely in Rust.
- Acts as a local static analysis orchestrator for iOS/macOS apps.
- Ruleset metadata: Every finding includes
rule_id,severity, andcategory(Privacy, Entitlements, Metadata, etc.). - Privacy Manifests: Checks for missing
PrivacyInfo.xcprivacy. - Permissions (Info.plist): Uses a heuristic Mach-O scan to infer which
NS*UsageDescriptionkeys are required, then validates presence and non-empty values. - LSApplicationQueriesSchemes: Audits custom URL scheme allowlists for duplicates, invalid entries, and potential private schemes.
- UIRequiredDeviceCapabilities: Flags capabilities that don't match observed binary usage.
- ATS: Flags overly broad ATS exceptions (global allows, includes subdomains, insecure TLS settings).
- Bundle leakage: Fails if sensitive files (e.g.,
.p12,.pem,.mobileprovision,.env) are found inside the app bundle. - Versioning: Ensures
CFBundleShortVersionStringandCFBundleVersionare valid and present. - Extension entitlements: Validates extension entitlements are a subset of the host app and required keys exist for common extension types.
- Privacy SDK cross-check: Warns if common SDK signatures are detected but PrivacyInfo.xcprivacy lacks declarations.
- Entitlements: Detects debug-only entitlements (like
get-task-allow=true) and flags mismatches between app entitlements andembedded.mobileprovision(APNs, keychain groups, iCloud containers). - Signing: Ensures embedded frameworks/extensions are signed with the same Team ID as the app binary.
- CI-friendly reports: Outputs
table,json, orsarifwith evidence and remediation recommendations. - Bundle size insights:
voc analyze-sizehighlights the largest files and category hotspots in.ipaor.appbundles. - Editor diagnostics:
voc lsppowers a VS Code extension with a clean Action Center sidebar soInfo.plistandPrivacyInfo.xcprivacyfindings show up in the Problems pane without leaving the editor.
cargo install verifyos-cliThis installs the voc binary for the CLI.
This repo also includes a VS Code extension in editors/vscode. The extension starts voc lsp instead of re-implementing rules in TypeScript, so the Rust CLI stays the single source of truth for diagnostics. It also adds a small sidebar Action Center for scans, handoff generation, output access, and language-server status. Marketplace builds can bundle prebuilt voc binaries for zero-config startup on supported platforms.
For local development:
cd editors/vscode
npm ci
npm run compileFor packaging and publishing:
npm run packagebuilds a.vsix.github/workflows/vscode-extension.ymlpackages the extension on tags, bundles prebuiltvocbinaries for macOS/Linux/Windows, and can publish to the VS Code Marketplace and Open VSX when credentials are configured
Download the pre-built binary for your platform:
# macOS (Apple Silicon)
curl -L https://github.com/0xBoji/verifyOS/releases/latest/download/verifyos-cli-macos-arm64 -o voc && chmod +x voc
# macOS (Intel)
curl -L https://github.com/0xBoji/verifyOS/releases/latest/download/verifyos-cli-macos-amd64 -o voc && chmod +x voc
# Linux (amd64)
curl -L https://github.com/0xBoji/verifyOS/releases/latest/download/verifyos-cli-linux-amd64 -o voc && chmod +x vocMove it to your PATH to use it globally:
mv voc /usr/local/bin/Run the CLI tool against your .ipa or .app path:
voc --app path/to/YourApp.ipaInclude a project file for deeper analysis:
voc --app path/to/YourApp.ipa --project path/to/YourProject.xcodeproj
# or
voc --app path/to/YourApp.ipa --project path/to/YourWorkspace.xcworkspaceBootstrap an AGENTS.md file for AI agent workflows:
voc initIf AGENTS.md already exists, voc init preserves your custom content and replaces only the managed verifyos-cli block.
Run a smaller core ruleset or the full scan:
voc --app path/to/YourApp.ipa --profile basicvoc --app path/to/YourApp.ipa --profile fullfull is the default if --profile is omitted.
Control when the CLI exits with code 1:
voc --app path/to/YourApp.ipa --fail-on offvoc --app path/to/YourApp.ipa --fail-on warningerror is the default if --fail-on is omitted.
Run only specific rules or exclude noisy ones by rule ID:
voc --app path/to/YourApp.ipa --include RULE_PRIVATE_API,RULE_ATS_AUDITvoc --app path/to/YourApp.ipa --exclude RULE_PRIVATE_APISelectors apply after the chosen --profile, so basic plus --include can narrow the set even further.
List available rules and their default profile membership:
voc --list-rulesMachine-readable inventory for agents and CI:
voc --list-rules --format jsonInspect one rule in detail:
voc --show-rule RULE_PRIVATE_API
voc --show-rule RULE_PRIVATE_API --format jsonIf verifyos.toml exists in the current working directory, voc will load it automatically. You can also point to a specific config file:
voc --app path/to/YourApp.ipa --config verifyos.tomlExample config:
format = "table"
profile = "full"
fail_on = "error"
timings = "off"
include = []
exclude = []
[init]
output_dir = ".verifyos"
write_commands = true
shell_script = true
fix_prompt = true
profile = "basic"
[doctor]
output_dir = ".verifyos"
fix = true
repair = ["pr-comment"]
freshness_against = "report.json"
plan_out = ".verifyos/repair-plan.md"
profile = "basic"
open_pr_brief = true
open_pr_comment = trueTop-level keys apply to normal voc --app ... scans. [init] and [doctor] let you keep agent-workflow defaults in one place so you do not have to repeat --output-dir, --profile, or PR handoff flags every run.
CLI flags still override config file values.
Generate or refresh an AGENTS.md playbook in the current directory:
voc initWrite to a custom path:
voc init --path docs/AGENTS.mdUse one root directory for generated init assets:
voc init --output-dir .verifyos --from-scan path/to/YourApp.ipaScan an app first and inject the current failing rules into AGENTS.md:
voc init --from-scan path/to/YourApp.ipaUse a lighter profile when you only want a quick playbook refresh:
voc init --from-scan path/to/YourApp.ipa --profile basicKeep only new or regressed risks relative to an older report:
voc init --from-scan path/to/YourApp.ipa --baseline old-report.jsonRefresh AGENTS.md and generate an agent bundle in one step:
voc init --from-scan path/to/YourApp.ipa --agent-pack-dir .verifyos-agentAlso inject copy-paste follow-up commands for the next agent loop:
voc init --from-scan path/to/YourApp.ipa --agent-pack-dir .verifyos-agent --write-commandsGenerate a runnable follow-up script too:
voc init --from-scan path/to/YourApp.ipa --agent-pack-dir .verifyos-agent --write-commands --shell-scriptGenerate a dedicated AI handoff prompt too:
voc init --output-dir .verifyos --from-scan path/to/YourApp.ipa --fix-promptThe generated block includes:
- a recommended
vocworkflow for quick and release scans - AI agent fix-loop rules
- a live rule inventory with
rule_id, category, severity, and default profiles - an optional
Current Project Riskssection with priority order and suggested fix scopes from the latest scan - an optional pointer to
agent-pack.jsonandagent-pack.mdwhen--agent-pack-diris used - an optional
Next Commandssection with exact re-scan and report refresh commands when--write-commandsis used - an optional
.verifyos-agent/next-steps.shwhen--shell-scriptis used - an optional
fix-prompt.mdwhen--fix-promptis used - an optional
repair-plan.mdpointer so fix prompts and PR handoff docs share the same repair source
When --baseline is provided with --from-scan, the Current Project Risks section only keeps findings that are new or regressed compared with the older JSON report. That keeps the playbook focused on what changed in the current branch.
voc init uses a managed block, so you can safely keep your own notes above or below it.
Run a quick self-check on the current setup:
voc doctorCheck an output root created by voc init --output-dir:
voc doctor --output-dir .verifyosShow a repair plan first without rewriting anything:
voc doctor --output-dir .verifyos --repair pr-comment --plan --format jsonWrite the same preview as a Markdown handoff file:
voc doctor --output-dir .verifyos --from-scan path/to/YourApp.ipa --repair pr-comment --plan --plan-out .verifyos/repair-plan.mdWhen --plan is paired with --from-scan, the JSON output also includes plan_context so agents can see:
- whether the preview is based on
fresh-scanorexisting-assets - the exact scan artifact path
- the baseline path in play, if any
- the freshness source used for stale-asset checks
- the effective repair targets
Repair a broken or missing local agent setup in place:
voc doctor --output-dir .verifyos --fixRepair only selected outputs:
voc doctor --output-dir .verifyos --fix --repair pr-comment
voc doctor --output-dir .verifyos --fix --repair agent-bundleRepair and refresh the setup from a fresh scan:
voc doctor --output-dir .verifyos --fix --from-scan path/to/YourApp.ipa --profile basicGenerate a PR-ready brief alongside the refreshed agent assets:
voc doctor --output-dir .verifyos --fix --from-scan path/to/YourApp.ipa --profile basic --open-pr-briefGenerate a shorter PR comment draft for sticky comments or manual updates:
voc doctor --output-dir .verifyos --fix --from-scan path/to/YourApp.ipa --profile basic --open-pr-commentUse a specific report file as the freshness source:
voc doctor --output-dir .verifyos --freshness-against report.jsonvoc doctor validates:
- config parsing
AGENTS.mdpresence- referenced agent assets like
agent-pack.json,agent-pack.md, andnext-steps.sh - whether generated agent assets look stale compared with the newest
report.jsonorreport.sarifin the output root, or a file passed through--freshness-against - sample
voccommands insideAGENTS.md next-steps.shcommand health, including whether follow-up flags like--open-pr-briefand--open-pr-commentstill match the managed block
When --fix is enabled, voc doctor will:
- create or refresh
AGENTS.md - recreate
.verifyos-agent/agent-pack.json - recreate
.verifyos-agent/agent-pack.md - recreate
.verifyos-agent/next-steps.sh - recreate
fix-prompt.md - repair the managed
verifyos-cliblock so its pointers line up with the chosen output root again
--repair lets you scope that work to just the targets you want: agents, agent-bundle, fix-prompt, pr-brief, or pr-comment.
--plan adds a repair preview so you can see which files would be rebuilt before you run a write operation.
When --fix --from-scan is enabled, voc doctor does the same repair work but uses a fresh app scan to repopulate:
Current Project Risksagent-pack.jsonagent-pack.mdfix-prompt.md- cross-links between
fix-prompt.md,repair-plan.md,pr-brief.md, andpr-comment.md next-steps.sh- follow-up commands that point back to the scanned artifact
When --open-pr-brief is added, voc doctor also writes pr-brief.md with:
- a concise risk summary
- current findings in patch order
- target files and patch hints
- validation commands for the next review loop
- a pointer back to
repair-plan.md
When --open-pr-comment is added, voc doctor also writes pr-comment.md with:
- a shorter review summary for GitHub PR comments
- top risks only
- quick validation commands
If you want the full agent bundle in one clearer command, use:
voc handoff --output-dir .verifyos --from-scan path/to/YourApp.ipa --profile basicThis wraps the common handoff flow and refreshes:
AGENTS.mdhandoff.jsonfix-prompt.mdrepair-plan.mdpr-brief.mdpr-comment.md.verifyos-agent/agent-pack.json.verifyos-agent/agent-pack.md.verifyos-agent/next-steps.sh
You can also combine --baseline old-report.json with --fix --from-scan to keep only new or regressed risks in the repaired setup.
Inspect the biggest contributors to IPA/app bundle size:
voc analyze-size --app path/to/YourApp.ipaJSON output for CI or automation:
voc analyze-size --app path/to/YourApp.ipa --format json --top 15The current size analysis reports:
- total bundle size
- top largest files
- category breakdowns for
framework,extension,binary,asset,metadata, andresource
This repo ships a reusable workflow at .github/workflows/voc-analysis.yml for CI and PR review flows.
Manual run from the Actions tab:
Workflow: voc Analysis
Inputs:
- app_path
- baseline_path (optional)
- profile
- fail_on
- output_dir
- comment_on_pr
- pr_number (optional)
Reusable workflow example:
name: App review
on:
pull_request:
branches: ["main"]
jobs:
voc:
uses: 0xBoji/verifyOS/.github/workflows/voc-analysis.yml@main
with:
app_path: path/to/YourApp.ipa
baseline_path: baseline.json
profile: full
fail_on: error
output_dir: .verifyos-ci
doctor_repair: pr-comment
comment_on_pr: true
comment_mode: sticky
comment_plan_path: .verifyos-ci/repair-plan.md
pr_number: ${{ github.event.pull_request.number }}The workflow generates and uploads:
report.sarifAGENTS.mdfix-prompt.mdrepair-plan.mdpr-brief.mdpr-comment.mddoctor.json.verifyos-agent/agent-pack.json.verifyos-agent/agent-pack.md.verifyos-agent/next-steps.sh
When comment_on_pr is enabled and a PR number is available, the workflow also updates a sticky PR comment from pr-comment.md when present, with a safe fallback to an inline summary if that file is missing.
doctor_repair lets the workflow scope voc doctor --fix to specific outputs such as pr-comment or agent-bundle. comment_mode controls whether voc pr-comment emits a sticky marker (sticky) or a plain body (plain).
If the workflow inputs are left empty and verifyos.toml exists, voc-analysis.yml will also read:
[ci]
doctor_repair = ["pr-comment"]
comment_mode = "sticky"Those values act as repository defaults for the reusable workflow.
You can build the same sticky body locally or in custom CI steps with:
voc pr-comment --output-dir .verifyos-ci --from-plan --scan-exit 1 --doctor-exit 0 --sticky-marker
voc pr-comment --from-plan --plan-path /tmp/repair-plan.md --sticky-markerTable (default):
voc --app path/to/YourApp.ipa --format tableJSON:
voc --app path/to/YourApp.ipa --format json > report.jsonSARIF (for GitHub code scanning, etc.):
voc --app path/to/YourApp.ipa --format sarif > report.sarifMarkdown report (agent-friendly):
voc --app path/to/YourApp.ipa --md-out report.mdAgent fix pack:
voc --app path/to/YourApp.ipa --agent-pack fixes.jsonThe agent pack writes a machine-readable JSON file with failing findings only, including rule_id, message, evidence, recommendation, priority, suggested_fix_scope, target_files, patch_hint, and why_it_fails_review.
Markdown agent pack:
voc --app path/to/YourApp.ipa --agent-pack fixes.md --agent-pack-format markdownBundle agent pack:
voc --app path/to/YourApp.ipa --agent-pack .verifyos-agent --agent-pack-format bundle--agent-pack-format supports json, markdown, and bundle. bundle writes both agent-pack.json and agent-pack.md, with the Markdown output grouped by suggested_fix_scope so AI agents and humans can work from the same fix queue.
The extra patch-hint fields are designed so an AI agent can jump directly to likely edit targets such as Info.plist, PrivacyInfo.xcprivacy, entitlements, or bundled SDK/resources without guessing first.
Timing summary:
voc --app path/to/YourApp.ipa --timingsFull timing details:
voc --app path/to/YourApp.ipa --timings full--timings by itself defaults to summary, which prints total scan time, slowest rules, and cache activity without adding a per-rule time column. Use --timings full when you want the table and markdown outputs to include per-rule execution times too.
JSON and Markdown reports still carry timing data for automation and profiling.
The timing summary also highlights the slowest rules so you can spot hot paths quickly.
It also includes cache hit/miss activity for artifact scans so we can tell whether a slow run is coming from repeated IO or genuinely expensive rules.
JSON and SARIF outputs now expose machine-readable perf metadata too, including slow_rules, total_duration_ms, and cache telemetry.
Suppress existing findings by providing a baseline JSON report. Only new failing findings will be shown:
voc --app path/to/YourApp.ipa --format json > baseline.json
voc --app path/to/YourApp.ipa --baseline baseline.jsonBaseline matching currently uses rule_id + evidence for failing findings.
Analysis complete!
╭────────────────────────────────────────┬─────────────┬──────────┬────────┬─────────╮
│ Rule ┆ Category ┆ Severity ┆ Status ┆ Message │
╞════════════════════════════════════════╪═════════════╪══════════╪════════╪═════════╡
│ Missing Privacy Manifest ┆ Privacy ┆ ERROR ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Missing Camera Usage Description ┆ Permissions ┆ ERROR ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ LSApplicationQueriesSchemes Audit ┆ Metadata ┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ UIRequiredDeviceCapabilities Audit ┆ Metadata ┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ ATS Exceptions Too Broad ┆ ATS ┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Sensitive Files in Bundle ┆ Bundling ┆ ERROR ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Info.plist Versioning Consistency ┆ Metadata ┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Extension Entitlements Compatibility ┆ Entitlements┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Privacy Manifest vs SDK Usage ┆ Privacy ┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ Debug Entitlements Present ┆ Entitlements┆ ERROR ┆ PASS ┆ PASS │
╰────────────────────────────────────────┴─────────────┴──────────┴────────┴─────────╯
Analysis complete!
╭────────────────────────────────────────┬─────────────┬──────────┬────────┬────────────────────────────────────────────────────────────╮
│ Rule ┆ Category ┆ Severity ┆ Status ┆ Message │
╞════════════════════════════════════════╪═════════════╪══════════╪════════╪════════════════════════════════════════════════════════════╡
│ Missing Privacy Manifest ┆ Privacy ┆ ERROR ┆ FAIL ┆ Missing PrivacyInfo.xcprivacy │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ Missing required usage description keys┆ Privacy ┆ WARNING ┆ FAIL ┆ Missing required usage description keys │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ LSApplicationQueriesSchemes Audit ┆ Metadata ┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ UIRequiredDeviceCapabilities Audit ┆ Metadata ┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ ATS Exceptions Too Broad ┆ ATS ┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ Sensitive Files in Bundle ┆ Bundling ┆ ERROR ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ Info.plist Versioning Consistency ┆ Metadata ┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ Extension Entitlements Compatibility ┆ Entitlements┆ WARNING ┆ PASS ┆ PASS │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ Privacy Manifest vs SDK Usage ┆ Privacy ┆ WARNING ┆ PASS ┆ PASS │
╰────────────────────────────────────────┴─────────────┴──────────┴────────┴────────────────────────────────────────────────────────────╯
verifyOS-cli is organized as a layered scanner plus an AI-agent handoff system:
src/main.rs: CLI entrypoint, subcommands (scan,init,doctor,handoff,pr-comment,analyze-size,lsp), output routing, and exit policy.src/core/: Scan orchestration, rule execution timing, and artifact-context lifecycle.src/parsers/: Low-level readers for.ipa,.app,Info.plist, provisioning profiles, Mach-O usage/signing/SDK scans, and related bundle metadata.src/rules/: Trait-based App Store review rules grouped by concern such as privacy, entitlements, signing, ATS, metadata, and bundling.src/report/: Normalized report model plus renderers for table, JSON, SARIF, Markdown, agent-pack JSON, and agent-pack Markdown.src/profiles.rs: Rule inventory, default profile membership, and CLI-facing rule metadata.src/agents.rs:AGENTS.mdmanaged block generation, current-risk summaries, next-step commands, and fix-prompt rendering.src/doctor.rs: Project self-checks for config,AGENTS.md, and repair-oriented setup validation.src/commands/: Command-specific logic forinit,doctor,handoff,pr-comment,analyze-size, andlsp.src/commands/lsp.rs: The language server that turns scan findings into editor diagnostics forInfo.plistandPrivacyInfo.xcprivacy.src/size_analysis.rs: Implementation for theanalyze-sizefeature, breaking down the app bundle's space consumption.editors/vscode/: Thin editor client that launchesvoc lsp, exposes a clean UX, and relies on the Rust scanner for real diagnostics.tests/: CLI, report, config, and rule-level regression coverage for both normal scans and agent workflows.
The design goal is to keep scanning concerns, report rendering, and AI-agent onboarding separate enough that we can keep adding rules without tangling the developer workflow around them.
We initialized a clean layout for a future backend/frontend split while keeping the current Rust crate stable:
apps/cliplaceholder for the CLI app crateapps/backendplaceholder for the Rust HTTP API (uploads + scan)packages/coreplaceholder for the Rust engine + rules crateapps/frontendplaceholder for a web UI or TUI shelleditors/vscodefor the current VS Code extension
See docs/ARCHITECTURE.md and docs/STRUCTURE.md for the full plan and the migration checklist.
- Input
vocreceives an.ipaor.app, optional config, profile, include/exclude filters, baseline, and output targets. - Artifact preparation
core::engineresolves the app bundle, whileparsers/load plist files, provisioning data, Mach-O metadata, and bundle resources. - Cached scan context
ArtifactContextcaches expensive lookups such as usage scans, signing summaries, bundle file indexes, entitlements, and plist reads so multiple rules can reuse them. - Rule execution
rules/run against the shared artifact context and emit normalized results with:rule_idcategoryseveritymessageevidencerecommendation
- Report normalization
report/converts engine output into a stable report model, applies timing metadata, cache telemetry, and optional baseline suppression. - Primary outputs
The CLI renders one of:
- table
- JSON
- SARIF
- Markdown
- agent-pack JSON/Markdown/bundle
- Agent workflow outputs
voc initandvoc doctor --fixcan materialize:AGENTS.md.verifyos-agent/agent-pack.json.verifyos-agent/agent-pack.md.verifyos-agent/next-steps.shfix-prompt.mdpr-brief.mdwhen--open-pr-briefis enabledpr-comment.mdwhen--open-pr-commentis enabledrepair-plan.mdwhen--plan-outis used
- CI / PR integration
The reusable workflow
.github/workflows/voc-analysis.ymlruns scans in GitHub Actions, uploads SARIF and agent assets, and can post a sticky PR summary comment. - Repair / refresh loop
Developers or AI agents patch the suggested target files, rerun
voc, compare against the previous baseline or agent pack, and repeat until findings clear.
To ensure the automated semantic versioning and changelog parsing through the release-plz bot behaves properly, developers MUST use Git Conventional Commits format:
feat:A new feature (correlates to a MINORv0.X.0bump).fix:A bug fix (correlates to a PATCHv0.0.Xbump).docs:Documentation only changes.chore:Changes to the build process or auxiliary tools.
- CI: fmt + clippy + tests on push and pull request.
- Automated release PRs:
release-plzworkflow. - Publishing: crates.io + GitHub release artifacts.
MIT