Configuration
Exclusive web research
Configure the pinned research relay and optional VPN readiness evidence.
Source:
docs/16-research.mdate21be475a3af· docs version 0.4.37
Tentaflake uses the public MIT-licensed
tentaflake-research service for
web access. Its stdio MCP server is named secure-research-tool; it exposes
research_job, research_search, research_fetch, research_browser and
research_read. Model requests continue through the separate LLM broker.
The service is a pinned flake input, imported by nixosModules.default. An
operator must configure the VPN and service before enabling a research agent.
The template does not create an Internet route or obtain provider credentials.
Configuration
Section titled “Configuration”Declare a registered Research-capable adapter with mkAgent or either
compatible runtime builder, then use its complete OCI container name:
tentaflake.research.agents = { hermes-assistant.uid = 62101; zeroclaw-assistant.uid = 62102;};services.secureResearch = { serviceUid = 4201; egressUid = 4202; vpnInterface = "wg0"; resolvers = [ "9.9.9.9" ];};Choose distinct unused host UIDs. Each relay has its own host UID, even when two containers use the same internal UID. The service authorizes job ownership using the relay’s peer credentials. These UIDs are service identities, not host user-namespace remapping for the containers.
This fragment does not configure WireGuard or VPN readiness. Select the reference observer below, or supply fresh, trusted observations under the same lease contract described in the upstream operations guide and egress control. Missing or stale readiness prevents public requests. Optional search providers, Chromium and OCR require their own explicit configuration. Provider keys and data grants belong in the research service; never put keys in agent settings or the Nix store. Research summarization remains disabled in this topology.
Keep the agent’s LLM broker, disposable worker and workspace quota declarations. A secure automatically started controller requires all four policies. A stopped scaffold can remain offline without research. The old fetch broker must be disabled for every research-enabled agent; its configuration fails evaluation otherwise.
VPN readiness selection
Section titled “VPN readiness selection”nixosModules.default already imports the pinned Research module. Configure its
services.secureResearch.vpnObserver options directly; there is no separate
Tentaflake observer wrapper. The observer is disabled by default, and its extra
evidence selections are opt-in. An operator-owned producer of the generic
observation lease remains valid; no VPN vendor is required.
For an existing, operator-configured WireGuard exit, extend the service declaration:
services.secureResearch = { vpnInterface = "wg0"; resolvers = [ "9.9.9.9" ]; drainSeconds = 60; vpnObserver = { enable = true; linkKind = "wireguard"; egressPathEvidence = true; handshakeWithinSeconds = 180; # Illustrative public key: replace with the selected exit's exact peer key. peerPublicKeys = [ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" ]; firewallMarker = "/run/research-vpn/firewall-ready"; drainMarker = "/run/research-vpn/drain"; };};This does not create a tunnel, enroll management, install VPN credentials or
declare that an exit is ready. Keep VPN credentials on the host. Only the
operator’s firewall installer creates firewallMarker, after establishing the
independent egress firewall; creating the marker alone proves no kernel rule.
Markers must be distinct regular root-owned files without group/world write
access. Their ancestors must be real root-owned directories, without symlinks
or write access for other identities. A missing drain marker means no drain;
an unsafe marker makes readiness fail closed.
| Selection | Evidence and limits |
|---|---|
linkKind = "wireguard" or "tun" |
Checks the selected link kind and that the interface is up. The default is null; interface presence alone does not prove a usable exit. |
egressPathEvidence = true |
Evaluates the configured egressUid through policy-routing rules: unmarked IPv4 must use the tunnel, IPv6 must use it or be denied, and every services.secureResearch.resolvers address must use it. The default is false. |
handshakeWithinSeconds |
Selects WireGuard handshake-age evidence. Use 180 seconds or more; loss can take the selected window to detect. The default is null. |
peerPublicKeys |
Pins the exact WireGuard peer set, at most four base64 public keys. These values are public, not VPN secrets. The default is []. |
drainMarker |
Requests a planned exit change. The default is null; it must differ from firewallMarker. |
Handshake and peer-key selections require linkKind = "wireguard". They grant
only the root observer unit CAP_NET_ADMIN to read kernel WireGuard evidence;
agents receive no added capability, credential or readiness hook. Link/path-only
observation has no capabilities. The observer retains only public peer keys and
handshake times and must never log the kernel dump or secret attributes.
Tailscale or Headscale management membership supplies neither an Internet exit
nor proof of one. For a separately configured exit using a layer-3 tun interface,
select linkKind = "tun" and egressPathEvidence = true, leaving WireGuard
handshake/key options unset. Tun exit identity and liveness are not observed or
accepted by this reference implementation. region is an optional operator
assertion, as is firewallMarker; neither substitutes for observed link/path
evidence. Conservative or unknown routing evidence withholds readiness. The
independent kernel firewall remains responsible for direct IPv4, IPv6 and DNS
denial if the observer, controller or both fail.
See the pinned upstream VPN evidence contract for route evaluation and its limits. Agents cannot select exits, write observations or control leases, execute readiness hooks, or access VPN credentials.
Planned exit change
Section titled “Planned exit change”An exit change is an operator action requiring its own authorization. Keep the
old tunnel and its firewall protection intact throughout draining. The observer
renews observations for at most ten seconds; the controller publishes leases for
at most three seconds. Repeated drain observations cannot extend the controller’s
monotonic drainSeconds deadline (1–300 seconds, default 60).
-
Read the controller’s control lease, not just the observer’s input, and record the current
readygeneration. The default path contains no credentials, queries or exit address. Bound every read by time and bytes:Terminal window sudo timeout 2s head -c 4097 -- /run/agent-research-egress/control/state.jsonRequire a regular, protected root-owned lease of at most 4096 bytes,
version1, a non-nil UUIDgeneration, andvalid_untillater than now but no more than three seconds ahead. A timeout, missing, oversized, malformed or expired lease is unknown/offline, never permission to declare readiness. -
Verify the marker parent protections above, then create the configured drain marker as root. For the example path, refuse an existing path or symlink and use noclobber; do not replace another operator’s marker:
Terminal window sudo bash -c 'set -eCmarker=/run/research-vpn/draintest ! -e "$marker"test ! -L "$marker"umask 077: > "$marker"' -
Repeat the bounded control read until a fresh lease reports
drainingwith the saved generation. Allow at mostvpnObserver.refreshSeconds + 3seconds for this transition (refresh defaults to five seconds). If it does not occur, leave the old exit protected and investigate; a marker alone does not prove controller drain admission. New work is refused once the controller drains. -
Keep the old exit and firewall protected until existing work finishes or the drain deadline expires. Without independent completion evidence, wait for a fresh
offlinecontrol lease; after a stalled controller, wait at least the configureddrainSecondsfrom the first observed drain and for every last valid control lease to expire. Bound the wait todrainSeconds + 3; if the condition cannot be established, stop and inspect rather than changing exits. -
Only after that completion/offline/deadline condition, change the host-owned exit configuration while retaining fail-closed firewall protection. Preserve the firewall marker and controller lock; do not fabricate a readiness lease.
-
Remove only the selected drain marker:
Terminal window sudo rm -- /run/research-vpn/drainConfirm a fresh
readycontroller lease with a generation different from the one saved before draining. The observer must re-prove all selected evidence, even if the exit ultimately stayed the same. If proof fails, remain offline.
Enforced boundary
Section titled “Enforced boundary”The shared projection installs the pinned research-client and its exact runtime closure
as read-only mounts. Only /run/tentaflake-research/CONTAINER is projected into
that agent as /run/tentaflake-research. The root-only host parent hides other
agents’ sockets. The container receives no research credentials or upstream
service socket. Mounts must use normalized paths and retain these exact
read-only capabilities after caller overrides.
The security manifest records the declared mounts. doctor --security accepts
the research socket only when its source, destination and read-only mode match
that declaration; broad /run, writable and other-agent mounts remain unsafe.
gVisor runs with --host-uds=open to connect to projected sockets; host socket
creation remains disabled. Agent IP networking still permits only its declared
LLM broker, or remains network=none. Browser, HTTP and shell networking cannot
gain general egress by changing a tool setting.
Adapter hooks generate upstream-specific settings after shared socket/closure projection. An arbitrary OCI container with a runtime-like prefix cannot opt into Research; validation checks explicit registered instance metadata and the adapter’s accepted capability. OpenClaw’s stopped scaffold has no accepted Research discovery integration. See agent adapters.
Hermes settings disable the web and browser toolsets and register
mcp_servers.secure-research-tool. Additional MCP entries must be local stdio
commands. ZeroClaw settings disable browser, http_request, web_fetch and
web_search, and replace the MCP server list with this stdio service. These
settings are applied after caller settings. The LLM broker permits local
function/custom tool definitions but rejects provider-hosted tools, including
web search and remote MCP, and known provider extension fields. Operators must
select inference-only models; this does not constrain a provider’s internal
implementation or make an arbitrary vendor extension safe.
Each relay has at most four connections and no IP networking. Healthy sessions can remain idle between tool calls. Stop one relay socket to revoke accepted connections immediately:
sudo systemctl stop tentaflake-research-hermes-assistant.socketStopping agent-research.socket also stops all relay sockets. Restart the
selected socket and the agent to establish a fresh client session. Upstream
readiness, budgets and source-retention policy remain enforced by the service.
Verification and limits
Section titled “Verification and limits”The observer option fixture evaluates without building Research closures:
nix eval --option allow-import-from-derivation false --no-write-lock-file \ --impure --json --expr ' let flake = builtins.getFlake (toString ./.); in import ./tests/research-vpn-observer.nix { self = flake; pkgs = flake.inputs.nixpkgs.legacyPackages.x86_64-linux; }'The broader policy and runtime gates are separate workloads:
nix build --no-link .#checks.x86_64-linux.research-policynix build --no-link -L .#checks.x86_64-linux.research-integrationThe policy check exercises remote MCP rejection, missing capability mounts, duplicate relay UIDs, legacy fetch rejection and disabled summarization. The VM runs the real stdio client and service inside real Docker/gVisor capsules with Hermes and ZeroClaw UID values, verifies the generated settings, performs MCP initialization and job creation, and checks direct IP and offline-fetch denial. Its container image is a synthetic Python probe. It does not run the actual vendor agent images or prove their MCP discovery behavior.
Observer option evaluations cover direct module exposure, defaults, selected link/path/handshake/peer/drain arguments and refusal of invalid combinations. They establish configuration wiring, not a working tunnel or live firewall. The pinned upstream separately records a real WireGuard observer/controller VM fixture; its evidence does not establish Tentaflake host integration or a non-WireGuard exit node. Issue #141 retains real-tunnel integration and production acceptance. That VM work was explicitly excluded from the current lightweight-only run; no runtime acceptance or production activation is claimed here.
The upstream synthetic VM suite separately checks encrypted WireGuard egress, direct-path denial, parser credentials, rendering, resources, cross-agent ownership, idle sessions and socket revocation. See its verification evidence. Actual Hermes/ZeroClaw sessions, Podman socket transport, paid-provider billing, production VPN policy, ARM and arbitrary kernel escapes still require deployment acceptance. No production host is activated by these checks. Retrieved content remains untrusted; the network boundary does not prevent prompt injection or disclosure of sensitive text intentionally sent to an allowed destination.
Client recovery
Section titled “Client recovery”The pinned stdio client reconnects future calls after a lost upstream Unix RPC session. Each fresh session repeats protocol negotiation and host peer-UID authorization. Interrupted calls fail without replaying dispatched operations; caller cancellation and explicit client close remain enforced. If the service is unavailable, connection retries remain bounded. Socket revocation still closes accepted relays and prevents research access.
The public research service fixture verifies a real upstream SIGKILL followed by a successful new call from the same stdio process, plus the idle-session and live socket-revocation regressions. Tentaflake’s separate Docker/gVisor fixture checks the packaged MCP interface, exact closure/socket mounts and UID policy. These synthetic fixtures do not establish actual vendor-agent workloads or production VPN/provider acceptance.