# 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