Security Architecture
System Overview
Agent pays via x402, controller spawns an isolated Firecracker VM to execute and verify code, then records the result on-chain.
Controller Service
The Controller is a Node.js/TypeScript service running on Fly.io. It orchestrates the entire verification flow.
Key Responsibilities
- API Endpoints - Accept verification requests
- x402 Payment - Validate payments with 2 block confirmations
- VM Orchestration - Spawn/destroy VMs via Fly Machines API
- Log Streaming - Capture results via stdout (no callbacks)
- Blockchain Writes - Submit attestations to registry
- Refunds - Automatic refunds for infrastructure failures
Security Features
| Feature | Implementation |
|---|---|
| Rate Limiting | 100 req/min per IP |
| Circuit Breaker | Opens after 10 failures in 60s |
| Replay Protection | Tracks consumed tx hashes |
| Block Confirmations | Requires 2 confirmations |
| VM Reaper | Kills orphaned VMs >60s old |
Request Flow
- Schema Validation - Zod parsing enforces types and constraints
- Size Checks - Code must be <100KB, hash <128 chars
- Rate Limit Check - Progressive limits per IP
- Circuit Breaker - Reject if system under stress
- Payment Check - Verify X-402-Payment header if present
- VM Threshold - Check active VM count before spawning
- VM Spawn - Create ephemeral worker via Fly API
- Log Streaming - Stream stdout until NOTARY_RESULT found
- Result Processing - Compare hashes, determine VALID/INVALID
- Blockchain Write - Submit attestation to registry
- Cleanup - Destroy VM, update metrics
Input Validation
All requests are validated using Zod schemas before processing:
- taskType: Enum restricted to known values
- code: Optional string, max 100KB
- expectedHash: Optional string, max 128 chars
- timeoutSeconds: Number 5-60, defaults to 30
Validation failures return 400 with detailed error messages.
Graceful Shutdown
The controller handles SIGTERM and SIGINT for graceful shutdown:
- Stops accepting new requests
- Stops VM reaper
- Allows in-flight requests to complete (up to timeout)
- Exits cleanly
Worker VMs
Workers are ephemeral Firecracker micro-VMs that execute untrusted code.
VM Lifecycle
- Created - Controller calls Fly Machines API
- Boot - ~300ms cold start
- Execute - Run code in sandboxed environment
- Output - Write result to stdout with NOTARY_RESULT: prefix
- Destroy - VM auto-destroys on process exit
Sandbox Layers
| Layer | Technology | Purpose |
|---|---|---|
| Hypervisor | Firecracker | Hardware-level isolation |
| OS | Alpine Linux | Minimal attack surface |
| Process | Node.js vm module | JS sandbox |
| Context | Isolated globals | No system access |
Blocked Code Patterns
The sandbox validates code before execution and blocks:
- Import/require statements
- Network access (fetch, http, WebSocket)
- File system access (fs, path)
- Process manipulation (process, global)
- Dynamic execution (eval, Function)
- Timers (setTimeout, setInterval)
- Prototype pollution attempts
- Memory exhaustion attacks
Stdout-Only Architecture
Unlike traditional architectures that use HTTP callbacks, WorkProof uses stdout-only communication:
Why stdout?
- No network egress required (better security)
- No callback URLs to manage
- More reliable (no HTTP timeouts)
- Simpler failure modes
How it works:
- Worker writes
NOTARY_RESULT: {json}to stdout - Controller streams logs via Fly Logs API
- Parse result from log stream
Smart Contract
The Validation Registry is an ERC-8004 compatible contract on Base Sepolia.
Contract Address
0x3Faff789460Bf79db94b0034AF7dd779C81e6BA9
Data Structures
struct Attestation {
bytes32 taskHash; // Fingerprint of verified task
address verifier; // WorkProof service address
string verificationMethod; // "reexecution-v1-code-execution"
bool result; // true = VALID, false = INVALID
uint256 timestamp; // Block timestamp
bytes proofData; // Execution logs (encoded JSON)
}
struct VerifierStats {
uint256 totalAttestations;
uint256 successfulAttestations;
uint256 firstAttestationTime;
uint256 lastAttestationTime;
}
Key Functions
| Function | Purpose |
|---|---|
attestValidation() |
Submit new attestation (called by WorkProof) |
getAttestation() |
Query attestation by ID |
getAttestationsForTask() |
Get all attestations for a task hash |
getVerifierStats() |
Check verifier reputation |
Security Properties
- No Access Control - Anyone can submit attestations (open registry)
- Immutable - Attestations cannot be modified after creation
- Unique IDs - Nonce prevents overwriting old attestations
- Transparent - All verifier activity is public
Gas Costs
| Operation | Gas | Cost (1 gwei) |
|---|---|---|
| Deploy | ~1,200,000 | 0.0012 ETH |
| attestValidation | ~150,000 | 0.00015 ETH (~$0.30) |
| getAttestation | ~2,000 | 0.000002 ETH |
Why No Access Control?
The contract intentionally has no whitelist of approved verifiers. This is a feature, not a bug:
- Multiple Notary services can compete
- Market decides which verifiers to trust
- No central authority
- Agents check
verifieraddress off-chain