Skip to main content

Verification Rules

Normative rules for validating EWP operations. Implementations MUST perform all listed checks before mutating any state.

CreateConnection

Target: POST /ewp/connections

  1. typedData and signature MUST be present and structurally valid
  2. followerAddress, followeeAddress, followeeUrl, followerUrl, and timestamp MUST all be present
  3. followerUrl and followeeUrl MUST be valid https:// URLs
  4. Recover signer address from signature; MUST equal followerAddress
  5. timestamp MUST be within ±1 hour of the receiver's current time
  6. Fetch GET /ewp/profile from followeeUrl; returned address MUST equal followeeAddress
  7. Fetch GET /ewp/profile from followerUrl; returned address MUST equal followerAddress
  8. A connection record with this (followerAddress, followeeAddress) pair MUST NOT already exist
  9. Create connection record
Rationale for step 7

Without verifying followerUrl, an attacker could submit a valid follow request with an arbitrary target URL as followerUrl, causing the followee to direct all replication notifications at an unintended endpoint. This is both a misdirection attack and a DDoS amplification vector.

DestroyConnection

Target: DELETE /ewp/connections

  1. typedData and signature MUST be present
  2. followerAddress, followeeAddress, and timestamp MUST be present
  3. Recover signer address from signature
  4. Recovered signer MUST equal followerAddress OR followeeAddress; otherwise reject with INVALID_SIGNATURE
  5. timestamp MUST be within ±1 hour of the receiver's current time
  6. Locate the connection record matching (followerAddress, followeeAddress); if not found, return 404 CONNECTION_NOT_FOUND
  7. If the connection record's createdAt > message.timestamp, return 409 STALE_REQUEST and stop
  8. Delete the connection record

StatementOfSource (Inbound Publication)

Target: POST /ewp/publications

  1. typedData and signature MUST be present and structurally valid
  2. contentHash, publisherAddress, and timestamp MUST be present
  3. publisherAddress MUST correspond to a node in the receiver's follow list
  4. Recover signer; MUST equal publisherAddress
  5. If a publication with (contentHash, publisherAddress, timestamp) already exists locally, return 409 REPLICATION_ALREADY_EXISTS and stop
  6. Return 202 Accepted
  7. Asynchronously: if X-Epress-Node-Updated is present and newer than locally cached updatedAt, refresh the publisher's profile before fetching content
  8. Asynchronously: fetch GET /ewp/contents/<contentHash>?timestamp=<timestamp> from the publisher's cached URL; if the fetch fails and no header was present, re-fetch profile and retry once
  9. Asynchronously: compute SHA-256 of the fetched body; MUST equal contentHash; if mismatch, discard and log error
  10. Asynchronously: persist the Content Unit and publication record

NodeProfileUpdate

Target: PATCH /ewp/nodes/:address

  1. typedData and signature MUST be present
  2. Path :address MUST equal typedData.message.ownerAddress
  3. Recover signer; MUST equal ownerAddress
  4. If typedData.message.url differs from the locally cached URL:
    • Fetch GET /ewp/profile from the new URL
    • Returned address MUST equal ownerAddress
    • If this check fails, reject the url field; other fields MAY still be applied
  5. Apply the update only if timestamp > locally stored updatedAt

Connection Staleness Check

DestroyConnection requests are rejected if the targeted connection was established after the termination was signed.

Rule: If connection.createdAt > message.timestamp, return 409 STALE_REQUEST.

This prevents delayed or replayed termination from destroying a connection that was re-established in the interim.

Content Hash Verification

For content integrity:

  1. Compute SHA-256 of UTF-8 encoded content body (POST) or raw bytes (FILE)
  2. Compare against contentHash in the StatementOfSource
  3. Verify match before accepting publication