Here is the problem, more or less as it was handed to me.
Someone builds an agent. It needs to run somewhere that is not their laptop, against data that is not ours, using credentials we should never see. We need to know it is running, because we are responsible for it being up. We need to know how much it ran, because that is how we charge. And we need to be able to tell them what it did when it goes wrong.
Now do all of that without ever holding their keys.
For a long time I assumed one half of that had to give. You cannot license what you cannot see, and you cannot debug what you never touched, so at some point the vendor ends up inside the customer’s data. Every system I had worked with resolved it the same way, quietly, in the direction of the vendor.
This is the design we landed on instead, and more usefully, the reasoning that got us there. Here is the shape first, so you know where this is going.
The three planes. Your machine and your infrastructure are the two ends. Provider and connector traffic, dotted, never touches us. The control plane sits off to the side of the crossing rather than in the middle of it.
The rest of this post is why it looks like that.
The thing I kept seeing go wrong
Experience: what happens to your data when a third-party system deploys onto your own infrastructure.
I have spent a lot of my career integrating third-party systems that run on infrastructure I was responsible for. The pattern I kept watching, in different companies and with different vendors, is that data you never agreed to send ends up on a partner’s systems anyway.
It is almost never one decision. It is three reasonable ones.
The first is telemetry. The vendor needs to know their software is healthy, so they send events home. Events start as counters and timings. Then a support ticket needs reproducing, so the event grows a payload, because a counter cannot tell you which request failed. The payload is your data, and it is now in their logging system.
The second is licensing. The vendor needs to know you are entitled to what you are running, so the software phones home. A licence check that only sends a key cannot say much, so it grows context: which features, how many seats, which workloads, how often. Context is a description of your business, and it is now in their database.
The third is support. Something breaks at two in the morning and you want it fixed, so you hand over the logs. Once handing over logs is the way incidents get resolved, the vendor starts collecting them continuously, because asking you for them every time is slower. Now your logs live with them permanently, and nobody ever ran that decision past your security team, because it was not a decision. It was an incident.
None of those three steps is unreasonable on its own. Taken together, you can no longer answer a simple question about your own system: what exactly does this vendor have? Not “what are they allowed to have”, which is in the contract, but what is actually sitting on their disks tonight.
The problem was never that anyone acted in bad faith. It is that nobody wrote the rule down at the start, so every individual exception was defensible, and the aggregate was indefensible.
So we wrote the rule down first
The constraint we set before designing anything, and the objection it immediately runs into.
Before any of this was designed, we fixed one thing:
The user’s keys, the user’s data and the user’s credentials are the user’s. We do not want access to them.
Not “we treat them carefully.” Not “they are encrypted at rest.” No access. If the answer to what does Viorant have? is going to hold up at two in the morning during an incident, it has to be a fact about what we built rather than a promise about how we behave.
The objection arrives immediately, and it is a good one.
We are a commercial product. We have to license the software, which means knowing what is running. We are responsible for the service being up, which means monitoring it. Customers will ask us why their agent failed, which means seeing something about the failure. Every one of those needs is legitimate, and every one of them is exactly the wedge that opened the door in the story above.
So the tradeoff looked mandatory. Either we can see enough to run a business, or the customer keeps their data, and you pick a point on the line between them.
We decided not to pick a point on the line. The interesting question is not how much access is acceptable, because that question has no stable answer and drifts in one direction over time. The interesting question is: what is the smallest thing we could receive that still answers “is it running” and “how much did it run”, and can that thing be structurally incapable of carrying customer data?
That reframing is what produced the architecture. Once the answer has to be a structure rather than a policy, most of the design decisions stop being choices. Four steps came out of it, and the order matters, so I will take them in order.
Step one: a place to build it where nothing leaves
The local plane: where you build the thing, and what is deliberately not in the room while you do.
The first step is the least surprising one, which is why it is worth being precise about it. You build and test the agent on your own machine.
That happens in one of two shapes depending on how you like to work. There is a desktop application, where a Space is a project: prompts, skills, memory, the model you are pointing at, the connectors it is allowed to touch. And there is a terminal, where vio init turns a folder into the same thing, and the project is files you can read and commit like any other code. They are the same capability, and the artifact they produce is identical. Pick whichever one matches how you already work.
What matters is what is happening on the network while you iterate.
Your provider keys are in your operating system’s keychain. When the agent you are building calls a model, the call goes from your machine to your provider, authenticated with your key. When it calls a connector, it talks to that connector’s server. We are not proxying either one. There is no leg of that round trip that we are in a position to read, because there is no leg of it that touches us.
Every path, and whether we are on it:
| What is happening | Where it goes | Are we on it? |
|---|---|---|
| Your agent calls a model, under your own key | your machine straight to the provider | No |
| Your agent calls a connector tool | your machine or your instance straight to that connector | No |
| You run on Viorant credits, having brought no key: while you build, or in a deployed agent | through our gateway, metered per call | Yes, deliberately. Metering has to be server-authoritative |
| A turn of the build assistant | through our gateway, charged | Yes, deliberately. Same reason |
| You connect a connector and it needs an OAuth redirect | the provider bounces off our relay and straight back to your machine | Relay only. The token lands on your machine; we never hold one |
| You deploy | one call: verify, licence gate, write the record, stand it up | Yes, for that one call. We are not on it afterwards, unless the agent runs on Viorant credits (above) |
| Your deployed agent gets its credentials | your client encrypts them and writes them to the deployment’s own storage; the instance decrypts them | No. We mint the write grant and cannot read through it |
| Your deployed agent reports in | heartbeat and content-free run record | Yes, and that is the whole payload |
| A schedule runs your deployed agent | we call the agent with a token we sign for that one run, plus the input you saved with the schedule | Yes, as the caller. We hold the schedule and its input. What the run produces does not come back to us |
| You ask for your deployed agent’s logs | we read them through from the log store, scoped to that agent | Read-through. We keep no copy |
Two rows say yes for the same reason, and it is the honest one: a client that meters itself is not metering. Everything else is either no, or yes for exactly as long as one call takes.
I want to name the exception rather than let you find it, because the whole argument of this post is that the honest version of that table is the product.
If you use our managed inference tier, the one where you have not brought a key and you just want a model to answer, that call goes through us. It has to. Metering has to be server-authoritative, because a client that meters itself is not metering. The same is true of the build assistant: every turn of it runs through our gateway and is charged for. Bring-your-own-key is the principle, and the managed tier is a brokered convenience for people who have not brought one. If you have brought one, we are out of the path entirely. That holds for a deployed agent too. An agent that runs on Viorant credits sends each model call through our gateway for as long as it runs, which is the one case where we sit on a running agent’s path. An agent deployed with your own key does not.
The second thing this step gives you is the one people underrate: you can run the real artifact locally before it goes anywhere. Not a simulation of it, not a dev mode with different behaviour. The same file, in a container, on your machine, against your credentials. The deploy step later on does not change what runs. It changes where it runs and who is paying for the compute.
That turns out to matter more than it sounds like, because it means the question “does this work?” is answered before the question “where should this live?” is asked. Most of the trust problems in this space come from those two being the same event.
Step two: compile it to an artifact, not a runtime
The pivot the rest of the architecture hangs on: what leaves your machine is a file, and a file can be hashed.
Here is the decision that made everything after it tractable.
When your agent leaves your machine, it does not leave as your source code and it does not leave as a running process we now operate. It leaves as one file: a .vio. That file declares the prompts, the skills, the memory, the model references and the framework it expects to run under. It is the whole agent, stated rather than executed.
The reason to do it this way is not elegance. It is that a file can be hashed, and a running process cannot.
Every boundary in this system is a place where something arrives from somewhere less trusted and has to be checked. If what arrives is a process handle or an API session, “check it” has no real meaning; you can authenticate who is calling but not what they are asking you to run. If what arrives is a file, “check it” means recomputing its digest from the bytes in front of you and comparing. That is a question with an answer. Everything else in this post is downstream of having made the crossing checkable.
What the artifact declares, and what it does not
There are five kinds of thing in a .vio, sorted across two tiers by a single rule: sign what you author, digest-pin what you reference or what is mutable.
The prompts, skills and agents are yours. You wrote them, so they carry a signature. The memory and the model are not: memory is inherently mutable, and a model is a reference to something living somewhere else that we have no business claiming to have authored. Those carry integrity only. You get to prove the model reference has not been swapped since the artifact was built, which is the honest claim, rather than a signature that implies something about the weights that nobody can actually stand behind.
That split is not fussiness. It is the difference between a system that tells you what it knows and a system that signs everything uniformly and therefore tells you nothing.
The digest rule
The digest is the load-bearing detail, so here it is exactly:
digest = sha256(utf8(JCS(jsonProjection(value))))
A .vio is YAML, but the digest is not taken over the YAML text. It is taken over the canonicalized JSON projection of the parsed structure, using RFC 8785.
That choice buys a specific promise: reformatting is signature-safe. Change the key order, switch a flow mapping to a block one, add quotes, reindent the whole file, and the digest does not move, because none of that survives into the projection. Change a single character of a prompt body and the digest moves, because the body is a string and the string changed.
This matters more than it sounds like for anything that lives in Git. An artifact whose digest changes when a formatter runs over it is an artifact nobody will keep verified, because the verification will cry wolf on every commit and people will turn it off. Making cosmetic edits safe is what makes real edits detectable.
One deliberate departure, which I will flag because someone will find it: we do not Unicode-normalize. RFC 8785 explicitly does not normalize strings, and our earlier design text wanted NFC over skill trees. The two rules are incompatible and one of them had to win. JCS won, because the canonicalization standard winning is less surprising than the canonicalization standard losing to a local preference.
Making cosmetic edits safe is what makes real edits detectable.
Where the framework goes
The artifact declares which framework it expects. That single declaration does two jobs later, at two different boundaries, and keeping them separate is the point.
At deploy time it selects a base image, pinned by digest rather than by tag, so that “the CrewAI image” means one specific set of bytes and not whatever was most recently pushed under that name.
At run time it selects an adapter. This is the part worth being careful about, because the intuition runs the wrong way round. The adapter does not translate your code into the artifact. The artifact is framework-neutral by design, and the adapter lives at the far end, inside the thing that executes it, where it lowers the neutral description into whatever shape that particular framework demands.
The direction matters for a reason that is not academic. If the artifact were produced per framework, then the framework would own the format and every new framework would fork it. Because the artifact is neutral and the adapter sits on the consuming side, adding a framework is writing one adapter against a fixed contract. Nothing upstream of it changes, and nothing already built stops working. The framework became the first adapter instead of the architecture, which is a thing we got wrong once already and had to go back and undo.
The adapters are held to that contract mechanically rather than by review: an adapter that reaches for ambient authority, that goes looking for a network or a credential or an environment it was not handed, fails the build. An adapter is a pure lowering from one shape to another, and CI is what keeps it one.
And the part that makes it real
The producer and the verifier compute that digest from the same code, not from the same specification. One implementation, imported by the thing that writes artifacts and the thing that checks them.
That sounds like a small engineering hygiene note and it is actually the crux. Two independent implementations of a canonicalization rule agree right up until the day they do not, and the day they do not is the day every artifact in the field fails verification for a reason nobody can reproduce. A digest rule that lives in prose is a digest rule with a scheduled outage in it. So it lives in one place and gets imported.
Step three: deploy it onto infrastructure you own
The step that makes the refusal in step two hold. The deploy target is an interface, and which implementation you pick is a deployment decision rather than an architectural one.
At some point the artifact has to go somewhere and run. This is the step where most of these systems quietly become the thing I described at the top of this post, because “where it runs” is usually decided by the vendor and the answer is usually “with us.”
So the first thing we built here was not a destination. It was a seam.
A deploy target is an interface. It takes a verified artifact and an instance specification and it is responsible for making something run. Everything above it, the parsing, the verification, the licence check, the image selection, is identical regardless of which implementation is on the other side. That is the whole design, and it was worth building before we needed it.
Two implementations exist today.
Local Docker stands the artifact up in a container on your own machine. This is the one from step one: the real artifact, not a simulation of it, so the thing you tested is the thing you later deploy.
Viorant Cloud is our managed target. One service per deployed agent, one instance each, opaque names, in infrastructure we run. It is the path for people who want an agent deployed and do not want to think about a cloud account at all, and it is the honest answer to “can I just click deploy.”
The third implementation is the one the interface was cut for: your own infrastructure. The shape is a short infrastructure-as-code run in your cloud account that creates the service accounts, the services, the DNS and the networking the agent needs, and then stands the container up inside it. Declarative on purpose, so that infrastructure you are being asked to trust arrives as something you can read in a diff and reject line by line, rather than as a script that does something to your account while you watch a progress bar.
I want to be straight about where that one is. The interface exists, the two implementations above are running against it, and customer infrastructure is the implementation the seam was designed to accept rather than one you can select today. I am describing it here because the design only makes sense if you can see what it was built to allow, and because a deploy target interface with exactly one vendor-operated implementation behind it would be a marketing diagram rather than an architecture.
What that third target is worth is worth stating precisely, because it is the payoff for everything in step two:
The container runs where your data already is, so the data does not travel. It runs under identities you created, in a network you control, subject to the policies you already wrote for everything else in that account. Your credentials reach it without passing through us. And it runs in your jurisdiction, which turns data residency from a feature request into a property of where you chose to deploy.
What the deploy creates, and where:
| In your cloud account | Anywhere of ours | |
|---|---|---|
| The service accounts the agent runs as | created by the infrastructure run | — |
| The service that runs the container | yes | — |
| DNS and networking | yes | — |
| The agent’s own storage and key | yes | — |
| Your credentials, encrypted | written there by your client | — |
| The image the container starts from | pulled, pinned by digest | published by us, readable by anyone |
| That a deployment exists, and its entitlement | — | one record |
| That it is alive and that it ran | — | content-free |
| If you schedule it: when it runs, and the input you gave it | — | the schedule record |
| What it actually did | — | nothing |
The last row is the one worth reading twice. It is not a retention policy. There is no column for it on our side.
How credentials get there
This is the part of the deploy that people assume must be where we cheat, so it is worth walking.
The agent needs secrets to do its job: provider keys, connector tokens, whatever it was built against. Those are on your machine, and they need to end up inside a container that is not on your machine. The obvious implementation is that you give them to us and we put them there. We did not want that implementation, because it makes us a holder of every customer’s credentials, and a control plane that holds every customer’s credentials is a single breach away from being the worst day in several other companies’ years.
So the credentials never pass through us. Your client encrypts them and writes them directly to the deployment’s own storage, under a short-lived write grant that the control plane issues. The instance reads them from there and decrypts them itself.
The property that does the work: the grant we mint authorizes writing to that location, and it does not authorize reading it. We can hand you the ability to put a sealed envelope in a box. We cannot open the box, and we did not build a way to. The deployer sets two environment variables telling the instance where its envelope is, and never looks inside it.
Every deployed agent now runs as its own identity, on its own storage, under its own key, so no shared identity can read one agent’s state on behalf of another. The part I will label as direction rather than present tense is delivery: everything sent to an instance sealed to a key it registered, and pulled over its own outbound connection rather than pushed in. That last detail is not cosmetic. A system that only ever dials out works inside a network that does not accept inbound connections, which is exactly the kind of network the people who care most about this run.
The isolation is done. The sealed pull is in progress. The shape is settled.
Step four: two conversations, and one you opt into
Having put the agent somewhere we cannot reach into, the two things we still need back, and how narrow we were willing to make them.
We have now deliberately put the running agent out of our reach. Which leaves the problem I said at the start looked like a contradiction: we still have to license this thing, and we are still on the hook for knowing whether it is up.
The answer is that there are two conversations between a deployed agent and us that always exist, they are the narrowest we could make them, and neither of them carries your data. A third exists only if you ask for it.
Conversation one: admission
A licence check is usually a thing running software does to itself, periodically, by phoning home. We put it somewhere else entirely.
There is exactly one moment when an agent crosses from your machine onto infrastructure that costs someone money, and that moment is a single call. What happens on that call is, in order: the artifact is parsed, its digest is recomputed from the bytes that actually arrived, the licence gate runs, a record is written, and only then is anything stood up.
The order is the argument. Verification happens before entitlement, because asking whether you are allowed to deploy something is meaningless until you know what the something is. And entitlement happens before provisioning, because the cheapest place to refuse is before any infrastructure exists.
What this buys is that the licence check is not inside your process. It is not a library you are running that could be patched out, and it is not a callback your agent makes at runtime that we could quietly grow into a telemetry channel. It is an admission check at a door, and once you are through the door we are not asking you again. Your deployed agent does not phone home to ask permission to keep running, because permission was not the kind of thing we decided to implement.
The entitlement itself is one axis. A single field decides your price, your monthly allowance, how many agents you can have deployed, and which models you can reach. Not four systems that each have an opinion about what you are entitled to and drift apart over eighteen months. One field, and the four answers are derived from it, so they cannot disagree.
Conversation two: is it alive, and what did it do
The second conversation runs the other way, from your deployed agent back to us, and this is the one I was most careful about, because this is where the story at the top of this post actually happens. Nobody ever leaks a customer’s data through a licence check. They leak it through observability, one reasonable enrichment at a time.
So the outbound channel carries two things: a heartbeat, and a content-free run record. That the agent is alive. That a run happened, when, and whether it finished. Not what was in the run. Not the prompt, not the response, not the arguments to a tool call, not a sample “for debugging.”
The word doing the work there is content-free, and it is a structural property rather than a redaction policy. There is no field in that record for your data, so there is no code path where a future well-meaning change adds one without somebody having to explicitly design it and argue for it. Redaction is a filter you can misconfigure. Not having a field is not.
Not redacted on the way out. Never modelled on the way in.
The agent authenticates with a deployment identity when it talks to us, and we store a hash of it rather than the thing itself. Revoking a deployment makes that identity stop resolving. In the other direction, the key that lets something call into a running instance authorizes running it and specifically does not authorize reconfiguring it, because “can invoke” and “can change what this is” are different powers and merging them is how a compromised caller becomes a compromised deployment.
Conversation three, if you ask for it: schedules
If you want an agent to run on its own, every hour or every morning, something has to start it, and that something is us. You set a cadence and, if you like, a fixed input, and we store both. When it is time, we sign a token for that one run and call the agent with it and the input. The agent checks the signature before it does anything, the token lets it run and nothing else, and what the run produces goes wherever the agent sends it. We get the same content-free run record as for any other run.
The honest cost is that the schedule’s input lives on our side. If you put something sensitive in it, we hold that. Keep secrets in the agent’s credentials, not in its schedule.
And logs, which are the interesting case
Logs are where I expect the most pushback, because logs are the thing customers most want from a vendor and the thing vendors most want to keep.
When you ask for your deployed agent’s logs, we read them through from the underlying log store, scoped to that one agent, and hand them to you. We do not keep a copy. There is no log archive on our side, which means there is no log archive to breach, to subpoena, to accidentally index, or to find in a storage bucket in two years.
The cost of that is real and I will not pretend otherwise. We cannot do sophisticated cross-customer analytics on log content. We cannot proactively notice a pattern in your traffic and email you about it. When you ask us a question about something that happened inside your agent’s execution, sometimes the answer is that we do not know.
I think that is the correct trade, and I think it is the one people actually want once it is put to them plainly. “We cannot show you that because we never had it” is a better sentence than every alternative that ends with us having had it.
What that actually buys
Four things that fall out of the four steps, none of which we had to build separately.
Everything so far has been mechanism. Here is what the mechanism is for, and the test of whether a design like this was worth the trouble is whether the benefits are consequences rather than features. These four were not built. They fell out.
Hosting stopped being an architectural decision. The same artifact runs in a container on your laptop, in our managed cloud, or against the target interface we cut for your own account. Nothing about the agent changes between those. That means the choice can be made late, revisited, and made differently for different agents in the same organisation, because it is a deployment decision rather than a commitment you made when you picked a vendor. It also means that if we are wrong about our runtime substrate being the right choice, that is a wrong we can fix, which is not a sentence most platforms can say about their own hosting.
You can state the blast radius of trusting us. Not as a promise about our security practices, which is what a vendor questionnaire actually collects and which is worth approximately nothing at two in the morning, but as a statement about what we hold. We do not hold your provider keys. We do not hold your connector tokens. We do not hold your logs. We hold who you are, what you are entitled to, that a deployment exists, that it ran, and, if you scheduled it, when to run it and the input you gave it. If we were breached tomorrow, that list is the exposure, and you can evaluate it now rather than finding out later.
The agent is portable because the artifact is. Nothing about a .vio is entangled with our runtime. It declares what the agent is rather than how our system happens to run it, and the framework-specific lowering happens at the far end against a fixed contract. The practical consequence is that moving an agent is a deploy, not a migration, and that the thing you built does not quietly become an asset that only works while you keep paying us. I would rather compete on being worth using than on being expensive to leave.
Data residency stopped being a feature request. It is the single most common reason a serious customer cannot adopt a tool like this, and it usually gets answered with a regional deployment roadmap. If the agent runs in your account, it runs in your jurisdiction, and there is no roadmap involved. The data never travelled, so there is nothing to localise.
Why any of this should be believed
The difference between a vendor that promises not to hold your data and a system with nowhere to put it.
Everything I have described so far could be said by a company that was lying, or more likely, by a company that was telling the truth today and would drift over the next three years under perfectly ordinary commercial pressure. So the last thing worth saying is why any of it should be believed, and the answer is not that we are trustworthy.
Here is the distinction the whole design turns on.
An architecture that could hold your keys and promises not to is a policy. Policies are real, and they hold exactly as long as the incentives and the people who wrote them do. A policy survives a reorganisation, a funding round, an acquisition and an outage at roughly the rate you would expect.
An architecture with nowhere to put your keys is a structure. Structures are boring. They hold because changing them is work that someone has to specify, build, review and ship, and that work is visible.
Every choice in this post is an attempt to move something from the first category into the second:
Policy, and structure:
| The usual promise | What this makes true instead |
|---|---|
| “We encrypt your keys at rest” | We are not in the path where your keys are used |
| “We only collect what we need” | The run record has no field your content could go in |
| “We will not look at your data without permission” | The grant we hold writes and does not read |
| “We retain logs for N days” | We keep no copy of your logs at all |
| “We verify uploads carefully” | Nothing unverified can reach provisioning, because verification is upstream of it |
| “You can export your data and leave” | The thing you built is a file that was never entangled with our runtime |
| “Trust our implementation of the format” | The format is open, and the suite that proves you implemented it is public |
The left column is a set of intentions, and intentions are held by people under pressure. The right column is a set of facts about what exists.
And the format is not ours
There is one more move required, because a post arguing against “trust us” cannot end with a file format that only we can read.
The .vio specification is open. The spec, schema, examples, fixtures and the adapter registry are CC-BY-4.0. The tooling, the reference linter and the conformance suite are Apache-2.0, and that choice was deliberate over MIT, because MIT grants no patent rights and a format nobody can safely implement is not open in any sense that matters. The format patents carry a royalty-free non-assertion covenant scoped to conforming implementations. VIO stands for Verifiable Intelligence Object, and we are its steward rather than its owner.
The line that matters:
Implementing the specification requires no agreement with anyone. Claiming conformance requires passing the conformance suite, and nothing else.
No partner programme, no certification fee, no relationship with us. If you want to write a deployer that consumes .vio artifacts and competes directly with ours, the specification, the fixtures and the suite that proves you got it right are all sitting there. That is the point. An artifact format is only a trust boundary if more than one party can implement it, and a verification you can only perform using our software is not a verification. It is a courtesy.
The next thing: attaching a person to a digest
Where this goes next, and the difference between knowing a file is intact and knowing who stood behind it. This section describes work in progress rather than something you can use today.
Everything above rests on a digest, and a digest answers exactly one question: is this the artifact that was built? That is integrity, it is genuinely useful, and it is not the question people actually ask once agents start doing things that matter.
The question they ask is: who stood behind this, and when?
That is attestation, and it is the same primitive one signature and one record further on. It is the system we are building next, and I am describing it here because it is the reason the digest work was done so carefully rather than a separate product bolted on later.
What it claims, and what it refuses to
The claim is deliberately narrow: an authenticated user, or an authorised member of a verified organisation, attested to this exact content at this time.
The refusals are longer than the claim, and they are the part that makes it credible. It does not determine authorship. It does not determine copyright ownership, originality or non-infringement. It does not determine licence validity or legal enforceability. It does not certify compliance with any named regulatory framework, and it will not, however much anyone would like a badge that says it does.
I am putting the refusals before the mechanism on purpose. Nearly every provenance system I have looked at states a narrow cryptographic fact and then markets a broad legal one, and the gap between those two is where the entire category loses its credibility. A signature proves a key was used. Everything past that is an inference someone is selling you.
The shape
Every signature requires a human. Specifically, it requires a freshly authenticated human, on a surface that shows them the exact content, its digest, the identity being signed under, and what it will cost, before they approve anything. Machines are first-class participants: a CI job, an agent or a script can request a signature, inspect it, poll it and cancel it. They cannot approve one. A provenance system where an automated caller can mint an attestation is a provenance system that attests to nothing.
The services are split by what they are permitted to do rather than by what they compute. The service that handles requests and approvals cannot invoke a signing key and cannot debit a credit. A separate worker, behind private ingress, is the only thing in the system allowed to do either. Before it signs, it re-reads the frozen request, re-canonicalizes it and compares: if any material field or the digest has moved since the human approved, it fails, and it fails without charging anyone. The human approved a specific set of bytes, and the only thing that can be signed is that set of bytes.
Underneath: a canonical digest under a versioned canonicalization profile, a primary signature in hardware, an independent counter-signature under a different algorithm and a separate key, and an RFC 3161 timestamp so that “when” is something other than our word.
Verification, and the floor that keeps it honest
Verification evaluates layers independently. A hidden signature, a visible manifest, a server record and its timestamp are each separately valid, invalid, missing, unavailable, unsupported or not applicable, and the result is a score in a band rather than a green tick, because a green tick compresses six different situations into one and the differences are the whole point.
The rule that keeps the score meaningful: if no cryptographic layer validates, the score is zero. A document that carries a beautifully formatted, internally consistent, entirely unsigned manifest earns nothing at all. Self-consistency is not evidence. It is the easiest thing in the world to forge, and any scoring system that awards partial credit for it is a system that rewards forgery over honesty.
Two more decisions worth naming. Duplicate content is allowed rather than blocked, but flagged before approval, because identical content legitimately gets attested by different people and pretending otherwise breaks real workflows. And disputes are append-only overlays that never rewrite the original record, because a provenance system that can quietly edit history is a provenance system with no provenance.
Close
So, back to the question at the top.
How do you deploy a container running someone’s agent onto any infrastructure, keep licensing and observability working, and never hold their keys? You stop trying to find the acceptable point on the line between access and privacy, and you go looking for the smallest thing you can receive that still answers your actual questions. Then you build so that thing is the only thing you can receive.
Four steps came out of that. Build it somewhere nothing leaves. Compile it to an artifact instead of a runtime, because a file can be hashed. Deploy it onto infrastructure that belongs to whoever owns the data. And keep two conversations open, plus a third only if you ask for a schedule, each of them narrow enough to describe in a sentence.
A Verifiable Intelligence Object is only verifiable if something actually checks it at every boundary it crosses. That is what the three planes are for, and it is why the digest came first.
Try the flow yourself: install the CLI, read the open .vio specification, or see how the trust model holds together end to end. Browse more reporting in Architecture Field Notes.

