Security Model

September 20, 2026 ยท View on GitHub

JevGuard sends code-change evidence to Jev. Evidence safety is therefore a product boundary, not a best-effort feature.

What is sent

For an applicable rule, JevGuard sends:

  • the direct parent user task;
  • the rule ID, policy, violation definition, and optional allowed exception;
  • the full attributed textual diff for changed files within the rule scope;
  • the relevant file paths.

It does not send repository-wide diffs as a fallback.

Evidence must be complete

JevGuard never truncates a relevant diff. The assembled diff for the rule's applicable files is capped at 100000 characters; a larger diff is UNAVAILABLE with reason OVERSIZED_DIFF.

It also does not evaluate a rule from a subset of its relevant files. If any applicable file is rejected by the safety policy, that rule is UNAVAILABLE with reason BLOCKED_EVIDENCE.

Rejections are scoped to the rules they affect. Evidence is selected per rule from that rule's applicable files, so an oversized or blocked file makes only the rules whose scope matches it UNAVAILABLE; rules with disjoint, safe applicable files still run.

File safety policy

The evidence policy is fixed in core (not configurable). Only common code and text extensions may be sent; a file without an allowlisted extension is rejected rather than assumed textual.

Allowed extensions are broad and cover common source, config, docs, shell, and data text formats: for example .ts, .tsx, .js, .jsx, .json, .md, .yaml, .toml, .css, .html, .py, .rb, .go, .rs, .java, .cs, .php, .c, .cpp, .swift, .sh, .ps1, .sql, .graphql, .tf, and .patch.

Sensitive paths are always rejected, including:

  • exact file names such as .env, .envrc, .netrc, _netrc, .npmrc, .pypirc, .pgpass, .git-credentials, .htpasswd, .dockercfg, id_rsa, id_dsa, id_ecdsa, id_ed25519, credentials, credentials.json, credentials.yaml, credentials.yml, secrets, secrets.json, secrets.yaml, secrets.yml, shadow, and master.key;
  • extensions such as .pem, .key, .p12, .pfx, .jks, .keystore, and .ppk;
  • directory segments such as .ssh, .aws, .gnupg, .azure, .kube, and .docker.

Matching is case-insensitive and treats both / and \ as separators. A dot-prefixed denied name also matches its dotted variants, so .env rejects .env.local; entries without a leading dot such as credentials and secrets match exactly. The invariant is that rejected content never leaves the machine.

Structured logs may record a rejected path and reason. They must never record the rejected file contents, and a rejected file is never replaced by the repository diff or by another file's patch.

Credentials

For normal local use, run the login command with the published package:

bunx --package @jevguard/plugin jevguard login

Or install it globally so jevguard is on PATH:

bun install --global @jevguard/plugin
jevguard login

Adding the plugin to opencode.json does not put jevguard on PATH. @jevguard/plugin is not published yet; until then, install the packed local tarball globally and run jevguard login.

The CLI reads the key using a masked terminal prompt and stores it in the native operating-system credential store:

  • macOS: Keychain;
  • Windows: Credential Manager;
  • Linux: Secret Service/keyring.

JevGuard reads the credential each time it evaluates a turn. The API key never belongs in .jev/, opencode.json, agent messages, logs, toasts, test fixtures, or a committed file.

For CI and controlled automation, set TYPESAFE_API_KEY in the job's secret environment. This environment value overrides the credential store for that process. Do not print it, pass it as a CLI argument, or use a shell command that persists it in history.

If neither source provides a key, the plugin stays loaded and the evaluation is UNAVAILABLE, with a safe instruction to run jevguard login.

Important

A secret passed through the environment of the OpenCode process can be inherited by subprocesses. The credential store is the preferred path for local use because it avoids placing the key in the agent process environment. Treat environment credentials as an automation/CI integration surface, not a local secret vault.

Operational outcomes

UNAVAILABLE is intentional when JevGuard cannot obtain complete safe evidence, validate policy, access Jev, or retrieve the attributed diff. It is not a policy pass, a warning, or a failure, and the observe-only plugin does not block work.