Docs

How yof works

What yof does today, in plain words. Every section describes something you can use now. This page is in English for now.

Getting started

  1. Sign up. Start free. It costs nothing for your own apps. One account works in yof, sōtem and Mondaily.
  2. Add your site. Paste its address on the Overview. yof takes https addresses on a public name only (no IP addresses, no localhost, no private names) and keeps the origin, not a single path. Up to 10 apps per account.
  3. Prove it is yours. yof gives you a token. Put it in any one of three places: a <meta name="yof-verify" content="TOKEN"> tag on your home page, a file at /.well-known/yof-verify.txt that holds the token, or a DNS TXT record yof-verify=TOKEN. yof looks for the proof again at the start of every check, so removing it takes the app back to the short report.
  4. Run a check. Press Check now. You see each check as it runs, then a report: findings by urgency (Fix now, Fix this week, Nice to have), a health score and, for each finding, a fix prompt to paste into the AI that built your app.

Without proof, yof reads only what any visitor's browser gets and shows a headline (counts and score), never details. Keys and secrets stay locked until you prove the site is yours, so nobody can use yof to find a weak spot on someone else's site.

The box on the yof home page runs a free check with no account: the start page, its scripts, the headers and public DNS, with the same headline-only rule.

A proven app can also get a daily check and one short email a day: all good, or the single most urgent open finding. Every email has a link to turn it off. Each person can start 6 checks an hour and 30 a day.

What each check reads

Every check reads through one polite fetcher: GET and HEAD only, no cookies, no body, a time limit and a size limit per answer, a request budget per run, at most two requests at a time per host, default ports only. It identifies itself as yof-check (+https://yof.io), so you can see who called.

CheckWhat it readsRuns for
bundleKeys and secrets in your pages and the JavaScript they load, as any visitor's browser gets them. Only scripts from your own site are read.Proven apps
databaseSupabase: asks for a count of common sensitive tables the way any visitor could. No row is ever fetched. Firebase Realtime Database: whether the root answers without a login (keys only).Proven apps
filesFiles left open: .env, the .git folder, backups, folder listings, debug pages and source maps. A file counts only when its content proves what it is.Proven apps
adminA short list of common admin addresses: flagged only when the server sends admin content to a stranger without a sign-in form.Proven apps
apiYour own API addresses that your pages name: personal data served to anyone, keys in addresses, error details, CORS, open API docs. GET only.Proven apps
aiYour own chatbot, about 12 mild test messages read with fixed rules: leaked instructions, hidden commands, keys, other people's data.Proven apps, after you set it up and agree
headersSecurity headers on your start page and whether plain http redirects to https.Every app
emailSPF, DMARC and DKIM in public DNS, so you know if others could send mail as your domain.Every app
seoWhat your start page tells search engines and link previews, robots.txt and the sitemap.Every app
frontendPhone viewport, insecure files on an https page, very large images, fonts and low-contrast colour pairs in your CSS.Every app
contentPlaceholder text, unedited AI phrases, the same paragraph on several pages, links to missing pages.Every app
websiteA real browser opens your site on a laptop and a phone screen: console errors, failed requests, broken links, the 404 page, layout and speed. It never fills in or sends a form.Proven apps (runs in our cloud browser in Frankfurt)

The health score counts the safety checks only (bundle, database, files, admin, api, ai, headers, email). Search, front end, content and website findings show in the report but never pull the score down. Keys that are public by design (a Supabase anon key, a Firebase web key, a Stripe publishable key) are shown as info and never counted.

If a site's bot protection blocks yof, the report says so. The owner of a proven app can let yof through with a pass header that only they and yof know.

What yof never does

  • It never logs in, never submits a form and never guesses passwords.
  • It never attacks: no fuzzing, no guessed record ids, no port scans, no POST, PUT, PATCH or DELETE to your site. The one exception is the assistant test you turn on yourself, which sends short test messages to your own chatbot.
  • It never reads rows from your database. The database check asks for a count and reads only the total.
  • It never keeps page contents, scripts or answers. A finding keeps the rule, the place and a masked piece of evidence.
  • It never sneaks past bot protection. Only the owner can let it in.
  • A check describes what yof looked at on that date. It is not a promise that nothing can go wrong, so the badge says "Checked by yof" and nothing more.

Guard on GitHub

Guard is a GitHub app, yof by sotem. Install it on the repositories you choose. On every pull request it reads the changed files on our server and runs three sets of rules: code rules (who may read or change data, money and webhooks, swallowed errors and secrets named for the browser), key patterns, and the package check (made up, brand new, lookalike or with a known advisory).

  • It leaves one comment, created once and updated on each push.
  • It adds one check named yof Guard. The check fails only when something must be fixed now. Findings for this week or nice to have give a neutral result.
  • It blocks a merge only if your repository makes that check required in branch protection. That is your choice; Guard never decides it.
  • It never pushes, approves, merges or edits a file. All it writes is that comment and that check.

Settings: .yof.yml

Optional. Put it in the root of your repository. Guard and the CLI read the same file and the same two settings: profile and an ignore list (at most 50 entries). Anything else in the file is ignored.

profile: balanced    # relaxed | balanced | strict

ignore:
  - fixtures/
  - docs/examples.md
  - "**/*.snap"
ProfileThe CLI showsThe CLI fails on
relaxedFix nowFix now
balanced (default)Fix now, fix this weekFix now
strictEverything, including public keys that are fineFix now, fix this week

In Guard, relaxed keeps only sure findings to fix now, balanced every sure finding, and strict adds the less certain ones. The yof Guard check fails only on something to fix now, in every profile.

Guard reads .yof.yml from the base branch of the pull request, not from the pull request itself. A pull request can't add an ignore rule to silence Guard on its own change. When a pull request edits .yof.yml, Guard says so, and the new rules apply after it is merged.

The CLI

The npm package @yof/cli is open source under the Apache 2.0 license, at github.com/yof-dev/yof. It runs on your machine, needs no account and Node 20 or newer. It finds secret keys in your code and dependencies that don't exist or imitate popular ones.

npx @yof/cli check

That checks the current folder. To check every commit, add the pre-commit hook. It runs yof check --staged, which reads exactly what is about to be committed and stops the commit when something must be fixed now.

npx @yof/cli hook install
Command or flagWhat it does
yof check [path]Check a folder (default: the current one)
yof check --stagedCheck only the files staged for the next commit
yof hook installRun yof check --staged before every commit
yof hook uninstallRemove that hook
--offlineSkip the package-registry lookups (keys only, no network calls at all)
--jsonMachine-readable output, masked values only
--profile <name>relaxed, balanced or strict; overrides .yof.yml

Exit codes: 0 nothing to fix, 1 something to fix (per the profile), 2 usage or setup error. The CLI never prints a key in full. Its only network calls are package lookups (names and pinned versions) to registry.npmjs.org, api.npmjs.org, pypi.org and api.osv.dev. There is no telemetry.

Run it on every push

The same CLI as a GitHub Actions step, on every push and pull request. The job fails when yof finds something to fix now.

# .github/workflows/yof.yml
name: yof
on: [push, pull_request]
permissions:
  contents: read
jobs:
  yof:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: yof-dev/yof@v0.1.0
        with:
          path: .
          offline: false

path and offline are optional. offline: true checks keys only, with no registry lookups.

Patch (early access)

Patch is yof's agent that watches your proven apps. It is free while in early access; nothing is charged. Toward your site it reads only, like the rest of yof. After each check it watches:

  • Scripts: a new third-party script host, or a changed script at the same address, on your start page and up to two sensitive pages (login, checkout).
  • Subdomains: new names under your domain in public certificate logs, and subdomains that point at a service someone else could claim.
  • DNS and nameservers: the records that matter (A, AAAA, CNAME, MX, SPF, DMARC) and your domain's nameservers. A nameserver change moves control of the whole domain, so it counts as Fix now.
  • Certificates that expire within 14 days.

Every Patch event shows what it noticed, the before and after, its verdict and what it did. Patch never changes your DNS; it only watches.

Checks after a deploy

Make a deploy hook for an app and call it from your deploy, or link a GitHub repository: a successful production deploy that GitHub reports (deployment_status) runs the same check. At most one deploy check per app every 5 minutes, within your usual check limits.

Fix pull requests

For a repository you linked (the yof App installed, and you have write access), Patch can open one pull request with a fix from a small set of safe templates: missing security headers (vercel.json or a Netlify _headers file), a .well-known/security.txt, and the missing .env lines in .gitignore. If it can't tell your host for certain, it writes nothing. The pull request goes on a new branch. Patch never merges, approves, deletes or force-pushes. You review it and merge it.

The terminal in the app

The Terminal in the yof app runs yof's actions on your own apps by typing. It runs on our side, for your account only, and can't run anything on your computer (that is what the CLI is for). A host that isn't one of your apps is refused before any check starts.

CommandWhat it does
helpShow the commands
appsYour apps: host, proven or not, score, last check
check <host>Check one of your apps now and watch it run
findings [host]Open findings, most urgent first
fix <id> [--pr]The fix for one finding, ready to paste into your AI. --pr opens a pull request when Patch has a safe fix
ignore <id> --reason "<text>"Accept the risk of one finding, with a reason
accepted [host]Your accepted risks: why, who and when
reopen <id>Count an accepted finding again
diff <host>What changed on one app since the check before
exportSave yof-rules.md with your open findings and their fixes, for Cursor or Claude Code
ask <question>Ask yof about your apps
guardWhat Guard does and where to turn it on
installHow to run the open-source CLI on your computer
whoamiThe account you're signed in with
clearClear the screen

Add --json to apps, check, findings, fix, ignore, accepted, reopen, diff or whoami to print the result as JSON. Tab completes commands, hosts and finding IDs.

Accepted risks

When a finding is a risk you have decided to live with, accept it with a short reason (up to 300 characters), in the app or with ignore in the Terminal. yof still checks it, but it stops counting in your score, your open findings and your daily email. A later check that finds it again in the same place keeps it accepted; the same problem in a new place is a new finding. Reopen it at any time to count it again. Only the owner of the app can accept or reopen.

What yof keeps

  • Checks: your apps, the results of each check and the findings (the rule, where it was seen, a masked piece of evidence). Not page contents, scripts, API answers or database rows.
  • Guard: where the App is installed and one row per reviewed commit: the repository, the pull request number, the commit, the status, the number of findings and the comment id. Never your code. Guard reads the changed files on our hosting in Frankfurt (EU) and keeps nothing of them.
  • Patch: snapshots of hosts, addresses, script hashes, DNS values and certificate dates. Never page content.
  • The cloud browser for the website check runs on AWS in Frankfurt (eu-central-1). Its error logs, which can include your site address, are kept for 14 days.
  • The CLI keeps nothing and sends no code anywhere.

Found a problem in yof itself? See how to report it.