sharness.ai / components / sharness.net · this page as markdown

sharness.net: the only network client

Status: designed; a prototype of its allowlist runs in the browser. The playground's network panel is this component's shape, played by a web page for a Linux guest: every packet seen, a hostname allowlist enforced outside the process, three verdicts on screen. The component itself is not built. Written as of 25 Oct 2026.

Its one job

Every byte to and from the outside goes through it. It is the TLS client, so it is where a hostname can actually be seen and verified; it holds the host, endpoint and vault-id allowlists; it refuses redirects to other hosts and IP literals; and for sgit, in the full split, it does the vault encryption, decryption and signing with keys it gets from .kms for one command.

Why it must be the TLS client

The kernel only sees connect(127.0.0.1, <port>). A relay that forwarded bytes would let the inner process choose SNI and the Host header itself, which is domain fronting: the allowlist would check one name and the server would see another. So sharness.net sends the CONNECT, sets SNI and Host, and verifies the certificate for that name. The same reason the page is the TLS client for the guest in the playground.

The API (proposed)

RequestWhat it isResponse
CONNECT host:443 on the proxy porttransparent mode: the agent's HTTPS_PROXY points here; the tunnel is only opened to hosts on the allowlist, and .net is the TLS client inside it200 and a tunnel, or 403 with the reason
POST /v0/fetch {"method": "GET", "url": "https://…", "headers": {}, "body": null}harness-aware mode: a request by value, checked against host and endpoint allowlists{"status": 200, "headers": {}, "body": "…"} or 403
POST /v0/vault/pull {"vault_id": "…", "op": "read"}for sgit: fetch and decrypt with keys obtained from .kms for this one commandthe objects, decrypted, never a key
GET /v0/logthe requests this component has seen this session, with verdictsJSON lines
GET /v0/policythis component's own policy and its hash

Requests, and their verdicts (designed)

The agent (or bash, or llm) asksVerdictBy
GET https://api.github.com/repos/… with api.github.com on the allowlistserved; logged.net
GET https://pastebin.com/raw/…403 host not on the allowlist; logged.net
GET https://api.github.com/… that answers 302 Location: https://evil.example/the redirect is not followed; 403 redirect to another host.net
GET https://93.184.216.34/403 IP literals are refused.net
CONNECT allowed.host:443 then, inside the tunnel, SNI other.hostimpossible: .net sets SNI itselfthe design
a connect() from the agent's process straight to the internet, bypassing the proxyEACCES: the agent's Landlock policy allows TCP only to .net's portthe kernel
a DNS lookup from the agent's processnothing to look up with: an empty network namespace, or .net resolvesthe kernel, or .net
push to a vault for which the command asked op=readthe KMS returned only the read key; the push cannot be signed.kms

The playground shows the first two verdicts and the sixth, live: a fetch through the page, a 403 from the page, and a connect refused by the kernel before any packet left the process, with the DNS query under a ports-only policy still visible (the panel).

On its own

This is the component the design has argued for longest under another name: an inline proxy that monitors and restricts everything the agent reaches, the model API included. Adopt only this one, point the agent's HTTPS_PROXY at it, give the agent's process a policy that allows TCP to this port only, and every connection has a hostname, a verdict and a log entry, and nothing leaves on a name the allowlist does not carry. It does not read the traffic inside the tunnel in transparent mode; that is .llm's job for the model API, and .fetch by value for a harness-aware app.

What it does not do

It is not a content filter: the secret filter on tool results is in .llm, where the request is seen by value. It cannot stop a process that never uses it; that is the kernel, told by the launcher. UDP is not seen by Landlock, so a process with ports only and no namespace can still resolve names, which the playground shows.

Sources: Components, Architecture, Keys and the KMS §4. The prototype: Linux in a tab.