Skip to main content

Publications (Replication)

EWP uses a Notify-Pull model for content distribution, separating the lightweight provenance proof (Notify) from the potentially large content payload (Pull).

The Notify-Pull Model

Notify Phase Pull Phase (async)

Publisher Follower Publisher
│ │ │
│── POST /ewp/publications ────────►│
│ (PoS payload) │
│◄─ 202 Accepted ──────────────────│
│ │
│ (async) ─────┤
│ │── GET /ewp/contents/:contentHash
│ │◄─ Content Unit
│ │
│ │ verify hash,
│ │ store replica
  1. Notify — On signed publication, the publisher sends the PoS to each follower node via POST /ewp/publications. The PoS contains no content body; it is a cryptographic proof that content exists and is authentic.
  2. Pull — The follower verifies the PoS synchronously, returns 202 Accepted, then asynchronously fetches the Content Unit from GET /ewp/contents/:contentHash. The follower recomputes the hash of the fetched bytes and stores the replica only if it matches.

Why Notify-Pull?

Traditional PushNotify-Pull
Publisher sends full content to all followersPublisher sends only lightweight PoS
Bandwidth scales with follower countBandwidth constant
Followers must be onlineFollowers can pull when ready
Publisher bears distribution costFollowers bear retrieval cost

Publication Notification Endpoint

POST /ewp/publications

Auth: Active follow relationship + EIP-712 signature

Request Headers:

HeaderRequiredDescription
X-Epress-Node-UpdatedOPTIONALISO 8601 UTC timestamp of publisher's current updatedAt. If present and newer than the receiver's cached value, refresh the publisher's profile before fetching content.

Request Body:

{
"typedData": {
"domain": {
"name": "epress world",
"version": "1",
"chainId": 1
},
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" }
],
"StatementOfSource": [
{ "name": "contentHash", "type": "bytes32" },
{ "name": "publisherAddress", "type": "address" },
{ "name": "timestamp", "type": "uint64" }
]
},
"primaryType": "StatementOfSource",
"message": {
"contentHash": "0xabc123def456...",
"publisherAddress": "0xpublisher...",
"timestamp": 1705312800
}
},
"signature": "0x..."
}

Response: 202 Accepted

{ "status": "accepted" }

The 202 response indicates the PoS was validated. Content retrieval and storage proceed asynchronously after the response is sent.

Verification

The receiver must verify:

  1. Signature is valid EIP-712 from publisherAddress
  2. An active follow relationship exists to the publisher
  3. Deduplication: check if publication with (contentHash, publisherAddress, timestamp) already exists

Content Retrieval

After receiving a publication notification, the follower fetches content:

GET /ewp/contents/:contentHash

Returns the Content Unit. The follower MUST:

  1. Compute SHA-256 of the fetched bytes
  2. Compare against contentHash in the PoS
  3. Verify match before storing

If the Pull step fails and X-Epress-Node-Updated was absent, the follower SHOULD proactively re-fetch the publisher's profile and retry the content fetch. This handles URL changes that were not announced via the header.

Idempotency

Replication is at-least-once. Publishers MAY retransmit a notification for the same publication. Followers MUST handle duplicate notifications idempotently by deduplicating on the tuple (contentHash, publisherAddress, timestamp).