Run the task.
Control the access.
The agent uses tools. The runtime applies the access policy.
The grader checks the result.
Request
Control
Result
These tasks use example tools and results. They do not show live execution. The outer container or VM protects the host.
Check what the event proves.
An agent can report success and still produce an incorrect result.
Select a signal to see what it tells you.
Show example data
Can the runtime miss an action?
Yes. File and process checks use samples. A short action can occur between two samples.
No fileNo recorded changeNext sample
No file
The samples match. This does not prove that no file existed between them.
The loop waits one second between checks. Scans and scheduling add time. The interval is not exact.
Where do events go?
The harness sends reports through a write-only pipe. The daemon collects file, process, and resource samples.
The network supervisor collects firewall decisions. The daemon stores the events in bounded memory.
An authenticated observer receives permitted events through /observe. A separate service must store a permanent record.
Enable the required streams in the policy. A configured stream can collect events without a connected subscriber.
How does reset affect the record?
Reset clears the collected events. Sequence numbers start at zero again.
Export the record before reset. Add run and episode identifiers in the external record.
The native event fields are seq, ts, type, and data. They do not include an episode identifier.
Permission filters can cause gaps in the sequence received by an observer. A gap does not always indicate data loss.
What happens if collection fails?
The default collector limit is 100,000 events. Observation failures can stop the episode.
Detected network-log loss also stops the episode. This does not mean that the system can detect every possible loss.
File scans are not atomic. Files larger than 64 MiB cause a fingerprint error.
The health route reports basic worker status. It does not prove that the event record is complete.
Can a grader run submitted code safely?
The privileged oracle runs trusted operator code. It is not a sandbox for code from the agent.
A separate verifier needs a controlled handoff. Stop the writers. Preserve the selected files. Start a fresh verifier with its own policy.
This handoff is proposed work. A grader call alone does not stop the agent or change its network policy.
All values shown here are examples. The grader result comes from application logic, not the native observation stream.
Convert Echo.
Keep the Echo tools. Add a policy and change the launch command.
1. Prepare the runtime
The daemon needs Linux root, writable cgroup v2, network namespaces, and nftables. The provider must enable IPv4 forwarding.
Use a disposable container or VM with these permissions. The commands assume that the PR checkout is at /opt/OpenEnv.
# Inside a disposable, suitably isolated Linux runtime, as root. # This example assumes the PR checkout lives at /opt/OpenEnv. cd /opt/OpenEnv apt-get update apt-get install -y python3-venv iproute2 nftables conntrack \ libseccomp2 libnetfilter-log1 python3 -m venv /opt/openenvd-venv /opt/openenvd-venv/bin/pip install -e . # Reserve UID/GID 65536 exclusively for this workload. install -d -m 0755 -o root -g root /srv/echo-demo install -d -m 0700 -o 65536 -g 65536 /srv/echo-demo/workspace install -d -m 0700 -o root -g root /srv/echo-demo/assets printf '%s\n' 'Echo preserves the input message.' \ > /srv/echo-demo/assets/reference.txt chmod 0600 /srv/echo-demo/assets/reference.txt
Reserve UID 65536 for this worker. Keep the workspace parent root-owned. Keep asset files outside worker access.
2. Add the policy
Append this section to envs/echo_env/openenv.yaml. Keep the existing manifest fields.
openenvd:
enabled: true
surfaces:
agent:
tools: [echo_message, echo_with_length]
orchestrator:
allow_lifecycle: true
grader:
tools: [grader.read_file]
fs_read: ['/openenvd/assets/reference']
observer:
stream: [process, resource]
privileged_assets:
reference: reference.txtEcho needs no network access. The default policy blocks outbound traffic. The reference file demonstrates a protected grader read.
Echo already uses create_app. The daemon can find its environment factory without a class change.
3. Start the daemon
Replace the Echo Uvicorn command with this command. Run it in the same root shell.
# Same root shell; distinct secrets, never shared with the agent. export OPENENVD_ORCHESTRATOR_TOKEN="$(/opt/openenvd-venv/bin/python -c 'import secrets; print(secrets.token_urlsafe(32))')" export OPENENVD_GRADER_TOKEN="$(/opt/openenvd-venv/bin/python -c 'import secrets; print(secrets.token_urlsafe(32))')" export OPENENVD_OBSERVER_TOKEN="$(/opt/openenvd-venv/bin/python -c 'import secrets; print(secrets.token_urlsafe(32))')" /opt/openenvd-venv/bin/python -m openenv.core.openenvd \ --manifest /opt/OpenEnv/envs/echo_env/openenv.yaml \ --workspace /srv/echo-demo/workspace \ --asset-root /srv/echo-demo/assets \ --uid 65536 --gid 65536 \ --host 127.0.0.1 --port 8100 --timeout 300
The server listens on port 8100. Update the image health check if necessary.
Keep the tokens in trusted processes. A new shell does not automatically receive these token values.
The provider sets resource limits. The CLI timeout controls the episode timeout. The manifest validation limits do not configure the runtime.
4. Connect the callers
The agent uses /mcp. It does not receive a privileged token.
# Agent call: no lifecycle or grader credential.
curl --fail-with-body http://127.0.0.1:8100/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"echo_message","arguments":{"message":"Hello, isolation!"}}}'The controller uses its token to reset the episode.
# Trusted orchestration process, with the orchestrator token.
import asyncio
import os
from openenv.core.generic_client import GenericEnvClient
async def main():
async with GenericEnvClient(
base_url="http://127.0.0.1:8100",
headers={"Authorization":
f"Bearer {os.environ['OPENENVD_ORCHESTRATOR_TOKEN']}"},
) as env:
result = await env.reset(seed=0)
print(result)
asyncio.run(main())The grader uses a separate token to read the reference.
# Trusted grader process, with only the grader token.
import asyncio
import os
from openenv.core.openenvd import GraderClient
async def main():
async with GraderClient(
"http://127.0.0.1:8100/mcp/grader",
os.environ["OPENENVD_GRADER_TOKEN"],
) as grader:
print(await grader.call_tool(
"grader.read_file",
{"path": "/openenvd/assets/reference"},
))
asyncio.run(main())The observer uses its token to receive events.
# Trusted observer process, with only the observer token.
import asyncio
import os
from openenv.core.openenvd import observer_stream
async def main():
async for event in observer_stream(
"ws://127.0.0.1:8100/observe",
os.environ["OPENENVD_OBSERVER_TOKEN"],
):
print(event["type"], event["data"])
asyncio.run(main())5. Check the result
Confirm that echo_message returns the supplied message. Confirm that the agent cannot call grader.read_file.
Confirm that the grader can read the reference with its token. Confirm that unauthenticated privileged requests fail.
Confirm that the controller can reset. Confirm that the observer receives process and resource events.
The policy and factory were checked locally. The Echo tool returned the expected message. The complete Linux launch procedure remains untested.