diff --git a/docs/current/SPECIFICATION.md b/docs/current/SPECIFICATION.md index 9fdddfa..1d6c4f6 100644 --- a/docs/current/SPECIFICATION.md +++ b/docs/current/SPECIFICATION.md @@ -1080,7 +1080,7 @@ EEP nodes that advertise `pqc_ready: true` in their `/.well-known/eep.json` mani | FIPS 204 | ML-DSA-65 | Digital signatures | | FIPS 205 | SLH-DSA-128s | Hash-based signatures (stateless) | -Nodes SHOULD advertise supported algorithms in the `pqc_algorithms` array of the manifest. Nodes that are not yet PQC-ready MUST set `pqc_ready: false` (not omit the field). +Nodes SHOULD advertise supported algorithms in the `pqc_algorithms` array of the manifest. `pqc_ready` is OPTIONAL and defaults to `false`; a node that is not PQC-ready MAY either set it explicitly or omit it. Omission and `false` are equivalent, and neither affects conformance at any tier — PQC readiness is a capability a publisher advertises, not a bar it must clear. ### 11.7 Signing Algorithm Negotiation (Crypto-Agility) diff --git a/docs/standards/draft-eep-protocol-core-00.md b/docs/standards/draft-eep-protocol-core-00.md index 959114c..533ed35 100644 --- a/docs/standards/draft-eep-protocol-core-00.md +++ b/docs/standards/draft-eep-protocol-core-00.md @@ -143,8 +143,8 @@ The fields relevant to the Core tier are: | `eep_version` | string | yes | The EEP version supported, e.g. `"0.1"`. | | `layers` | object | yes | Endpoint URLs per layer; see below. | | `supported_content_types` | array | yes | Media types the Layer 1 entity endpoint can serve, e.g. `["application/json", "text/markdown"]`. | -| `pqc_ready` | boolean | yes | Whether the publisher can verify post-quantum signature algorithms. | -| `x402_enabled` | boolean | yes | Whether the publisher supports HTTP 402 payment gating. | +| `pqc_ready` | boolean | no | Whether the publisher can verify post-quantum signature algorithms. Absent means false. | +| `x402_enabled` | boolean | no | Whether the publisher supports HTTP 402 payment gating. Absent means false. | | `gates_url` | string (URI) | no | Absolute https URL of the gate configuration document. | | `services_url` | string (URI) | no | Absolute https URL of the service catalog. | | `updated_at` | string | no | RFC 3339 timestamp of the last manifest change. | diff --git a/packages/@eep-dev/compliance-cli/src/index.ts b/packages/@eep-dev/compliance-cli/src/index.ts index 280974b..b8615bf 100644 --- a/packages/@eep-dev/compliance-cli/src/index.ts +++ b/packages/@eep-dev/compliance-cli/src/index.ts @@ -155,9 +155,9 @@ const RECOMMENDATIONS: Record = { '/.well-known/eep.json manifest reachable': 'Serve eep manifest with stable URL and valid JSON contract.', 'manifest.did field present': 'Include did in manifest and keep it resolvable.', 'manifest.eep_version field present': 'Publish supported eep_version in manifest.', - 'manifest.reputation (ERC-8004) field present': 'Add reputation block with contract information when claiming full-tier readiness.', - 'manifest.pqc_ready flag present': 'Publish pqc_ready boolean in manifest.', - 'manifest.x402_enabled flag present': 'Publish x402_enabled boolean in manifest.', + 'manifest.reputation (ERC-8004) field present': 'Optional. Add a reputation block with contract information only if you use on-chain reputation; it is not required for any conformance tier.', + 'manifest.pqc_ready flag present': 'Optional. Publish pqc_ready only if you advertise post-quantum signature verification; absent means false.', + 'manifest.x402_enabled flag present': 'Optional. Publish x402_enabled only if you support HTTP 402 payment gating; absent means false.', 'HTTP 403 response for non-payment gate': 'Return RFC-consistent 403 response body for non-payment gate denials.', 'Federation registry economics metadata': 'Publish optional `economics` on `/.well-known/eep-registry.json` (registration fee, query quota, staking/challenge policy).', 'Cold-start trust status endpoint': 'Expose GET /eep/trust-status?agent_did=… returning trust_state cold_start or standard (reference stacks).', @@ -734,25 +734,37 @@ async function runTests() { if (json.eep_version) pass('manifest.eep_version field present', json.eep_version); else fail('manifest.eep_version field present', 'missing'); - // ERC-8004 reputation field (G3) + // ERC-8004 reputation field (G3). + // + // OPTIONAL in eep-manifest.json, so failing a publisher for + // its absence made the conformance bar stricter than the + // schema — and made an Ethereum reputation registry a + // prerequisite for Full conformance. A publisher with no + // blockchain involvement could not reach Full. Report it, + // don't fail on it. if (json.reputation && json.reputation.contract) { - pass('manifest.reputation (ERC-8004) field present', json.reputation.contract); + logPass('manifest.reputation (ERC-8004) field present', json.reputation.contract); } else { - fail('manifest.reputation (ERC-8004) field present', 'missing or no contract'); - } - - // PQC readiness flag (G8) - if (typeof json.pqc_ready === 'boolean') { - pass('manifest.pqc_ready flag present', String(json.pqc_ready)); - } else { - fail('manifest.pqc_ready flag present', 'missing or not a boolean'); + logSkip( + 'manifest.reputation (ERC-8004) field present', + 'optional in eep-manifest.json — only meaningful for publishers using on-chain reputation', + ); } - // x402 enabled flag (G2) - if (typeof json.x402_enabled === 'boolean') { - pass('manifest.x402_enabled flag present', String(json.x402_enabled)); - } else { - fail('manifest.x402_enabled flag present', 'missing or not a boolean'); + // PQC readiness and x402 flags. Both are OPTIONAL with a + // documented default of false — a publisher that supports + // neither post-quantum verification nor HTTP 402 payment + // gating is fully conformant and simply omits them. Report + // what is declared; do not require a declaration. + for (const flag of ['pqc_ready', 'x402_enabled'] as const) { + const value = json[flag]; + if (typeof value === 'boolean') { + logPass(`manifest.${flag} flag present`, String(value)); + } else if (value === undefined) { + logSkip(`manifest.${flag} flag present`, 'optional — absent means false'); + } else { + logFail(`manifest.${flag} flag present`, `present but not a boolean: ${typeof value}`); + } } // Dynamic capability discovery (G5) diff --git a/schemas/v0.1/eep-manifest.json b/schemas/v0.1/eep-manifest.json index 4441cbb..0530074 100644 --- a/schemas/v0.1/eep-manifest.json +++ b/schemas/v0.1/eep-manifest.json @@ -1,537 +1,537 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://eep.dev/schemas/v0.1/eep-manifest.json", - "title": "EEP Manifest", - "description": "The /.well-known/eep.json manifest declaring an entity's EEP capabilities", - "type": "object", - "required": [ - "did", - "eep_version", - "layers", - "supported_content_types", - "pqc_ready", - "x402_enabled" - ], - "additionalProperties": false, - "properties": { - "did": { - "type": "string", - "description": "W3C Decentralized Identifier of the entity", - "pattern": "^did:[a-z]+:.+" - }, - "eep_version": { - "type": "string", - "description": "EEP specification version this entity is currently operating at (e.g. '0.1').", - "pattern": "^\\d+\\.\\d+" - }, - "eep_versions": { - "type": "array", - "description": "List of EEP specification versions this entity supports. Agents send EEP-Version header; publisher confirms or returns HTTP 505. (§5.4, G21).", - "items": { - "type": "string", - "pattern": "^\\d+\\.\\d+" - }, - "minItems": 1, - "examples": [ - [ - "0.1", - "1.0" - ] - ] - }, - "preferred_version": { - "type": "string", - "description": "The EEP version this entity prefers when a client sends multiple accepted versions.", - "pattern": "^\\d+\\.\\d+" + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://eep.dev/schemas/v0.1/eep-manifest.json", + "title": "EEP Manifest", + "description": "The /.well-known/eep.json manifest declaring an entity's EEP capabilities", + "type": "object", + "required": [ + "did", + "eep_version", + "layers", + "supported_content_types" + ], + "additionalProperties": false, + "properties": { + "did": { + "type": "string", + "description": "W3C Decentralized Identifier of the entity", + "pattern": "^did:[a-z]+:.+" + }, + "eep_version": { + "type": "string", + "description": "EEP specification version this entity is currently operating at (e.g. '0.1').", + "pattern": "^\\d+\\.\\d+" + }, + "eep_versions": { + "type": "array", + "description": "List of EEP specification versions this entity supports. Agents send EEP-Version header; publisher confirms or returns HTTP 505. (\u00a75.4, G21).", + "items": { + "type": "string", + "pattern": "^\\d+\\.\\d+" + }, + "minItems": 1, + "examples": [ + [ + "0.1", + "1.0" + ] + ] + }, + "preferred_version": { + "type": "string", + "description": "The EEP version this entity prefers when a client sends multiple accepted versions.", + "pattern": "^\\d+\\.\\d+" + }, + "layers": { + "type": "object", + "required": [ + "layer1" + ], + "additionalProperties": false, + "properties": { + "layer1": { + "type": "string", + "format": "uri", + "description": "Layer 1 REST state endpoint URL" }, - "layers": { - "type": "object", - "required": [ - "layer1" - ], - "additionalProperties": false, - "properties": { - "layer1": { - "type": "string", - "format": "uri", - "description": "Layer 1 REST state endpoint URL" - }, - "layer2_sse": { - "type": "string", - "format": "uri", - "description": "Layer 2a SSE stream URL" - }, - "layer2_webhook": { - "type": "string", - "format": "uri", - "description": "Layer 2b webhook subscription endpoint URL" - }, - "layer3_ws": { - "type": "string", - "format": "uri", - "description": "Layer 3 WebSocket pulse URL" - } - } + "layer2_sse": { + "type": "string", + "format": "uri", + "description": "Layer 2a SSE stream URL" }, - "supported_content_types": { - "type": "array", - "description": "MIME types the entity can return via Content Negotiation", - "minItems": 1, - "items": { - "type": "string", - "examples": [ - "application/json", - "text/markdown", - "text/toon" - ] - } + "layer2_webhook": { + "type": "string", + "format": "uri", + "description": "Layer 2b webhook subscription endpoint URL" }, - "gates_url": { - "type": "string", - "format": "uri", - "description": "Gate configuration endpoint URL" + "layer3_ws": { + "type": "string", + "format": "uri", + "description": "Layer 3 WebSocket pulse URL" + } + } + }, + "supported_content_types": { + "type": "array", + "description": "MIME types the entity can return via Content Negotiation", + "minItems": 1, + "items": { + "type": "string", + "examples": [ + "application/json", + "text/markdown", + "text/toon" + ] + } + }, + "gates_url": { + "type": "string", + "format": "uri", + "description": "Gate configuration endpoint URL" + }, + "services_url": { + "type": "string", + "format": "uri", + "description": "Service catalog endpoint URL" + }, + "capabilities_query_url": { + "type": "string", + "format": "uri", + "description": "Dynamic Capability Discovery endpoint (G5)" + }, + "reputation": { + "type": "object", + "description": "ERC-8004 on-chain reputation binding (ref28)", + "required": [ + "contract", + "chain" + ], + "additionalProperties": false, + "properties": { + "contract": { + "type": "string", + "description": "ERC-721 contract address", + "pattern": "^0x[0-9a-fA-F]{40}$" }, - "services_url": { - "type": "string", - "format": "uri", - "description": "Service catalog endpoint URL" + "chain": { + "type": "string", + "description": "Blockchain network", + "examples": [ + "ethereum", + "base", + "polygon" + ] }, - "capabilities_query_url": { - "type": "string", - "format": "uri", - "description": "Dynamic Capability Discovery endpoint (G5)" + "scan_url": { + "type": "string", + "format": "uri", + "description": "8004Scan URL for this agent record" + } + } + }, + "pqc_ready": { + "type": "boolean", + "description": "Post-Quantum Cryptography readiness flag (NIST FIPS 203/204/205 \u2014 ref26) Absent means false: the publisher does not advertise post-quantum signature verification.", + "default": false + }, + "pqc_algorithms": { + "type": "array", + "description": "Supported PQC algorithms", + "items": { + "type": "string", + "examples": [ + "ML-KEM-768", + "ML-DSA-65", + "SLH-DSA-128s" + ] + } + }, + "signing_algorithms": { + "type": "array", + "description": "Ordered list of signing algorithm identifiers supported by this publisher for DID-based proof verification, from most preferred to least preferred. Agents MUST select the strongest mutually supported algorithm. When multiple algorithms are shared, the agent picks the publisher's highest-preference match. If no overlap exists, the agent MUST abort and return an error. Per Whitepaper \u00a710.9 (crypto-agility) and NIST FIPS 204/205. Classical algorithms (EdDSA, ES256K) remain valid; PQC algorithms supplement them via hybrid signatures. (A1).", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "EdDSA", + "ES256K", + "ES256", + "ML-DSA-65", + "ML-DSA-87", + "SLH-DSA-128s", + "hybrid-EdDSA-ML-DSA-65", + "hybrid-EdDSA-ML-DSA-87" + ] + }, + "examples": [ + [ + "EdDSA", + "ES256K" + ], + [ + "hybrid-EdDSA-ML-DSA-65", + "EdDSA" + ], + [ + "ML-DSA-65", + "EdDSA", + "ES256K" + ] + ] + }, + "x402_enabled": { + "type": "boolean", + "description": "Whether this entity accepts x402 protocol payments (ref27) Absent means false: the publisher does not advertise HTTP 402 payment gating.", + "default": false + }, + "x402": { + "type": "object", + "description": "x402 payment rail configuration", + "additionalProperties": false, + "properties": { + "facilitator_url": { + "type": "string", + "format": "uri" }, - "reputation": { - "type": "object", - "description": "ERC-8004 on-chain reputation binding (ref28)", - "required": [ - "contract", - "chain" - ], - "additionalProperties": false, - "properties": { - "contract": { - "type": "string", - "description": "ERC-721 contract address", - "pattern": "^0x[0-9a-fA-F]{40}$" - }, - "chain": { - "type": "string", - "description": "Blockchain network", - "examples": [ - "ethereum", - "base", - "polygon" - ] - }, - "scan_url": { - "type": "string", - "format": "uri", - "description": "8004Scan URL for this agent record" - } - } + "payment_rails": { + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + [ + "x402/usdc", + "x402/eth" + ] + ] }, - "pqc_ready": { - "type": "boolean", - "description": "Post-Quantum Cryptography readiness flag (NIST FIPS 203/204/205 — ref26)" + "network": { + "type": "string", + "examples": [ + "base", + "ethereum", + "polygon" + ] + } + } + }, + "compliance": { + "type": "object", + "description": "Regulatory compliance declarations", + "additionalProperties": false, + "properties": { + "eu_ai_act": { + "type": "boolean", + "description": "Entity complies with EU AI Act obligations (ref30)" }, - "pqc_algorithms": { - "type": "array", - "description": "Supported PQC algorithms", - "items": { - "type": "string", - "examples": [ - "ML-KEM-768", - "ML-DSA-65", - "SLH-DSA-128s" - ] - } + "gdpr": { + "type": "boolean", + "description": "Entity is GDPR-compliant" }, - "signing_algorithms": { - "type": "array", - "description": "Ordered list of signing algorithm identifiers supported by this publisher for DID-based proof verification, from most preferred to least preferred. Agents MUST select the strongest mutually supported algorithm. When multiple algorithms are shared, the agent picks the publisher's highest-preference match. If no overlap exists, the agent MUST abort and return an error. Per Whitepaper §10.9 (crypto-agility) and NIST FIPS 204/205. Classical algorithms (EdDSA, ES256K) remain valid; PQC algorithms supplement them via hybrid signatures. (A1).", - "minItems": 1, - "items": { - "type": "string", - "enum": [ - "EdDSA", - "ES256K", - "ES256", - "ML-DSA-65", - "ML-DSA-87", - "SLH-DSA-128s", - "hybrid-EdDSA-ML-DSA-65", - "hybrid-EdDSA-ML-DSA-87" - ] - }, - "examples": [ - [ - "EdDSA", - "ES256K" - ], - [ - "hybrid-EdDSA-ML-DSA-65", - "EdDSA" - ], - [ - "ML-DSA-65", - "EdDSA", - "ES256K" - ] - ] + "dora": { + "type": "boolean", + "description": "Entity is compliant with EU DORA (Digital Operational Resilience Act, EU 2022/2554)" }, - "x402_enabled": { - "type": "boolean", - "description": "Whether this entity accepts x402 protocol payments (ref27)" + "eidas2": { + "type": "boolean", + "description": "Entity's credential stack is architecturally aligned with eIDAS 2.0 (EU 2024/1183) W3C VC wallet requirements" }, - "x402": { - "type": "object", - "description": "x402 payment rail configuration", - "additionalProperties": false, - "properties": { - "facilitator_url": { - "type": "string", - "format": "uri" - }, - "payment_rails": { - "type": "array", - "items": { - "type": "string" - }, - "examples": [ - [ - "x402/usdc", - "x402/eth" - ] - ] - }, - "network": { - "type": "string", - "examples": [ - "base", - "ethereum", - "polygon" - ] - } - } + "anp_compatible": { + "type": "boolean", + "description": "Semantically compatible with W3C ANP metadata (ref29)" }, - "compliance": { - "type": "object", - "description": "Regulatory compliance declarations", - "additionalProperties": false, - "properties": { - "eu_ai_act": { - "type": "boolean", - "description": "Entity complies with EU AI Act obligations (ref30)" - }, - "gdpr": { - "type": "boolean", - "description": "Entity is GDPR-compliant" - }, - "dora": { - "type": "boolean", - "description": "Entity is compliant with EU DORA (Digital Operational Resilience Act, EU 2022/2554)" - }, - "eidas2": { - "type": "boolean", - "description": "Entity's credential stack is architecturally aligned with eIDAS 2.0 (EU 2024/1183) W3C VC wallet requirements" - }, - "anp_compatible": { - "type": "boolean", - "description": "Semantically compatible with W3C ANP metadata (ref29)" - }, - "dpv_purpose": { - "type": "string", - "description": "W3C DPV purpose URI", - "examples": [ - "https://w3id.org/dpv#ServiceProvision" - ] - }, - "dpv_retention": { - "type": "string", - "description": "W3C DPV retention policy URI" - } - } + "dpv_purpose": { + "type": "string", + "description": "W3C DPV purpose URI", + "examples": [ + "https://w3id.org/dpv#ServiceProvision" + ] }, - "data_residency": { + "dpv_retention": { + "type": "string", + "description": "W3C DPV retention policy URI" + } + } + }, + "data_residency": { + "type": "string", + "description": "Data residency constraint. Declares where publisher stores and processes data received from agent interactions. ISO 3166-1 region or country code \u2014 see also GDPR/DORA requirements. (\u00a714.2, G22).", + "examples": [ + "EU-only", + "DE", + "US", + "US,CA", + "Worldwide" + ] + }, + "payment_networks": { + "type": "array", + "description": "Multi-chain payment configuration. Each entry declares a payment address on one blockchain network. Agents with multi-chain wallets may select the cheapest/fastest. (\u00a78.3, G22).", + "items": { + "type": "object", + "required": [ + "chain", + "address" + ], + "additionalProperties": false, + "properties": { + "chain": { "type": "string", - "description": "Data residency constraint. Declares where publisher stores and processes data received from agent interactions. ISO 3166-1 region or country code — see also GDPR/DORA requirements. (§14.2, G22).", + "description": "Blockchain network identifier.", "examples": [ - "EU-only", - "DE", - "US", - "US,CA", - "Worldwide" + "base", + "ethereum", + "solana", + "polygon" ] - }, - "payment_networks": { - "type": "array", - "description": "Multi-chain payment configuration. Each entry declares a payment address on one blockchain network. Agents with multi-chain wallets may select the cheapest/fastest. (§8.3, G22).", - "items": { - "type": "object", - "required": [ - "chain", - "address" - ], - "additionalProperties": false, - "properties": { - "chain": { - "type": "string", - "description": "Blockchain network identifier.", - "examples": [ - "base", - "ethereum", - "solana", - "polygon" - ] - }, - "address": { - "type": "string", - "description": "Payment address or smart contract on this chain." - }, - "min_confirmations": { - "type": "integer", - "description": "Minimum on-chain confirmation blocks before payment is considered final.", - "minimum": 0, - "examples": [ - 1, - 6, - 12 - ] - } - } - } - }, - "pricing_mode": { - "type": "string", - "description": "Pricing discovery mode for this entity's services. fixed: static price list; negotiable: bilateral counter-offer; auction: open auction (RFP). (§7.3, G19/G22).", - "enum": [ - "fixed", - "negotiable", - "auction" - ], - "default": "fixed" - }, - "updated_at": { + }, + "address": { "type": "string", - "format": "date-time", - "description": "ISO8601 timestamp of last manifest update" - }, - "tls_mode": { - "type": "string", - "description": "Transport security mode for this endpoint per Whitepaper §9.1. Declares whether this publisher requires standard TLS or mutual TLS (mTLS). Agents MUST check this field before connecting and present DID-backed client certificates when mTLS or mTLS-required is declared. (§9.1, G34).", - "enum": [ - "standard", - "mTLS", - "mTLS-required" - ], - "default": "standard", + "description": "Payment address or smart contract on this chain." + }, + "min_confirmations": { + "type": "integer", + "description": "Minimum on-chain confirmation blocks before payment is considered final.", + "minimum": 0, "examples": [ - "standard", - "mTLS", - "mTLS-required" + 1, + 6, + 12 ] + } + } + } + }, + "pricing_mode": { + "type": "string", + "description": "Pricing discovery mode for this entity's services. fixed: static price list; negotiable: bilateral counter-offer; auction: open auction (RFP). (\u00a77.3, G19/G22).", + "enum": [ + "fixed", + "negotiable", + "auction" + ], + "default": "fixed" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "ISO8601 timestamp of last manifest update" + }, + "tls_mode": { + "type": "string", + "description": "Transport security mode for this endpoint per Whitepaper \u00a79.1. Declares whether this publisher requires standard TLS or mutual TLS (mTLS). Agents MUST check this field before connecting and present DID-backed client certificates when mTLS or mTLS-required is declared. (\u00a79.1, G34).", + "enum": [ + "standard", + "mTLS", + "mTLS-required" + ], + "default": "standard", + "examples": [ + "standard", + "mTLS", + "mTLS-required" + ] + }, + "forward_secrecy_enforced": { + "type": "boolean", + "description": "Whether this publisher enforces Forward Secrecy (ECDHE/DHE key exchange) for all long-lived connections (SSE, WebSocket). Per EEP Whitepaper \u00a710.1 and security.md \u00a711, forward secrecy is mandatory for WS/SSE endpoints. Publishers SHOULD set this to true once they have verified their TLS configuration. Agents MAY refuse to connect to WS/SSE endpoints where this is false or absent. (G38).", + "default": true, + "examples": [ + true, + false + ] + }, + "discovery_hints": { + "type": "object", + "description": "Alternative discovery mechanism hints per Whitepaper \u00a74.4 (DNS and Link Header Discovery). For IoT devices, constrained environments, or deployments where /.well-known/ endpoints are not accessible, publishers can declare alternative discovery paths. Agents that cannot reach /.well-known/eep.json SHOULD check DNS TXT records and Link headers before failing. See docs/guides/iot-discovery.md for implementation details.", + "additionalProperties": false, + "properties": { + "link_header_supported": { + "type": "boolean", + "description": "Whether this entity publishes a 'Link: ; rel=\"eep\"' header on its root HTTP response. Agents can discover EEP capability by inspecting any HTTP response from this entity. Per Whitepaper \u00a74.4.", + "default": false, + "examples": [ + true + ] }, - "forward_secrecy_enforced": { - "type": "boolean", - "description": "Whether this publisher enforces Forward Secrecy (ECDHE/DHE key exchange) for all long-lived connections (SSE, WebSocket). Per EEP Whitepaper §10.1 and security.md §11, forward secrecy is mandatory for WS/SSE endpoints. Publishers SHOULD set this to true once they have verified their TLS configuration. Agents MAY refuse to connect to WS/SSE endpoints where this is false or absent. (G38).", - "default": true, - "examples": [ - true, - false + "dns_txt_record": { + "type": "string", + "description": "The DNS TXT record this entity publishes at _eep.{domain} for alternative discovery. Format: 'v=eep1; manifest={url}'. Agents MUST validate the manifest URL is HTTPS. Per Whitepaper \u00a74.4 and SPECIFICATION.md \u00a74.4.", + "pattern": "^v=eep1;\\s*manifest=https://", + "examples": [ + "v=eep1; manifest=https://api.example.com/.well-known/eep.json" + ] + }, + "beacon_interval_seconds": { + "type": "integer", + "description": "For IoT publishers that periodically broadcast their manifest URL via mDNS/DNS-SD or BLE Advertisement. Interval in seconds between broadcasts. Agents in constrained networks can listen for beacons instead of polling.", + "minimum": 30, + "maximum": 86400, + "examples": [ + 300, + 3600 + ] + } + } + }, + "conformance_credential": { + "type": "object", + "description": "The EEP Conformance Credential issued by eep.dev's DID for this publisher. Allows agents to verify conformance tier on first contact without querying any registry. See schemas/v0.1/conformance.credential.json for the full schema and Whitepaper \u00a710.2. The credential expires annually and must be renewed. Agents MUST verify the proof and validUntil before trusting the credential. (G39).", + "required": [ + "type", + "issuer", + "validFrom", + "validUntil", + "credentialSubject", + "proof" + ], + "additionalProperties": true, + "properties": { + "type": { + "type": "array", + "description": "Must include 'VerifiableCredential' and a specific EEP conformance tier type.", + "items": { + "type": "string" + }, + "examples": [ + [ + "VerifiableCredential", + "EEPConformanceCredential_Full" ] + ] }, - "discovery_hints": { - "type": "object", - "description": "Alternative discovery mechanism hints per Whitepaper §4.4 (DNS and Link Header Discovery). For IoT devices, constrained environments, or deployments where /.well-known/ endpoints are not accessible, publishers can declare alternative discovery paths. Agents that cannot reach /.well-known/eep.json SHOULD check DNS TXT records and Link headers before failing. See docs/guides/iot-discovery.md for implementation details.", - "additionalProperties": false, - "properties": { - "link_header_supported": { - "type": "boolean", - "description": "Whether this entity publishes a 'Link: ; rel=\"eep\"' header on its root HTTP response. Agents can discover EEP capability by inspecting any HTTP response from this entity. Per Whitepaper §4.4.", - "default": false, - "examples": [ - true - ] - }, - "dns_txt_record": { - "type": "string", - "description": "The DNS TXT record this entity publishes at _eep.{domain} for alternative discovery. Format: 'v=eep1; manifest={url}'. Agents MUST validate the manifest URL is HTTPS. Per Whitepaper §4.4 and SPECIFICATION.md §4.4.", - "pattern": "^v=eep1;\\s*manifest=https://", - "examples": [ - "v=eep1; manifest=https://api.example.com/.well-known/eep.json" - ] - }, - "beacon_interval_seconds": { - "type": "integer", - "description": "For IoT publishers that periodically broadcast their manifest URL via mDNS/DNS-SD or BLE Advertisement. Interval in seconds between broadcasts. Agents in constrained networks can listen for beacons instead of polling.", - "minimum": 30, - "maximum": 86400, - "examples": [ - 300, - 3600 - ] + "issuer": { + "description": "DID of the issuer. Must resolve to did:web:eep.dev for the credential to be trusted.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" } + } } + ], + "examples": [ + "did:web:eep.dev" + ] }, - "conformance_credential": { - "type": "object", - "description": "The EEP Conformance Credential issued by eep.dev's DID for this publisher. Allows agents to verify conformance tier on first contact without querying any registry. See schemas/v0.1/conformance.credential.json for the full schema and Whitepaper §10.2. The credential expires annually and must be renewed. Agents MUST verify the proof and validUntil before trusting the credential. (G39).", - "required": [ - "type", - "issuer", - "validFrom", - "validUntil", - "credentialSubject", - "proof" - ], - "additionalProperties": true, - "properties": { - "type": { - "type": "array", - "description": "Must include 'VerifiableCredential' and a specific EEP conformance tier type.", - "items": { - "type": "string" - }, - "examples": [ - [ - "VerifiableCredential", - "EEPConformanceCredential_Full" - ] - ] - }, - "issuer": { - "description": "DID of the issuer. Must resolve to did:web:eep.dev for the credential to be trusted.", - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "type": "string" - } - } - } - ], - "examples": [ - "did:web:eep.dev" - ] - }, - "validFrom": { - "type": "string", - "format": "date-time" - }, - "validUntil": { - "type": "string", - "format": "date-time" - }, - "credentialSubject": { - "type": "object", - "required": [ - "id", - "conformanceTier", - "testedAt" - ], - "properties": { - "id": { - "type": "string", - "description": "DID of the publisher (must match this manifest's did)." - }, - "conformanceTier": { - "type": "string", - "enum": [ - "Core", - "Standard", - "Full" - ] - }, - "eepVersion": { - "type": "string" - }, - "testedAt": { - "type": "string", - "format": "date-time" - }, - "passedChecks": { - "type": "integer" - }, - "totalChecks": { - "type": "integer" - }, - "manifestUrl": { - "type": "string", - "format": "uri" - }, - "sectorExtensions": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "proof": { - "type": "object", - "required": [ - "type", - "created", - "verificationMethod", - "proofPurpose", - "proofValue" - ], - "properties": { - "type": { - "type": "string" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "verificationMethod": { - "type": "string" - }, - "proofPurpose": { - "type": "string", - "const": "assertionMethod" - }, - "proofValue": { - "type": "string" - } - } - } + "validFrom": { + "type": "string", + "format": "date-time" + }, + "validUntil": { + "type": "string", + "format": "date-time" + }, + "credentialSubject": { + "type": "object", + "required": [ + "id", + "conformanceTier", + "testedAt" + ], + "properties": { + "id": { + "type": "string", + "description": "DID of the publisher (must match this manifest's did)." }, - "examples": [ - { - "type": [ - "VerifiableCredential", - "EEPConformanceCredential_Full" - ], - "issuer": "did:web:eep.dev", - "validFrom": "2026-03-05T12:00:00Z", - "validUntil": "2027-03-05T12:00:00Z", - "credentialSubject": { - "id": "did:web:api.publisher.example", - "conformanceTier": "Full", - "eepVersion": "0.1", - "testedAt": "2026-03-05T10:30:00Z", - "passedChecks": 47, - "totalChecks": 47, - "manifestUrl": "https://api.publisher.example/.well-known/eep.json" - }, - "proof": { - "type": "Ed25519Signature2020", - "created": "2026-03-05T12:00:00Z", - "verificationMethod": "did:web:eep.dev#key-1", - "proofPurpose": "assertionMethod", - "proofValue": "z58DAdFfa9SkqZMVPxAQpic7ndSayn1PzZs6ZjWp1CktyGesjuTSwRdoWhAfGFCF5bppETSTojQCrfFPP2oumHKtz" - } - } - ] + "conformanceTier": { + "type": "string", + "enum": [ + "Core", + "Standard", + "Full" + ] + }, + "eepVersion": { + "type": "string" + }, + "testedAt": { + "type": "string", + "format": "date-time" + }, + "passedChecks": { + "type": "integer" + }, + "totalChecks": { + "type": "integer" + }, + "manifestUrl": { + "type": "string", + "format": "uri" + }, + "sectorExtensions": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "proof": { + "type": "object", + "required": [ + "type", + "created", + "verificationMethod", + "proofPurpose", + "proofValue" + ], + "properties": { + "type": { + "type": "string" + }, + "created": { + "type": "string", + "format": "date-time" + }, + "verificationMethod": { + "type": "string" + }, + "proofPurpose": { + "type": "string", + "const": "assertionMethod" + }, + "proofValue": { + "type": "string" + } + } + } + }, + "examples": [ + { + "type": [ + "VerifiableCredential", + "EEPConformanceCredential_Full" + ], + "issuer": "did:web:eep.dev", + "validFrom": "2026-03-05T12:00:00Z", + "validUntil": "2027-03-05T12:00:00Z", + "credentialSubject": { + "id": "did:web:api.publisher.example", + "conformanceTier": "Full", + "eepVersion": "0.1", + "testedAt": "2026-03-05T10:30:00Z", + "passedChecks": 47, + "totalChecks": 47, + "manifestUrl": "https://api.publisher.example/.well-known/eep.json" + }, + "proof": { + "type": "Ed25519Signature2020", + "created": "2026-03-05T12:00:00Z", + "verificationMethod": "did:web:eep.dev#key-1", + "proofPurpose": "assertionMethod", + "proofValue": "z58DAdFfa9SkqZMVPxAQpic7ndSayn1PzZs6ZjWp1CktyGesjuTSwRdoWhAfGFCF5bppETSTojQCrfFPP2oumHKtz" + } } + ] } -} \ No newline at end of file + } +} diff --git a/tests/cross-impl/test_discovery_manifest.py b/tests/cross-impl/test_discovery_manifest.py index 63c02ba..463e3ad 100644 --- a/tests/cross-impl/test_discovery_manifest.py +++ b/tests/cross-impl/test_discovery_manifest.py @@ -30,12 +30,24 @@ def test_schema_is_valid_json(self): assert "did" in schema.get("required", []) def test_schema_required_fields(self): - """Schema must require the 6 mandatory fields per §4.1.""" + """Schema must require the 4 mandatory fields per §4.1. + + ``pqc_ready`` and ``x402_enabled`` were required until the manifest was + de-vendored: they are capabilities a publisher *advertises*, not bars it + must clear, so requiring a payment-rail flag in every manifest tied + conformance to one ecosystem. They are now optional with a documented + default of ``false``. + """ with open(SCHEMA_PATH) as f: schema = json.load(f) required = set(schema["required"]) - expected = {"did", "eep_version", "layers", "supported_content_types", "pqc_ready", "x402_enabled"} + expected = {"did", "eep_version", "layers", "supported_content_types"} assert expected.issubset(required), f"Missing required: {expected - required}" + # Still defined, still constrained — just no longer mandatory. + for optional in ("pqc_ready", "x402_enabled"): + assert optional not in required, f"{optional} should be optional" + assert optional in schema["properties"], f"{optional} should still be defined" + assert schema["properties"][optional].get("default") is False def test_schema_layers_requires_layer1(self): """layers object must require layer1.""" diff --git a/tests/types/eep-schemas.d.ts b/tests/types/eep-schemas.d.ts index 176fe1e..c9bc4d0 100644 --- a/tests/types/eep-schemas.d.ts +++ b/tests/types/eep-schemas.d.ts @@ -928,9 +928,9 @@ export interface EEPManifest { scan_url?: string; }; /** - * Post-Quantum Cryptography readiness flag (NIST FIPS 203/204/205 — ref26) + * Post-Quantum Cryptography readiness flag (NIST FIPS 203/204/205 — ref26) Absent means false: the publisher does not advertise post-quantum signature verification. */ - pqc_ready: boolean; + pqc_ready?: boolean; /** * Supported PQC algorithms */ @@ -963,9 +963,9 @@ export interface EEPManifest { )[] ]; /** - * Whether this entity accepts x402 protocol payments (ref27) + * Whether this entity accepts x402 protocol payments (ref27) Absent means false: the publisher does not advertise HTTP 402 payment gating. */ - x402_enabled: boolean; + x402_enabled?: boolean; /** * x402 payment rail configuration */