Guardrails that follow what a conversation has read. Once a session reads untrusted content (a web page, a browser snapshot, an MCP or third-party plugin's output, a customer's message), airlock seals it: messages to anyone but the owner, network writes, pushes and deploys, memory, skill and cron writes, browser input, URLs that could carry data out and code that reaches the network go through Hermes's own approval gate. Walls hold in every session: credential paths and secret files, the guard's own files and Hermes's config, shell startup files, other profiles, commands that switch approvals off. Shell commands are read the way the shell reads them, so a command that computes or hides what it runs (eval, base64 | sh, $G push, wildcards, write-then-run) is refused until written in clear. Subagents inherit the seal. A project's AGENTS.md, CLAUDE.md, .cursorrules or .hermes.md that Hermes loads at startup seals the session from its first turn until the owner has reviewed it (hermes airlock trust), and again when its content changes (a cloned repository, a pull). It also catches an injection by its signature: recognized secrets never leave in a call; a private marker in the agent's context locks the session if it shows up in a tool call; a send to an address the untrusted content named is blocked; repeated refusals lock the session; lookalike Unicode in commands is refused. A file behind a wall cannot leave as a message attachment, and attached files are checked for secrets. A call that names a cloud metadata service is refused. Plugin and MCP tools nobody admitted need approval. Observe mode, stop switch, hermes airlock check|demo|log|trust. On Windows, paths are read in every form Windows and Git Bash give them, and a path on another machine is refused without being opened. Tested on a corpus of 362 attacks and everyday commands, on Linux, macOS and Windows, and end to end in the official image. Disclosure — hooks only (pre_tool_call, post_tool_call, pre_llm_call, subagent_start, on_session_reset, post_approval_response), no tools. No network, no LLM call, runs no command. Reads the profile's config.yaml (home channels, approvals mode) and its own policy file (<HERMES_HOME>/airlock.yaml); lists directories to expand wildcards in commands it checks; reads the first 200 KB of each file a send_message attaches, locally, to look for secrets. On Windows, reads the machine's own name (no lookup), tests which drive letters exist, and imports tools.environments.local._find_bash to know Git Bash's root. On a session's first turn, imports agent.prompt_builder.discover_context_files and _project_context_suppressed and agent.runtime_cwd.resolve_context_cwd (guarded) to know which project context files Hermes loaded, and hashes them (sha256). Imports Hermes's attachment reader (gateway.platforms.base.BasePlatformAdapter.extract_media) to see exactly what a message would attach, with its own reader as a fallback. Writes under plugin-data/airlock: a journal of decisions, taints and approvals that includes tool arguments, truncated (journal.args: none leaves them out), the taint state, the stop switch and the context files the owner trusted (path and sha256). When nobody can approve (--yolo, /yolo, hermes -z, approvals.mode: off, cron, -q or webhook set to approve, a subagent under delegation.subagent_auto_approve) it blocks instead of letting Hermes auto-approve; to know it, it imports gateway.session_finite.unattended_turn and reads tools.approval (_yolo_active, _unattended_contexts, approval_context._get_approval_mode), each guarded, config.yaml as fallback (policy approvals_off: follow restores Hermes's behaviour). Install scan: caution, because the default policy and the shell reader name the patterns airlock guards against (startup files, crontab, credential commands, exfiltration shapes).