Skip to content

Secret scanning (on by default) πŸ”’

Every TOVIO repo scans for secrets on every commit, out of the box β€” no install, no configuration. It is the first-party plugin com.tovio.secret-scan, running in the same capability-bounded sandbox as any other plugin (Capabilities & security), so it can read the files you're committing but cannot touch the network, the filesystem, or anything outside the content it's handed.

When it finds a secret, it doesn't silently do something to your repo. It stops and asks you.

What it does

On tovio commit, before the snapshot is taken, the scanner looks at the clear (unencrypted) files the commit introduces. If it flags something, you get a prompt:

$ tovio commit -m "add prod config"

  πŸ”’ 1 secret(s) detected in the snapshot
     [error] Identified an AWS access key ID paired with a secret access key, which together can provide full access to AWS services. at line 1 (AKIA…EWZZ) (config/prod.conf)
     paths: config/prod.conf

  Protect these? [E]ncrypt in place (default) / [b]lock / [a]llow-once:

The wording of a finding is the detection rule's own description, and its severity is the rule's β€” most credential rules report error. Once your allowlist has vouched for some of a snapshot's findings, the summary line grows an (N allowlisted) count for the ones it suppressed; if every finding is vouched the block dissolves and the commit proceeds.

Three choices, and you pick:

  • [E]ncrypt in place β€” TOVIO adds a protective policy for the file and encrypts it as part of this commit. The secret goes into history as ciphertext, readable only by you (and anyone you later grant). This is the recommended default.
  • [b]lock β€” refuse the commit. Nothing is written; fix the file and try again.
  • [a]llow-once β€” commit the file unencrypted, just this once, on your explicit say-so. TOVIO prints the fingerprints so you can allowlist a confirmed false positive permanently, and records the say-so as a signed SecretAllowedOnce entry naming you and those fingerprints β€” never the raw values. (A Simple repo has no signing identity and therefore no audit chain, so nothing is recorded there.)

The raw secret never appears in output, in an error, or in the audit record β€” only a redacted preview. A matched value longer than eight characters shows its first and last four, AKIA…EWZZ; a shorter one shows only its first two. A rule that fires on the filename rather than on content (a .env file, say) has no secret to redact, so it previews the filename.

It never acts without a human

This is the important part. The scanner proposes; a person confirms. TOVIO will never silently encrypt a file or wave a secret through on its own. So in any context where there's no human at the keyboard β€” CI, an agent (tovio commit --token), --json, --quiet, or a piped stdin β€” the commit fails closed:

$ printf 'aws_key = "AKIA…"\n' > config/prod.conf
$ tovio commit -m "add config" < /dev/null      # non-interactive

βœ— Secrets detected β€” commit blocked
  …
  Fingerprints (paste into .tovio/secrets-allow.toml `allow = […]` to permanently allow a vouched false positive):
    SECRET:aws-access-token:d2c9a035e2eb
  …
  [TVO-SECRET-001]

A bot can only ever block β€” it can't auto-encrypt and it can't auto-allow. That's the guarantee.

Encrypting needs a team identity

Encrypt-in-place seals the file to your key, so it needs one. In a Simple repo (no cryptographic identity) the prompt tells you so and offers block / allow-once instead β€” run tovio init --mode team to enable encryption.

Handling false positives

Real code sometimes looks like a secret. Two ways to vouch for a line:

  1. Inline β€” add a tovio:allow-secret comment on the flagged line. It travels with the file, so your whole team gets the same suppression.
  2. By fingerprint β€” every finding has a stable fingerprint (its code, SECRET:<rule-id>:<hash>, like SECRET:aws-access-token:d2c9a035e2eb). When a commit is blocked, TOVIO prints it ready to paste into .tovio/secrets-allow.toml:
allow = [
  "SECRET:aws-access-token:d2c9a035e2eb",
]

The fingerprint hashes the exact occurrence β€” rule, path, line, and value β€” so an edit that moves or changes the value re-flags it. Keying on the fingerprint rather than a rule or path also makes the allowlist swap-proof: any scanner emitting the same code gets the same suppression. The file is local repo state, never synced, and it is fail-safe: a missing or malformed file suppresses nothing (a malformed one also warns).

Turning it off, or swapping it

Local, per-repo .tovio/config settings:

Setting Effect
secrets.scan = off turn default scanning off for this repo (env override: TOVIO_SECRETS_SCAN=off)
secrets.scanner = <plugin-id> swap in a different scanner (any plugin that reads content on pre-snapshot)
secrets.protect-policy = <expr> the read policy "encrypt" writes (default clearance=secrets)
$ tovio config set secrets.scan off        # opt this repo out

Keeping plugins on a leash

Plugins already run under hard, always-on limits β€” no network, no filesystem, a compute (fuel) cap of a few CPU-seconds, a 256 MiB memory ceiling, a 4 MiB output cap. So a plugin can't be turned into a free compute farm or hang your commit. If you want to tighten any of that further, you can β€” per repo:

Setting Env override Caps
plugins.max-timeout-ms TOVIO_PLUGIN_MAX_TIMEOUT_MS wall-clock time per run
plugins.max-fuel TOVIO_PLUGIN_MAX_FUEL compute (instructions) per run
plugins.max-memory-mb TOVIO_PLUGIN_MAX_MEMORY_MB memory per run
$ tovio config set plugins.max-timeout-ms 3000    # no plugin runs longer than 3s

Each setting only ever tightens the built-in ceiling β€” it can't loosen it. And if a plugin blows its budget, it fails closed (a required plugin that times out blocks the commit β€” it's never silently skipped).

See also

  • Capabilities & security β€” the sandbox model these plugins run in.
  • Tutorial: protect a secret β€” the encryption side, hands-on.
  • Errors β€” TVO-SECRET-001 and the plugin block codes.
  • The normative spec (docs/specs/secret-scanning.md, REQ-SECRET-*) and the decision record (ADR-0057) in the repository.

Last reviewed September 9, 2026

Suggest an improvement to this page Not for security reports — see disclosure