Security
This page covers the security configuration a yoker-assistant operator must
understand before running the package. The source SECURITY.md at the repo
root documents the manifest-addition review process contributors follow
before adding a tool; this page cross-references it and expands the
operator-facing configuration.
Trust blast radius
The package injects config.plugins.enabled = True and adds
yoker_assistant and pkgq to config.plugins.packages at startup in
loop.py. The user confirms trust interactively on first run via
yoker’s standard trust prompt (“Load this plugin? [y/N]”). After
confirmation the plugin is session-trusted. For permanent trust across
sessions, the user can optionally add [plugins.trusted] yoker_assistant = true (and pkgq = true) to ~/.yoker.toml.
Once trusted — by interactive confirmation or [plugins.trusted] entry
— ALL tool code from this package runs with no per-call gate. The trust
decision is made once, at install time. This is the intended yoker trust
model — the user explicitly confirms trust, and the package’s tools then
run without per-call interruption.
The blast radius is real: every tool in __YOKER_MANIFEST__ runs with no
per-call gate, including yoker:git (full git — read, commit, push).
Mitigate by:
Pin the installed version —
uv pip install yoker-assistant==<version>so the tool code you reviewed is the tool code that runs. A floating install silently swaps in new tool code on eachuv sync.Verify the source — review the package source (this repository) before marking it trusted. The manifest is small (
src/yoker_assistant/__init__.py); read it.Review the
pkgqplugin the same way — it is also marked trusted in the recommended config.pkgqis a third-party package; pin its version too.
Recipient whitelist — the primary reply-safety boundary
EMAIL_RECIPIENT_WHITELIST_ADDRESSES is the primary reply-safety boundary.
The assistant may only reply to addresses in this whitelist. Set it to the
single owner address; leaving it broad allows the agent to reply to
arbitrary senders.
EMAIL_RECIPIENT_WHITELIST_ADDRESSES=owner@example.com
The env var name is EMAIL_RECIPIENT_WHITELIST_ADDRESSES — NOT
EMAIL_RECIPIENT_ADDRESSES. The wrong name silently disables the whitelist
(this is an upstream simple-email-gw README bug; an issue is filed — see
TODO.md S-03). With the whitelist silently disabled, the assistant would
reply to arbitrary senders, which is the worst-case reply-safety failure.
The C1 blocking fix in loop.py makes this fail-safe: run() refuses to
start when the recipient whitelist is disabled — the whitelist fails open,
so an unset whitelist would let the assistant reply to arbitrary senders.
run() raises RuntimeError (citing EMAIL_RECIPIENT_DOMAINS,
EMAIL_RECIPIENT_ADDRESSES, or EMAIL_RECIPIENT_WHITELIST_JSON) before
constructing the Agent.
This is a simple-email-gw config concern, not package code. Python relies
on simple-email_gw’s existing config; no package-level allowlist code is
written.
~/.yoker.toml and .env are NEVER committed
Both files contain secrets (~/.yoker.toml has the LLM API key; .env has
the mailbox password). Neither is committed to the repo:
.envis gitignored at the repo root.~/.yoker.tomllives in the user home and never enters the repo.
A user who snapshots ~/.yoker.toml into a repo (for example, to back it
up) MUST gitignore it. The repo provides yoker.toml.example as reference
documentation only — it has a REFERENCE ONLY header and no real api_key;
do not copy real secrets into it.
The manifest-addition review process
Every addition to __YOKER_MANIFEST__ is admitted by the user’s trust
decision (interactive confirmation on first run, or [plugins.trusted]
entry in ~/.yoker.toml) — there is no secondary review gate inside the
package. SECURITY.md documents the review process contributors MUST
follow before adding a tool to the manifest (or making a
capability-changing edit to an existing tool — new args, new side
effects, new dependencies, or a change to the tool’s reach). Pure
refactors that do not change the tool’s inputs, outputs, reach, or
failure modes do not require this review.
The process has four steps (paraphrased here; see SECURITY.md for the
authoritative text):
Blast-radius assessment — document the tool’s inputs, outputs, reach, and failure modes. Any string/path/URL input? Side effects beyond the return value? What can it touch (filesystem, network, shell, subprocess, env vars, other tools)? What happens on bad input?
Capability review — does the tool duplicate an existing yoker built-in? Does it compose with
yoker:git,yoker:write, oryoker:webfetchin a way that creates a new exfiltration or persistence path? Is the tool bounded (named, typed args, no**kwargsshell) or unbounded? Unbounded tools are rejected by default.Version pinning — the tool’s behavior must be deterministic for a pinned package version. No network-fetched code paths at call time. If the tool depends on a network resource, the dependency must be declared in
pyproject.toml(souv pip install yoker-assistant==<version>reproduces it), not loaded dynamically.Review checklist — the PR description records: blast-radius assessment, capability review, version pinning, tests cover the happy path + at least one failure mode,
make checkgreen,SECURITY.mdupdated if the process itself changes.
A manifest addition or capability-changing edit merged without this checklist is a process violation: it is not automatically a security incident, but it triggers (1) immediate revert of the change, and (2) a retroactive security review of what the unreviewed code did while live, covering the exposure window from merge to revert. CI does not enforce this — it is a reviewer judgment gate.
Publishing guards
make pre-publish (see Makefile) rejects built sdist/wheel metadata
containing non-registry source URLs (file://, VCS schemes, direct
@ <url> references, path =). This prevents local-path development
wiring (which lives in [tool.uv.sources] and is structurally excluded
from PyPI metadata) from ever leaking into a published artifact if
discipline slips.
make pre-publish also rejects relative image paths in README.md (the
media/ prefix check) and verifies that the pyproject.toml version
matches the src/yoker_assistant/__init__.py __version__.
Deliberate non-additions
A supported-versions table, a formal CVE-handling section, a PGP key, and a
security.txt are deliberately omitted from SECURITY.md. For a 0.1.0
single-tool plugin provider with no released security history, these are
ceremony — premature policy-writing that would go stale immediately. If a
real CVE lands or multiple release lines emerge, revisit then; until then,
GitHub Security Advisories is the one-line-footnote fallback for CVE
publication.