All plugins

hermes-gatekeeper

Gatekeeper enforces rules the owner configures; not legal advice and not a compliance guarantee. A deterministic pre_tool_call gate for recognized outbound sends: literal `hermes send` and `imsg send` terminal commands (including terminal calls made from execute_code), and a send_message call if one ever traverses the hook, are checked in full. Hermes registers no model-callable send_message tool. Other tools whose names look like a send (connectors__*, mcp tools, plain names, and a cronjob_manage create/update/run with a delivery target) are not parsed: they only require owner approval, because consent, STOP and quiet hours cannot be checked for them. Browser and computer-use driving of an app and scripts that send through a subprocess are not inspected. Rules: absolute suppression, per-channel consent and recipient-local quiet hours (block), and native Hermes approval requests for unanswered threads, minimum gaps and unapproved wording (the host auto-approves these under --yolo, approvals off and hermes -z). Uses the public post_gateway_admission hook to record observed STOP keywords and DM replies (excludes control, approval, steering and slash-command traffic). Reads tool arguments, results and normalized inbound snapshots; writes only profile-local SQLite (owner-only permissions) with recipient/thread hashes, message SHA-256, decision metadata, consent source/date, timezone/region and owner template variables. No raw message-body log; hashes are not anonymity. No network, subprocess, model calls, telemetry, credential reads, external file scans or core patches. Invalid settings or storage errors block recognized sends. Disclosure — gates only the outbound sends it recognizes (literal `hermes send` / `imsg send` terminal commands and send_message calls) and asks owner approval for other send-looking tools; a whole-message STOP, STOPALL, UNSUBSCRIBE, CANCEL, END or QUIT from any admitted gateway sender silently suppresses that sender and is consumed without reaching the agent or getting a reply; a storage or inbound-observation failure blocks every recognized send until the owner repairs records and reloads; writes only a profile-local SQLite ledger of hashes and decision metadata, with no network, subprocess or credential access.

percyhendrux123-progAutomationv0.1.0Updated 3 hours ago

About

Gatekeeper enforces rules the owner configures; not legal advice and not a compliance guarantee. A deterministic pre_tool_call gate for recognized outbound sends: literal `hermes send` and `imsg send` terminal commands (including terminal calls made from execute_code), and a send_message call if one ever traverses the hook, are checked in full. Hermes registers no model-callable send_message tool. Other tools whose names look like a send (connectors__*, mcp tools, plain names, and a cronjob_manage create/update/run with a delivery target) are not parsed: they only require owner approval, because consent, STOP and quiet hours cannot be checked for them. Browser and computer-use driving of an app and scripts that send through a subprocess are not inspected. Rules: absolute suppression, per-channel consent and recipient-local quiet hours (block), and native Hermes approval requests for unanswered threads, minimum gaps and unapproved wording (the host auto-approves these under --yolo, approvals off and hermes -z). Uses the public post_gateway_admission hook to record observed STOP keywords and DM replies (excludes control, approval, steering and slash-command traffic). Reads tool arguments, results and normalized inbound snapshots; writes only profile-local SQLite (owner-only permissions) with recipient/thread hashes, message SHA-256, decision metadata, consent source/date, timezone/region and owner template variables. No raw message-body log; hashes are not anonymity. No network, subprocess, model calls, telemetry, credential reads, external file scans or core patches. Invalid settings or storage errors block recognized sends. Disclosure — gates only the outbound sends it recognizes (literal `hermes send` / `imsg send` terminal commands and send_message calls) and asks owner approval for other send-looking tools; a whole-message STOP, STOPALL, UNSUBSCRIBE, CANCEL, END or QUIT from any admitted gateway sender silently suppresses that sender and is consumed without reaching the agent or getting a reply; a storage or inbound-observation failure blocks every recognized send until the owner repairs records and reloads; writes only a profile-local SQLite ledger of hashes and decision metadata, with no network, subprocess or credential access.

Gatekeeper

A deterministic gate for recognized outbound messaging calls in Hermes.
The agent drafts; Gatekeeper evaluates the owner's policy before dispatch. Hermes—not
this plugin—performs any permitted send and owns the approval interface.

Gatekeeper enforces rules the owner configures; not legal advice and not a compliance guarantee.

Read "What it gates—and what it does not" before relying on it. In a stock Hermes
agent turn the model has no send_message tool, so the call this plugin practically
sees is a literal hermes send … / imsg send … run through the terminal tool.
Those are checked in full (suppression, consent, quiet hours, threading, wording).
Other tools whose names look like a send (connector, MCP and plain tools, and a
cronjob_manage create/update/run with a delivery target) are not checked for
consent, STOP or quiet hours; they only require owner approval. Browser or
computer-use driving of an app and scripts that send from inside python -c or
execute_code are not seen at all.

Version 0.1.0. Checked against Hermes v0.23.0 with the real plugin loader, hook
dispatcher and agent loop (scripted local model, stub send executables, invented
contacts); it has not been run against live messaging platforms. The send-looking-tool
escalation was tested with unit tests against synthetic hook contexts, not on a running
Hermes host.

Install

hermes plugins install hermes-gatekeeper
hermes plugins enable hermes-gatekeeper

Installing by catalog name gets the reviewed, pinned revision, not an unreviewed branch.
Requires Hermes >=0.22.5, Python >=3.10 and Linux or macOS with timezone data.
There are no third-party runtime dependencies or API keys. Restart/reload Hermes
after changing settings; the policy is loaded at registration.

What it gates—and what it does not

Gatekeeper registers pre_tool_call, post_tool_call, post_gateway_admission
and the local-owner /gatekeeper command. It registers no model tools, middleware,
transport, background job or core override.

  • What a live agent turn actually reaches. The model-callable tool that can
    send is terminal: Gatekeeper inspects literal hermes send and imsg send
    commands there (including calls to terminal made from inside execute_code
    scripts, which Hermes also routes through the hooks). Hermes's send_message is
    not currently registered as a model-callable tool (see toolsets.py and the
    note at the top of tools/send_message_tool.py in Hermes). Gatekeeper still
    evaluates a send_message
    call if one ever traverses pre_tool_call (for example from another plugin), using
    {"target":"platform:recipient[:thread]","message":"literal text"}; action=list
    is read-only and unsupported actions/options are blocked, not guessed. Gatekeeper
    does not register or resurrect that tool.

  • Send-looking tools it cannot parse: owner approval, no policy check. With
    escalate_unrecognized_sends on (the default), any other tool call whose name
    matches the send vocabulary below returns an approve directive:
    Gatekeeper: unrecognized send tool <name>; owner approval required. Gatekeeper cannot check consent/STOP/quiet hours for this tool. This covers hosted connector
    tools (connectors__<app>__<tool>, e.g. connectors__gmail__send_email,
    connectors__x__create_post), MCP tools (mcp__<server>__<tool>, and the older
    mcp_<server>_<tool> spelling, e.g. mcp_twilio_send_sms) and plain tool names
    (send_tweet, reply_to_thread). Gatekeeper does not parse these calls, so it
    cannot look up the recipient and cannot apply suppression, consent, quiet hours,
    threading or wording rules to them.
    Approval is the only control. A suppressed
    recipient reached through such a tool is asked about, not blocked.

    The vocabulary is matched on whole words of the tool name (case-insensitive, camelCase
    split, the connectors__<app>__ / mcp__<server>__ prefix ignored so an app called
    notify or sms is not mistaken for a verb; both the full name and each name segment
    are checked): send, resend, reply, tweet, publish; the nouns sms, mms, dm;
    text_message; post with message|msg|tweet|status|update|comment|reply|text|dm|toot|thread|story|to
    or after create|new|add|make|write|submit|compose|schedule; message(s)|msg after
    send|post|create|new|add|make|write|submit|compose|schedule (or before create|post|new);
    compose/forward with email|mail|message; comment creation on a social-network
    name (x, twitter, instagram, facebook, linkedin, tiktok, youtube,
    reddit, mastodon, bluesky, threads, …); notify only when the name or
    arguments point at a person or channel; the bare tool names email/mail, and
    message/chat/text only when called with a send action or a target plus a body;
    glued forms such as sendmail, sendEmail, createPost. A leading read, delete or
    draft word keeps a tool allowed: list, get, search, read, fetch, find,
    count, query, lookup, view, show, describe, retrieve, download,
    export, check, history, inspect, summarize, validate, delete, remove,
    cancel, archive, mark, trash, discard, draft. So
    connectors__gmail__list_messages, connectors__gmail__get_message,
    mcp_slack_search_messages, read_message, message_count, draft_email
    and create_draft are not escalated, while get_and_send and draft_send_email are.
    The matcher looks only at the tool name, never at message text. It is a bounded list,
    not a detector: a sender with an unrelated name (push, blast, zap) is not
    caught unless you add a pattern.

  • cronjob_manage / cronjob. A create, update, edit, run, run_now or
    trigger action that also carries a deliver, target or failure_deliver other
    than local requires approval. list, pause, resume, remove and cancel do not,
    and neither does a run of an existing job (its stored delivery target is not in the
    call) or a create that omits deliver (the host then addresses the originating
    chat).

  • Not reached at all: browser/computer-use driving of a messaging app; a script
    that sends from inside execute_code, python -c or any other subprocess (it is not
    seen as a send); platform tools whose names do not match the vocabulary; and any tool
    with an unrelated name. Direct helper use by the CLI, cron, notifier or adapters is not
    a tool-hook call. Ordinary gateway replies and streaming are not gated. Under
    --yolo, approvals.mode: off and hermes -z the host auto-approves every approve
    directive, so the escalation above does nothing there.

  • Recognized terminal commands are hermes send and imsg send, including
    explicit executable paths, hermes -p NAME send / --profile NAME, and
    hermes-agent. Simple, single-line literal forms can be evaluated:

    hermes send --to sms:+1-555-0100 'Demo notice.'
    imsg send --service sms --to +1-555-0100 --text 'Demo notice.'
    

    These are syntax examples, not commands the plugin runs. For hermes send only
    --to/-t and one literal message are understood; --file, --subject,
    --mention, --json, -q, --to=…, stdin bodies and any other global option
    before send are blocked as uninspectable. imsg needs explicit
    sms or imessage service; auto-service, chat-id lookup, file/stdin bodies,
    subject/attachment flags, wrappers (env, sh -c, python -m hermes_cli.main),
    pipelines and compound commands are blocked.
    Expansion-bearing characters ($, backticks, *?[]{}~) and raw newlines/continuations
    are conservatively blocked even when quoted, rather than approximating shell
    expansion. hermes send --list remains read-only. Mentioning a recognized send
    inside another command can also overblock.

  • Deferred tool_call / multi_tool_use.parallel batches are recursively checked;
    the strictest decision wins. Malformed/oversized/deep batches block. Leaves are
    checked again; the outer batch does not create duplicate send reservations.
    Only send_message and terminal leaves are parsed by the batch check itself. A
    send-looking leaf such as connectors__gmail__send_email is escalated when Hermes
    dispatches it, because every batched call fires its own pre_tool_call.

  • This is not an egress firewall or an OS sandbox. curl, osascript, custom
    scripts, encoded/dynamically constructed sends, renamed tools, direct Python
    calls and arbitrary connector tools are out of scope. Other hooks/middleware
    can rewrite arguments later; Gatekeeper cannot guarantee the final delivered
    payload in that composition. Local code/config/database edits can defeat it, and
    sessions started with --safe-mode/--ignore-user-config load no plugin hooks.

Use stable recipient/chat IDs, not directory/display names or implicit home channels.
#channel aliases are refused. Telegram/Discord/Slack thread suffixes are separated;
other platform address strings (including Matrix server names) remain intact.
Phone-format spaces/parentheses/hyphens after + normalize for record matching.
Consent and thread history match identities as exact, case-sensitive strings;
suppression matches case-insensitively, so a case variant of an opted-out address
is still suppressed. There is no address-book,
account, alias or group-membership lookup. Suppressing a person's user ID does not
prove every alternate DM chat/group/address for them is suppressed; record each
known identity. require_consent: true blocks unrecorded alternate addresses.

Decision rules

| Rule | Result |
|---|---|
| Suppressed recipient | block, always; approval or new consent cannot override |
| Missing consent for this channel | block by default |
| Outside known recipient-local permitted hours | block |
| Unknown/missing/invalid recipient timezone | approve; never infer location |
| Pending or unanswered outbound in this thread | approve, however old |
| Configured minimum gap not elapsed, even after a reply | approve |
| Not an exact owner-filled approved template | approve |
| Send-looking tool Gatekeeper cannot parse (connector, MCP, plain name, cron delivery) | approve; consent, STOP and quiet hours are not checked |
| Every applicable rule passes | no Gatekeeper veto; other Hermes guards still apply |
| Invalid settings, unreadable/corrupt storage, log failure or evaluation error | block recognized sends |

block outranks approve. The default permitted window is 08:00 inclusive to
21:00 exclusive
, in the stored IANA timezone, with DST handled by zoneinfo.
Overnight windows are supported; equal endpoints are invalid. Region/state
windows are owner-supplied, not a built-in jurisdiction or legal database.

An approve directive requests Hermes's native approval gate. It is not an
independent observation of a fresh human answer: host approval modes (including
smart/YOLO/off), unattended behavior and cached scopes remain host-owned. With no
human or approval bridge present (for example a plain scripted turn) the host fails
closed and the send does not run. But hermes -z/--oneshot, --yolo and
approvals.mode: off auto-approve, so in those modes every approve rule above is
a no-op and only the block rules (suppression, consent, quiet hours, errors)
still stop a send. Gatekeeper
never auto-answers a prompt or disables a guard. Its rule key binds the current
arguments/session/call, but it does not override host approval policy. Policy is
checked at the hook boundary, not rechecked after a delayed approval; a suppression
or policy change while a call is waiting does not retroactively cancel that call.
Use a host configuration requiring human review; do not rely on this as universal
or immutable outbound authorization.

Owner setup and commands

Every /gatekeeper command, including status/log, requires a local session: the
host-supplied platform is cli, tui, desktop or local and source is either
None or a source whose platform is local (Hermes runs slash commands of local
sessions in the gateway and passes such a source). Messaging platforms, remote
sources and missing context are refused. This trusts the local
OS/session owner; it is not a second authentication factor. These commands are
not agent-callable tools. All examples below are invented demo data.

/gatekeeper
/gatekeeper log 10
/gatekeeper optout +1-555-0100
/gatekeeper consent +1-555-0101 sms synthetic-form 2026-01-01 America/Chicago TEST
/gatekeeper consent demo@example.com email synthetic-form 2026-01-01 unknown
/gatekeeper revoke +1-555-0101 sms
/gatekeeper variables +1-555-0101 sms '{"topic":"TEST"}'
/gatekeeper reply +1-555-0101 sms
/gatekeeper reply 15550101 telegram 42

Consent stores recipient hash, channel, source, date, timezone and optional region.
unknown explicitly records no timezone. revoke retains location/variable metadata
but removes consent. New consent never removes suppression. V1 deliberately has
no unsuppress command and no approval override for an optout.

reply is the owner's attestation that a reply was actually observed in that
recipient/channel/thread. It also settles that thread's uncertain pending sends;
use it only after independently checking them. It grants no consent and lifts no
suppression. Log output shows timestamps, channel, shortened recipient hash,
rule and outcome, never message text or exception details.

Settings

Under plugins.entries.hermes-gatekeeper.settings (or the plugin's settings UI):

require_consent: true
start: "08:00"
end: "21:00"
region_windows:
  TEST: ["09:00", "20:00"]
min_gap_seconds: 0
templates:
  - "Demo notice for ${topic}."
stop_keywords: [STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT]
escalate_unrecognized_sends: true
extra_send_tool_patterns: ["mcp__acme__*push*"]
allow_tools: ["connectors__gmail__send_email"]
  • escalate_unrecognized_sends (default true) turns the send-looking-name approval
    and the cronjob_manage delivery approval on or off together. Turning it off removes
    that protection entirely.
  • extra_send_tool_patterns (default empty) adds case-insensitive fnmatch globs
    (*, ?) tested against the full tool name and each name segment. A match
    escalates; read-word exemptions do not apply to your own patterns.
  • allow_tools (default empty) lists exact, case-sensitive tool names you trust.
    Those are never escalated by this check. It does not switch off any check on
    send_message or terminal, and trusting a connector tool also means trusting every
    recipient it can reach, because Gatekeeper cannot check consent, STOP or quiet hours
    for it.
  • Invalid values (a non-boolean switch, a non-list, an empty, padded or over-long
    entry) make the whole settings block invalid: recognized sends are blocked until it is
    fixed, and the name-based escalation keeps running on its defaults.

Template variables come only from local-owner recipient records, never tool
arguments/model-provided fill values. $topic and ${topic} are simple string
substitutions; $$ means a literal dollar sign. No Python expressions, attribute
access or recursive substitution. Missing variables do not match. Case, whitespace
and punctuation must match exactly. With no templates (the default), every message
requires approval. min_gap_seconds defaults to 0 and adds an optional gap of up to
2,678,400 seconds; it never expires an unanswered-thread hold. Setting
require_consent: false disables only the consent check—not suppression, quiet
hours, threading or wording. It is an explicit reduction in protection.

STOP and inbound visibility

The public post_gateway_admission hook supplies authorized, routed-profile,
normalized platform/source/message_id/text snapshots. Whole-text matching after
whitespace trimming is case-insensitive: STOP matches, please stop later does
not. No fuzzy, substring or language inference occurs.

On an observed STOP, Gatekeeper suppresses the sender ID and, for a DM, its chat
ID too. It consumes that admitted message silently, without sending confirmation.
A group STOP suppresses its sender, not the entire group. Ordinary observed
DM replies are recorded for that exact destination/thread; group replies do not
clear DM history. When message IDs are present, reply observations are deduplicated
so replaying an old inbound event cannot clear a newer outbound.

This is not a raw all-inbound hook: rejected, ignored, internal, paused/control,
pending-approval/clarify, running-session steering and idle slash-command traffic
may never reach it. Messages outside Hermes are invisible. Record unseen optouts
with /gatekeeper optout and unseen replies with /gatekeeper reply. A reply
never creates consent. If inbound storage/identity observation fails, the running
service latches further recognized sends blocked. Repair the records (including
any missed optout) and reload; a restart alone does not reconstruct missed events.

Storage, failure behavior and disclosure

The public plugins.plugin_storage.plugin_data_dir() helper selects this plugin's
active-profile data directory; Gatekeeper does not reconstruct home from raw
HERMES_HOME, read credential/config files or import hermes_cli internals.
It creates data.db there with owner-only permissions (directory 0700, file 0600),
using SQLite transactions and explicitly closed connections. Permission/storage
errors fail closed. Linux/macOS only; no cross-profile/shared-ledger override.

Each gated decision records UTC epoch time, channel, SHA-256 recipient/thread IDs,
message SHA-256, stable rule and outcome. Uninspectable calls use an unresolved
identity and hash their input envelope. An escalation (rule unrecognized_send_tool,
outcome approve) uses the same unresolved identity and hashes the tool name plus
arguments; its channel column holds the tool name when it is a well-formed name of up to
64 characters (otherwise unrecognized_tool), so /gatekeeper log shows which tool asked.
Arguments are never stored in clear. Inbound STOP decisions, consent source/date,
timezone/region, owner template variables, pending/sent/failed thread events and
inbound-event hashes are also stored. No raw outbound/inbound message-body log
exists.
Identity columns store hashes, not raw recipient IDs. Source strings and template variables
are owner-entered plaintext and can contain sensitive content; approved templates
live in the owner's settings. Hashes are pseudonymous, not encryption or anonymity,
and short/repeated message text or addresses can be guessed. No retention pruning
is automatic; back up and retain the profile-local database according to your policy.

Send evaluation, logging and reservations share an immediate SQLite transaction,
so concurrent routine sends cannot both pass on an empty thread. A reservation
counts as ours before dispatch; it is not proof of delivery. Explicit host
blocked/cancelled outcomes with a matching tool-call ID release that reservation.
Confirmed success moves the outbound ordering boundary to observation time. Unknown,
partial, skipped, missing-ID or missing-observer results remain conservative pending
holds; an automatic inbound reply cannot unlock a still-pending send. After a true
observation failure, repair/reload is required. Normal expected rejections do not
poison service health. Decision outcomes are policy decisions, not delivery receipts
or proof that a human approved.

No network calls, subprocesses, model calls, telemetry, credential reads, external
file scans or background jobs are made by the plugin itself.
It reads only its
settings via ctx, hook arguments/results/inbound snapshots in memory, and its own
persistent data. Error messages/log lines never echo tool arguments or exception
text. It cannot repair a host that fails to load/register the plugin, intercept
unseen execution paths, or guarantee an audit write when storage itself is broken;
recognized sends are blocked in the latter case.

Contributor workflow (local checkout, not the catalog install path)

From a checkout of this repository:

python3 -m venv .venv
.venv/bin/python -m pip install -r requirements-dev.txt
.venv/bin/python -m pytest -q -W error --tb=short
.venv/bin/python -m examples.dry_run
hermes plugins validate . --install-deps
hermes plugins doctor . --ci

Tests and the dry-run use invented data, temporary SQLite and synthetic public host
contexts; they never send messages. Behavior against a real Hermes host (plugin
loader, hook dispatcher, approval gate, local-session slash-command context) is
exercised separately with hermes plugins validate/doctor and the manual checks in
docs/HOST-CONTRACTS.md.

README from the reviewed commit e6fb3d7; it updates when the author re-pins.

More by percyhendrux123-prog

5 plugins by percyhendrux123-prog in the catalog.

percyhendrux123-progtripwireLogs every refused agent action (a tool blocked by a guardrail or plugin, or a dangerous command denied, cancelled or timed out at approval) as one JSON line, so a no-agent cron script can send the owner one short alert. Observer only, fails open. Disclosure — appends one JSON line per refused action to ~/.hermes/tripwire/events.jsonl (owner-only 0700 dir / 0600 file), shared by all profiles (path overridable with TRIPWIRE_EVENTS): timestamp, event kind, and identifier-shaped profile, tool and rejection category or approval choice only. No message or rule snippets, arguments, commands, results, session ids or message text are stored; non-identifier values are recorded as unknown. A pre_tool_call hook reads ~/.hermes/tripwire/TEST_BLOCK and, only while that file exists, blocks the single tool it names for the single profile it names (a drill switch to prove the alarm). No network calls, no shell commands, no background processes, no stored credentials, no telemetry.Communitytools1percyhendrux123-proghermes-axiom-agentAdds the nine-article Axiom Doctrine as pre_llm_call context (compact form capped at 1800 characters, full at 4000, owner additions separately at 800) and a /doctrine command that reports, per article, whether a matching enforcer plugin (standing, hermes-axioma, tripwire, handydandy, hermes-gatekeeper) is loaded and enabled. Enforces nothing itself: no tools, middleware or approval hooks, and ENFORCED means an enforcer is loaded and enabled, not verified coverage. Reads an optional owner additions file and only the top-level name line of user-plugin manifests, without following symlinked files; makes no writes, network, subprocess or model calls. No Python dependencies.Communityautomationpercyhendrux123-proghermes-axiomaInline signal color in the agent's normal reply. The agent marks the few phrases that deserve a second look (stop, caution, go, whisper) and the plugin renders them per surface with color, a style cue and a word, never color alone: Desktop through KaTeX color, CLI/TUI through truecolor ANSI, Telegram/Discord/Slack as bold plus the word, SMS and email as the word only. Green renders only when a tool call Hermes reported as successful in this session backs it; a rarity budget caps marks per reply; in completed replies Axioma transforms, malformed tags in prose are removed when a recognized tag is present; malformed-only replies, preserved code and raw tags during streaming can still show brackets. A color-vision-safe palette and a style-and-words-only mode are one setting away. Secondary: an advisory card (intent, light, signal, question) staged before configured send/spend/spin/scrub tool calls and shown through Hermes's own approval prompt. Each card and notice carries a short card code (a keyed hash of the exact text shown, the tool, its arguments and the card id), logged when shown and again with the answer; /signals verify <code> reports in plain words whether what was staged, shown and answered line up (spec section 5.6, shown equals staged). Advisor, not a gate: it never blocks, approves or executes anything. Disclosure — before configured send/spend/spin/scrub tool calls (by default send_message, email/post/publish tools, payment/checkout tools, cron and kanban creation, delegate_task, terminal deploy and git push/gh pr commands, and terminal rm/mv when no workspace is configured) it escalates the call to Hermes's own approval prompt with an advisory card, using a fresh rule key each time so remembered Session/Always approvals never skip later prompts; under cron and other unattended runs Hermes's approval policy (default deny) then refuses those calls; it never auto-approves or executes anything, makes no network, model or subprocess calls, reads credentials only as presence checks through agent.secret_scope, adds a short markup guide to the system context through pre_llm_call, rewrites the agent's reply text to render the marks, and writes metadata, salted span hashes and keyed card-code digests (no message bodies) to an owner-only SQLite file plus a separate owner-only key file under its own plugin data directory. Optional operator-configured preflights probe file-presence metadata for required SKILL.md paths under external skill roots and lstat metadata for external device-lock paths; the policy resolves configured workspace/output roots and candidate remove/move paths, including symlinks, for containment checks, without reading those files' contents. Pasted-card verification optionally imports undocumented gateway.platforms.base_exec_approval text helpers and agent.i18n.t behind one compatibility function to recognize localized host framing, falling back to built-in English text if they are absent or change; it neither inspects approval state nor overrides approval. The card-code key must be owner-only; if its permissions cannot be confirmed, durable verification is refused.Communitytoolspercyhendrux123-proghermes-standingEarned trust for Hermes workers. Each profile has a level (L0 read, L1 draft, L2 act, L3 trusted) that decides which tool calls need the owner's approval, and a local record of checks that held up and checks that did not. A worker can always report on its own card (block, comment, request review, heartbeat), but it cannot mark the task done on its word: kanban_complete is blocked until the task has a passing standing_proof in the same session, and Standing runs that check itself (owner-scoped file, owner-allowlisted HTTP URL, or exact owner-allowlisted read-only command). Ten clean passes raise a level (capped at L1 automatically; L2 needs the owner's explicit opt-in); one failed check lowers it; a check that could not run is logged as unavailable and changes nothing. Seeds only create new actors, so restarts never undo a demotion. /standing shows the record, /standing log the last events, and /standing set (local owner only) changes a level. Optional same-machine shared ledger for a governor and its workers, and a short score line on the task card after a completion or demotion. Disclosure — pre_tool_call returns Hermes' own approve directive for every tool call outside the profile's level (at the default L1 that includes every terminal/shell, send and unknown-tool call) and blocks kanban_complete until a Standing proof passes in the same session; a worker's block/comment/review/heartbeat calls on its own assigned card skip the Standing prompt; under cron and other unattended runs Hermes' approval policy (default deny) decides; it adds a guidance note to the model context through pre_llm_call; it never auto-approves or calls a model, and makes network or subprocess calls only for owner-scoped proofs (HTTP GET with no redirects, proxies or credentials; exact allowlisted argv, shell=False, minimal environment); it stores actors and hashed proof events in SQLite under its plugin data directory (or an owner-set db_path) and, when card_line is on, posts task-card comments through the public kanban_comment tool.Communityautomation