A walkthrough · @ruvector/rvforge

The sandbox is not the boundary.

A sandbox limits what a running process can touch. It does not govern the whole agent — who made it, what model it carries, what it remembers, what it is allowed to do, or what it has already done. This page explains the difference in plain language, then walks eight commands that put a boundary around all of it.

Plain-language intro Author to execution in eight commands Ends by breaking it on purpose

Start here

What a sandbox actually covers

Imagine hiring a contractor and locking every room in your house except the kitchen. That is a sandbox: it constrains where they can go while they are working.

It does not tell you who they are, whether their tools were swapped out on the way over, what they wrote down about your house, who else gave them instructions, or what they did on their last three visits.

An agent is not just a running process. It is a bundle: code, a model, memory, tools, a policy about what it may do, and a history. A sandbox covers exactly one piece of that bundle — the part executing right now.

The complete agent Codewhat it runs Modelhow it thinks Memorywhat it kept Toolswhat it can call Policywhat it may do History & provenancewho signed it, what it already did Persistent statewhat survives a restart Sandbox covers only this … and says nothing about any of the rest
The gap. Every box here can change how the agent behaves. Process isolation only ever wraps the leftmost one.

This barely mattered when agents were throwaway chat sessions. It matters a great deal once an agent is downloaded from somewhere, keeps state, and runs for days.

The useful question is not “what may this command touch?” It is: may this exact agent, carrying this exact state and history, do this exact thing — and can we prove afterwards what happened?

The shape of the answer

Three layers, one identity

Rather than one large security feature, the work splits into three jobs happening at three different times: packaging the agent, governing it before it runs, and governing it while it runs.

The thread running through all three is a single number — the SHA-256 digest of the packaged bytes. It is recomputed at every checkpoint, and it either matches or it does not.

Authorbuilds it Publishsigned & logged Installre-verified Rungated RVForge governs here RVM governs here .rvf the same bytes and the same digest 93efb9ae… at every checkpoint
One artifact, four checkpoints. Nothing is trusted because it was trusted a step earlier — each gate recomputes the digest from the bytes in front of it.

RVF

The package

A signed container. Code, model, memory, tools, policies, checkpoints and history travel together under one cryptographic identity, instead of as loose files that drift apart.

RVForge

Before it runs

Authors and validates the container without executing it, checks signatures, applies default-deny capability policy, builds signed installers, records provenance, and chains a receipt across every step.

RVM

While it runs

Verifies the artifact, creates an isolated partition, grants only explicit revocable capabilities, and makes every privileged action pass a capability, policy, proof and quota check.

The walkthrough

Eight commands, author to broken

Follow the panel on the left. It fills in as the artifact acquires its identity, tracks that identity through packaging and execution, and turns red in the final step when we destroy it deliberately.

STEP 01 — IDENTITY

Create a publisher identity

In plain terms: before you can vouch for anything, you need a way to sign it. This makes a signing key, and starts your permission list at zero.

Every release must be attributable to someone. This generates an Ed25519 signing key and a capability policy that is default-deny — nothing is permitted until you write it down explicitly.

$ npx @ruvector/rvforge init --keygen
Wrote forge.config.json Wrote rvforge.json Wrote rvforge-publisher.key (mode 600) key id ed25519:852d63dce7ff6b2d…dd6dc98d99f4 Capability policy is default-deny: add explicit grants before building. Next: rvforge create agent.rvf
15 capability classes — every one denied until declared 🔒 Filesystem 🔒 Network 🔒 Process 🔒 Model 🔒 MCP 🔒 Memory 🔒 GPU 🔒 Sensor 🔒 Display 🔒 Clipboard 🔒 Audio 🔒 Clock 🔒 Randomness 🔒 State 🔒 Agent msg
Absence is a denial, not a question. A capability that is simply not mentioned is refused — it is never left for the host to decide at runtime.

STEP 02 — AUTHORING

Package the agent into one artifact

In plain terms: take the loose pieces — the code, the model, the starting memory, the permission list — and seal them into a single signed file.

This is the step that turns a directory into an agent. Each piece becomes a segment with its own hash; the segment table becomes a root manifest; the manifest is signed with the key from step 01. From here on, the artifact has an identity that travels with it.

$ npx @ruvector/rvforge create agent.rvf \
--code ./dist --model ./model.gguf \
--grant fs:read:./data --grant net:https://api.example.com
Sealed 3 segments code 2.1 KB sha256 4f2a91c3… model 1.8 KB sha256 88b1d05e… policy 0.5 KB sha256 a03c4496… Signed root manifest with ed25519:852d63dc… capabilities 2 granted, 13 denied Wrote agent.rvf (4.4 KB) identity 93efb9ae948d8ed3…b6d2b67e59899837
Two grants, thirteen refusals. The policy is written into the artifact and covered by the signature, so the permissions a user sees at install time are the ones the publisher actually signed — not a claim the installer makes on its behalf.

STEP 03 — INSPECTION

Validate without executing

In plain terms: read the package the way you would read a sealed envelope's postmark — you learn a great deal, and you never open it.

This walks the container's segment table and recomputes digests, and at no point runs anything inside it. That property is what makes pointing this at a hostile artifact safe by construction rather than safe by luck.

$ npx @ruvector/rvforge validate agent.rvf --deep
agent.rvf: valid RVF (format v1, 4.4 KB) file id 0123456789abcdef0123456789abcdef segments 3, all hashes match signature verified (ed25519:852d63dc…) sha256 93efb9ae948d8ed3…b6d2b67e59899837 checked in 3 ms
Inside the container Root manifest segment table + CRC32C + signature codesha256 • ok modelsha256 • ok policysha256 • ok Whole-file identity 93efb9ae948d8ed3… recomputed, never stored as a label Root-manifest validity is not transitive trust — each segment is verified on its own before anything loads it.
Verified bottom-up. A valid outer manifest never vouches for the segments inside it; every segment carries and must match its own hash.
ProvesThe bytes are a well-formed, correctly signed RVF, and its identity is fixed.
Not yetHow the agent behaves — nothing has run.

STEP 04 — EVIDENCE

Test it in quarantine

In plain terms: ten questions. Five are answered by looking at the file. Five are answered by running it inside a disposable, monitored partition.

The second five are where behaviour becomes observable: install it cleanly, run the same input twice and compare, watch what it dials, starve it of resources, and confirm the receipts it emits match what it actually did.

$ npx @ruvector/rvforge test agent.rvf
10 passed 0 failed by inspection container well-formed signature verifies segment hashes match capabilities declared runtime profile supported in quarantine clean installation deterministic evaluations network monitoring resource exhaustion witness verification runtime rvm 1.0 • profile os-isolation+wasm • 812 ms
Answered by inspection ✓ container is well-formed ✓ signature verifies ✓ segment hashes match ✓ capabilities declared ✓ runtime profile supported never executed  |  executed, contained Answered in quarantine ✓ clean installation ✓ deterministic evaluations ✓ network monitoring ✓ resource exhaustion ✓ witness verification
Two different kinds of answer. The left column is arithmetic on bytes. The right column is observed behaviour inside a partition that is destroyed afterwards.

STEP 05 — PACKAGING

Build a signed installer per platform

In plain terms: produce the file a normal person double-clicks — one per operating system — and prove the copies are byte-identical rather than merely similar.

Embedded mode stages the same RVF bytes into every target and re-hashes each copy; if any diverged, the build fails rather than shipping. Thin mode ships a small signed pointer bound to the same canonical identity, so the download is small but the artifact is unchanged.

$ npx @ruvector/rvforge build agent.rvf --mode embedded
Built in ./forge-out rvf identity 93efb9ae948d8ed3…b6d2b67e59899837 manifest hash dbf5d61fd1ce7f5d…699644242cabf19 packaging embedded • re-hashed per target, all identical Agent-1.0.0-x86_64.deb signed Agent-1.0.0-universal.dmg signed + notarized Agent-1.0.0-x64.msi signed inventory software bill of materials, 8 files witness sha256:0b448323e465…290cbcddd48198
Embedded — identical bytes in each .deb93efb9ae .dmg93efb9ae .msi93efb9ae re-hashed after staging — any divergence fails the build Thin — a signed pointer in each .deb .dmg .msi one canonical .rvf93efb9ae… smaller download, same identity
Two shapes, one identity. The mode changes how the bytes are delivered; it never changes which artifact the user ends up running.

STEP 06 — CUSTODY

Verify a build you did not make

In plain terms: the step a reviewer runs on someone else's output. Re-check every file, and confirm the chain of receipts has no gaps.

Each authoring, build, verification and publication appends a receipt containing the hash of the one before it. Removing or editing any link breaks every link after it, so the chain either reconstructs cleanly or it visibly does not.

$ npx @ruvector/rvforge verify forge-out
Verified against ./forge-out/provenance.json rvf identity 93efb9ae948d8ed3…b6d2b67e59899837 manifest hash dbf5d61fd1ce7f5d…699644242cabf19 files checked 8, all matching installers 3, signatures valid witness chain 4 receipts, intact back to genesis publisher ed25519:852d63dc… • trust level 3 (registry-attested)
Each receipt carries the hash of the one before it createprev: — (genesis)self: 0b448323… buildprev: 0b448323…self: 1fda3bf5… publishprev: 1fda3bf5…self: 7c02ea41… Edit or delete any receipt and every downstream link stops reconstructing — gaps are detectable, not silent.
History you cannot quietly rewrite. The chain is what turns “we think this is the right build” into something a third party can check for themselves.

STEP 07 — EXECUTION

Run it under the capability gate

In plain terms: the agent finally runs — inside its own partition, allowed to do exactly the two things it declared, and writing a receipt for each attempt including the refused ones.

Every privileged action passes four checks in order: is the capability held, does policy allow it here, does the proof verify, is it within quota. A refusal is not a silent no-op — it is recorded, so a compromised agent's attempts are visible afterwards rather than inferred.

$ rvm run agent.rvf
Verified before load 93efb9ae…59899837 Partition p-4a19 created • profile os-isolation+wasm grant fs:read ./data witness a91c… grant net:https://api.example.com witness b03f… deny fs:write /etc/hosts witness c7d2… (not declared) deny process:spawn witness d41a… (not declared) Agent exited 0 • 4 receipts appended • partition destroyed
Every privileged action, every time agentasks capabilityis it held? policyallowed here? proofverifies? quotawithin budget? witness record written — pass or refuse, both are recorded A refusal is evidence. It is never a silent no-op, so a compromised agent's attempts remain visible after the fact.
Four gates, then the host. Nothing reaches the operating system without clearing all four — and the attempt is written down either way.

STEP 08 — THE POINT

Now break it on purpose

In plain terms: change one single byte in the middle of the file, then ask the validator again.

This is the whole idea compressed into one command. Identity is not a label attached to a file — it is a value recomputed from the bytes every single time. There is no check to bypass and no policy to argue with; the number simply no longer matches what was signed.

$ printf 'X' | dd of=agent.rvf bs=1 seek=2000 conv=notrunc
$ npx @ruvector/rvforge validate agent.rvf --deep
error: Root manifest CRC32C mismatch — the file is corrupt or has been modified. code: FORGE_E_INVALID_RVF
4.4 KB of bytes — one of them changed 4f 2a 91 c3 … 88 1d 05 e2 7b … a0 3c 44 96 … 12 ff 6e 58 ↑ byte 2000 Signed digest 93efb9ae948d8ed3 … b6d2b67e59899837 Recomputed now c71a04f8e2bb5d90 … 4e8a11d6033fc2be no relation to the signed value — refused before load
One byte in four thousand. A digest does not degrade gracefully — the smallest change produces a completely unrelated value, which is exactly the property that makes it useful.
That is the boundary doing its job. Not a heuristic, not a scan for known-bad patterns — an arithmetic fact about the bytes in front of you. The same check runs at publish, at install, and again before every load.

Reference

The commands, in order

Each one appends to the same witness chain, so the sequence itself is reconstructable from the artifact afterwards.

CommandWhat it doesExecutes the agent?
init --keygenPublisher identity and a default-deny policyno
createSeals code, model and policy into one signed artifactno
validate --deepStructure, segment hashes, signatureno
testFive checks by inspection, five in a disposable partitionin quarantine only
buildSigned installer per platform, inventory, provenanceno
verifyRe-checks a build and its receipt chain from the outsideno
submitPublishes to the registry and the transparency logno
rvm runVerifies, partitions, grants, gates and witnessesyes — gated

The model can remain probabilistic.
The authority boundary cannot.