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
- 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. - Pull — The follower verifies the PoS synchronously, returns
202 Accepted, then asynchronously fetches the Content Unit fromGET /ewp/contents/:contentHash. The follower recomputes the hash of the fetched bytes and stores the replica only if it matches.
Why Notify-Pull?
| Traditional Push | Notify-Pull |
|---|---|
| Publisher sends full content to all followers | Publisher sends only lightweight PoS |
| Bandwidth scales with follower count | Bandwidth constant |
| Followers must be online | Followers can pull when ready |
| Publisher bears distribution cost | Followers bear retrieval cost |
Publication Notification Endpoint
POST /ewp/publications
Auth: Active follow relationship + EIP-712 signature
Request Headers:
| Header | Required | Description |
|---|---|---|
X-Epress-Node-Updated | OPTIONAL | ISO 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:
- Signature is valid EIP-712 from
publisherAddress - An active follow relationship exists to the publisher
- 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:
- Compute SHA-256 of the fetched bytes
- Compare against
contentHashin the PoS - 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).