Docs
Install
One line on macOS and Linux. It works out which build fits the machine, checks the download against its checksum, and puts the binary on your path.
curl -fsSL https://peko.so/install.sh | sh
On Windows, in PowerShell:
irm https://peko.so/install.ps1 | iex
It installs to /usr/local/bin by default. Set PEKO_BIN_DIR to put it somewhere you own. If another program on your machine is already called peko, the installer says so and tells you how to reach ours.
Your first run
Nothing below needs an account. The lint runs on your own machine and sends nothing anywhere.
peko init- Write .pekorc.json and work out the platform.
peko lint --all- Check every file and print what review would refuse.
peko lint on its own checks the files this commit touched, which is what you want in a hook or in CI. Pass --all to check the whole project.
The exit code is 1 when something at or above the fail level is found, and 0 otherwise. The default fail level is error. Pass --fail-on warning to be stricter, or --fail-on never to report without failing the build.
The commands
| Command | What it does |
|---|---|
peko init | Writes .pekorc.json, works out the platform, and fills in the facts it can read from the project. |
peko lint | The free checks. Runs on your machine, calls no model, needs no account. |
peko facts | Lists the questions the rules need answered that the code cannot answer for itself. |
peko audit | Reads the code with a model and judges the guidelines a file cannot settle. Needs a paid plan. |
peko diagnose | Reads a rejection letter and names the rules behind it. Free, and calls no model. |
peko outcome | Tells us what the store decided about your app. |
peko override | Records that a rule does not apply here, with a reason. |
peko rules | Lists the rules the server holds. |
peko status | Says where the key is read from and whether the server answers. |
peko login | Explains how to supply the key. It writes nothing to disk. |
Every command takes a project path. Run it from anywhere inside the project and it finds the root, the way git finds .git.
The audit tier
The lint proves things about files. The audit reads the code with a model and judges the guidelines that prose alone decides, which is most of store policy.
peko audit- Says what it would read. Uses nothing.
peko audit --yes- Runs it, and uses one of the month's audits.
An audit is one of a set number each month: 10 on Pro and 35 on Max. There is no per run charge and no price to approve. The count resets when the plan renews.
It runs as a job, because it takes minutes. The command starts it and waits, printing progress. A network drop while it waits does not lose the run.
It refuses to start while the free checks are failing. Paying a model to read code that a free check already refused is waste, and the message says which check to fix.
It also refuses while a fact a rule needs has no answer. Run peko facts to see them, and write them into .pekorc.json.
Facts
Some rules cannot decide from the code alone. Whether an app is for children, whether it ships to the EU, whether it sells data. The checker reads what it can from the project and asks you for the rest.
peko facts
Answers go in the facts block of .pekorc.json. A fact left unanswered makes every rule that reads it stay silent, and silence reads like a pass, so the audit refuses to run rather than report one.
distributes_in takes three values and only three: US, US-CA, and eu. Anything else parses and matches nothing, and peko facts says so.
When you get rejected
Paste the letter in. It finds the guidelines the reviewer cited, names the rules behind them, and says what to change.
peko diagnose < rejection.txt
It also names any guideline the rejection cites that no rule covers yet, as plainly as the ones it matched. Without that you would fix three things and miss the fourth.
When the store decides, tell us. It is the only thing that says whether a finding was right.
peko outcome rejected --sections 3.1.1 --notes-file rejection.txt
It attaches to the last audit you ran in that project. approved and withdrawn work the same way.
Overrides
A finding that does not apply to your app can be recorded as handled, with a reason.
peko override AAPL-PRIV-010 --reason "This target ships no privacy manifest by design."
Some rules cannot be overridden. Apple rejects an upload holding UIWebView whatever anybody writes down, so acknowledging it here would change nothing, and the command refuses rather than letting the file claim it is handled.
In CI
The GitHub Action runs the lint on every push and uploads the result to Code Scanning, so findings appear on the pull request against the lines they are about.
- uses: official-peko/peko@v1
with:
path: .
fail-on: error
No key is needed for the lint. Add one as a secret to run the audit tier.
Outside GitHub, peko lint --sarif peko.sarif writes the same file for anything that reads SARIF.
The config file
.pekorc.json sits at the project root. peko init writes it.
| Field | What it is |
|---|---|
platform | ios or android. |
facts | The answers the rules need. peko facts lists what is missing. |
overrides | Rules recorded as handled, each with a reason. |
api_key_env | Which environment variable holds the key. PEKO_API_KEY by default. |
telemetry | false to stop this project being measured. |
Environment
| Variable | What it does |
|---|---|
PEKO_API_KEY | The key for the audit tier. Nothing is written to disk, so put it in your shell profile or your CI secret store. |
PEKO_TELEMETRY | Set to off to stop this machine being measured. |
DO_NOT_TRACK | Honoured the same way. |
PEKO_BIN_DIR | Where the installer puts the binary. |
NO_COLOR | Turns off colour. A dumb terminal and a pipe do the same. |
For coding agents
A skill file teaches a coding agent which command to reach for, when to ask before spending one of the month's audits, and what not to claim on your behalf.
mkdir -p ~/.claude/skills/peko
curl -fsSL https://peko.so/skill.md -o ~/.claude/skills/peko/SKILL.md
That path is for Claude Code, for every project. Use .claude/skills/ inside a project for one of them. Other agents read from their own directory, and the file is plain markdown with a name and a description at the top, so it ports.
An agent that has read it runs the free lint before answering a question about store compliance, asks before starting an audit, and does not tell you your app is compliant. An agent that has not read it guesses at all three.
What we measure
We record which rules run, what they decide, how long they take, and what they cost us. It is how a rule that never fires or always misfires gets found.
Never your source, never a file path, never your app's identity. The full list, and every way to turn it off, is in the measurement page.
Turning it off on the account page covers the whole account, including a copy of the CLI that has not been updated.
Something wrong
Write to contact@pekoui.com. A rule that fired when it should not, or stayed quiet when it should not, is the most useful thing you can send, and the rejection letter with it is better still.