The dual-use binding
The one idea behind Herkos: the minimal code context a model is served and the egress allowlist that bounds what it may send should be the same object, computed once. Herkos calls that object a Binding. This page states the idea, separates it from the work it resembles, and says plainly where it holds and where it does not.
The problem
An AI coding agent reads private source and can also call tools that leave the machine. The usual response bolts on two separate mechanisms: one to trim the context sent to the model, another to police egress. Provenance, "which lines of my code actually went to the model", is then reconstructed after the fact from logs, if at all. The two mechanisms can disagree, and the reconstruction is exactly that.
One object, three roles
A Binding is constructed once from a resolved SpanSet (a set of (file, line-range) spans produced by the local tree-sitter code graph). That single value is simultaneously:
- the context the model is served (those spans, canonicalized);
- the deny-by-default read and egress allowlist (a payload is authorized only if every source span it derives from is covered by the set; the zero value authorizes nothing);
- the signed input manifest (the spans are the leaves of an ed25519 Merkle receipt, verifiable offline with only the public key).
There is no API that serves one set and enforces another. "Context set == egress set" is a type invariant, not a convention, because both the serve path and the egress authorizer read exactly the same Binding. That invariant is what ships and what is tested: the two halves provably read one value.
// internal/core: the only value both the serve path and the egress authorizer read.
type Binding struct{ spans SpanSet }
func NewBinding(ss SpanSet) Binding { return Binding{spans: ss} }
// AuthorizePayload allows a payload iff EVERY source span it derives from is covered by
// the bound set. No provenance, or any span outside the set, denies. Zero Binding denies all.
func (b Binding) AuthorizePayload(req EgressRequest) Decision {
if len(req.SourceSpans) == 0 {
return Deny(ReasonDenyByDefault, "no provenance")
}
for _, s := range req.SourceSpans {
if !b.spans.Covers(s) {
return Deny(ReasonOutsideAllowlist, s.String())
}
}
return Allow()
}
AuthorizePayload above is the strong, span-exact form: it needs explicit provenance on each egress request and denies anything outside the bound set. That form is the reference/demo path, not what runs in herkos serve. On the wire the broker has no provenance handed to it, so it enforces the binding as a content tripwire: it fingerprints the served spans' lines and blocks a tool-call argument that carries a served-set-outsider line, after normalizing case and whitespace (so a reflow or recase still trips it). Base64, paraphrase, or splitting a line across calls defeat it. The strong form is what the binding is; the tripwire is what a stdio broker can approximate of it today.
Why this is not the prior art
The pieces look familiar; the combination is not.
| Work | What it binds | How Herkos differs |
|---|---|---|
IFC for agents (e.g. Microsoft Fides, arXiv:2505.23643) | confidentiality/integrity labels through messages, tool calls, results | labels bind to messages; the dual-use binding binds to (file, line-range) spans, auto-derived from the code graph, no manual labels |
| Signed agent receipts (Agent Receipts, Vouched KYA-OS, Signet, mcptrust) | the output envelope of a tool call (method, params, result) | same primitives (Ed25519, Merkle, JCS), but none signs the input context the model saw |
| SLSA / in-toto | "materials" at repo or artifact granularity | the input predicate at span granularity |
| Outbound allowlists (HTTP/domain egress) | agent identity, destination domains, IPs | binds to the code-span context the model was given |
The field is output- and traffic-centric: it can assert what flowed and what was called, never what was allowed in bound to what is allowed out. The span set is the only artifact organized around the input set, and binding it to egress upgrades it from a log to a boundary.
To be clear about what is and is not new here: the idea of constraining what an agent does with tainted context, and of taint that survives transformation, is already in the literature - CaMeL, OCELOT, and NeuroTaint all anticipate it. What is distinct is shipping it in a product as a single span-derived object that is at once the served context and the egress allowlist. Novel in a shipping tool, not novel in concept.
The binding is only real where the broker mediates 100% of the agent's code access and egress. A coding agent that reads files natively (cat, grep, its own Read tool) bypasses an MCP-broker-level binding, leaving the manifest incomplete and the allowlist unenforced. To be complete, the binding must sit on the sole filesystem-read and egress path: a syscall/FUSE-level interception or a policy-mandated sandbox, not an in-path MCP broker. Where the agent can bypass it, the binding is an honest log, not a boundary, and should be described as exactly that.
What it does and does not protect
- Does: bound direct egress of repo content outside the served set; produce an exact, deterministic record of what entered context (versus probabilistic NL taint-tracking, which is ~0.9-F1 at best).
- Does not: stop denial-feedback / causal side channels (
arXiv:2604.04035); stop exfiltration via logs, stdout, or subagents outside the mediated path (the majority of real-world leak channels); stop regurgitation from the model's training memory. A span boundary mediates the read and egress path and nothing beyond it.
Where it is meaningful
Forced-mediation deployments where org policy routes all model context through the broker: regulated or air-gapped shops (defense, finance, medical-device firmware). There the chokepoint is real, the allowlist is enforceable, and line-range granularity is contractually meaningful for license-contamination ("prove the authorized context excluded GPL span X") and data-residency ("these lines never crossed the boundary"). Outside such a topology, the value collapses to provenance-as-log, and the market currently asks for that at file/commit granularity, not span.
As a standard
The cleanest form of the idea is not a proprietary receipt but an in-toto / SLSA inputs/materials predicate at (file, line-range) granularity, captured at the point of generation and verified downstream. That is the interoperable contribution, and a more useful one than another signed-receipt format.
Reference implementation in Go: internal/core (Binding, SpanSet), the SpanGate pipeline, and ed25519 Merkle receipts (pkg/receipt), with the egress half wired into herkos serve as an opt-in content gate. A reference implementation of the idea.
On the wire that opt-in gate lives in the broker's data path: every tool call passes through herkos serve, allowed calls are forwarded and denied ones answered in-path, and every brokered call lands in a signed, context-bound audit log that herkos verify checks offline.