# Oyster CVM + ERC-8004 Validation Registry Integration
> **TL;DR** — An Oyster CVM agent (running inside a TEE) publishes verifiable computation artifacts to an ERC-8004 validation registry so downstream agents can trust results without re-running the workload.
## Table of Contents
1. [Actors & Components](#1-actors--components)
2. [End-to-End Lifecycle](#2-end-to-end-lifecycle)
3. [Sequence Diagram](#3-sequence-diagram)
4. [Contract Interfaces](#4-contract-interfaces)
5. [Data Model](#5-data-model)
6. [Validation Decision Logic](#6-validation-decision-logic)
## 1. Actors & Components
**`Client`**
Sends compute requests and submits returned artifacts to the verifier.
**`oyster-validation-gateway`** *(Nitro enclave)*
Public ingress and signing/publishing layer. Handles request routing, auth/rate limits, IPFS upload, response signing, and `validationRequest` calls.
**`oyster-agent-service`** *(Nitro enclave)*
Runs the compute job and returns output to the gateway.
> [!IMPORTANT]
> `oyster-validation-gateway` and `oyster-agent-service` run in the **same Nitro enclave** (same attested boundary).
**`AttestationVerifier`** *(Marlin-deployed root of trust)*
Verifies the attestation verifier enclave on-chain once. All other enclaves are verified cheaply through its chain of trust. ([source](https://github.com/marlinprotocol/oyster-monorepo/blob/master/contracts/contracts-foundry/src/attestation/AttestationVerifier.sol))
**`AttestationAuther`** *(base contract — inherited by app contracts)*
Tracks verified enclave keys for a specific application. Your app contracts inherit this to gate access to verified enclaves only. ([source](https://github.com/marlinprotocol/oyster-monorepo/blob/master/contracts/contracts-foundry/src/attestation/AttestationAuther.sol))
**`Verifier Service`** *(TEE)*
Validates data integrity, signature, and enclave key binding. Calls `validationResponse` on the registry.
**`ValidationRegistry`** *(ERC-8004 contract)*
Stores canonical validation lifecycle state.
**Consumer Agents**
Query final validation state and retrieve request/verification artifacts from IPFS.
## 2. End-to-End Lifecycle
### 2.1 Setup *(one-time per environment)*
1. Deploy `AttestationAuther` contract on-chain
2. Verify the attestation verifier enclave's own attestation on-chain (expensive — done once via ZK proof or NitroProver)
3. Verifier enclave's key is now the root of trust — all other enclaves can be verified cheaply through it.
4. Ensure `ValidationRegistry` (ERC-8004) is deployed/available
5. Deploy verifier service in Oyster CVM and fund its EOA for gas.
6. Enclave boots → attestation server exposes Nitro attestation at `/attestation/raw`.
7. Attestation is sent to Marlin's verifier enclave (`POST /verify/raw`) → verifier checks cert chain, PCRs, freshness → returns a signed EIP-712 receipt.
8. Receipt submitted on-chain via `AttestationAuther.verifyEnclaveSignature(signature, attestation)` → enclave key registered.
### 2.2 Runtime Flow
```
Client ─► Gateway ─► Agent ─► IPFS ─► Registry ─► Verifier ─► Registry (final)
```
1. **Client** sends compute request to `oyster-validation-gateway`
2. **Gateway** forwards request to `oyster-agent-service` (intra-enclave call)
3. **Agent** executes request in TEE, returns output
4. **Gateway** writes `{input, output}` to IPFS → gets `requestURI`
5. **Gateway** computes `requestHash = keccak256(canonical_request_payload)`
6. **Gateway** signs response payload with KMS-derived key
7. **Gateway** submits `validationRequest(...)` to ValidationRegistry
8. **Gateway** returns `{response, signature, requestURI, requestHash}` to client
9. **Client** calls verifier `POST /verify` with returned artifacts
10. **Verifier** fetches `requestURI` content and re-computes hash
11. **Verifier** recovers signer address from response signature
12. **Verifier** calls `AttestationAuther._ensureKeyVerified(signerAddress)` → confirms key was registered via chain-of-trust
13. **Verifier** derives KMS public key for `(imageId, signingPath)` off-chain, compares to recovered key
14. **Verifier** writes proof object to IPFS → `responseURI`, computes `responseHash`
15. **Verifier** submits `validationResponse(...)` → **APPROVED** or **REJECTED**
16. **Consumer** queries registry and fetches evidence URIs
## 3. Sequence Diagram
```mermaid
sequenceDiagram
autonumber
participant C as Client
participant P as oyster-validation-gateway
participant A as oyster-agent-service
participant I as IPFS
participant VR as ValidationRegistry (ERC-8004)
participant V as Verifier (TEE)
participant AV as AttestationAuther
participant O as Consumer Agents
note right of C: Request & Compute Phase
C->>P: Compute request
P->>A: Forward request (intra-enclave)
A->>A: Execute workload in TEE
A-->>P: response payload
note right of P: Publish & Register Phase
P->>I: Put {input, output}
I-->>P: requestURI
P->>P: requestHash + response signature
P->>VR: validationRequest(validator, agentId, requestURI, requestHash)
VR-->>P: tx receipt (PENDING)
P-->>C: response, signature, requestURI, requestHash
note right of C: Verification Phase
C->>V: POST /verify(payload)
V->>I: GET requestURI
I-->>V: request payload
V->>V: Verify requestHash and recover signer address
V->>AV: _ensureKeyVerified(signerAddress)
AV-->>V: confirmed (reverts if not registered)
V->>V: kms-derive(imageId, signingPath) → compare keys
V->>I: Put verification proof
I-->>V: responseURI
V->>VR: validationResponse(requestHash, status, responseURI, responseHash, tag)
VR-->>V: tx receipt (final)
note right of O: Consumption Phase
O->>VR: Query by requestHash
VR-->>O: status + URIs
O->>I: Fetch requestURI/responseURI
I-->>O: evidence artifacts
```
## 4. Contract Interfaces
### 4.1 `validationRequest` *(ERC-8004)*
```solidity
// validatorAddress — address of the designated verifier
// agentId — numeric identifier of the registered agent
// requestURI — IPFS URI pointing to {input, output} payload
// requestHash — keccak256 of the canonical request payload
function validationRequest(
address validatorAddress,
uint256 agentId,
string requestURI,
bytes32 requestHash
) external;
```
### 4.2 `validationResponse` *(ERC-8004)*
```solidity
// requestHash — links this response to the original request
// response — validation outcome (1=APPROVED, 2=PENDING, 3=REJECTED)
// responseURI — IPFS URI pointing to verifier's proof object
// responseHash — hash of the response payload for integrity
// tag — application-specific metadata tag
function validationResponse(
bytes32 requestHash,
uint8 response,
string responseURI,
bytes32 responseHash,
bytes32 tag
) external;
```
## 5. Data Model
### 5.1 `requestURI` Payload *(produced by agent)*
```json
{
"input": { "...": "arbitrary request data" },
"output": { "...": "arbitrary response data" }
}
```
### 5.2 `responseURI` Payload *(produced by verifier)*
```json
{
"signature": "0x...",
"agentPublicKey": "0x...",
"imageId": "0x...",
"verifiedBy": "0xVerifier...",
"verifiedAt": 1704067300,
"checks": {
"hashMatch": true,
"signatureValid": true,
"enclaveKeyVerified": true,
"kmsKeyMatch": true
}
}
```
### 5.3 Enclave Boot Registration Flow
```
Enclave boots
└─► GET /attestation/raw (attestation server inside enclave)
└─► POST /verify/raw (Marlin's verifier enclave)
└─► returns { signature, imageId, timestampMs, publicKey, userData }
└─► AttestationAuther.verifyEnclaveSignature(signature, attestation)
└─► AttestationVerifier.verify() — confirms receipt is from trusted verifier
└─► _setKeyVerified(publicKey, imageId) — key is now registered on-chain
```
## 6. Validation Decision Logic
The verifier marks a request as **REJECTED** if any of the following checks fail:
1. **IPFS Fetch** - `requestURI` content cannot be retrieved
2. **Hash Validation** - re-computed `keccak256(fetched_payload)` ≠ submitted `requestHash`
3. **Signature Recovery** - ECDSA recover fails or returns an unexpected address
4. **Enclave Key Verified** - `AttestationAuther._ensureKeyVerified(signerAddr)` reverts — enclave never completed chain-of-trust registration
5. **KMS Key Binding** - KMS-derived public key for `(imageId, signingPath)` ≠ recovered signer key