InterPlanetary Name System (IPNS)
IPNS enables mutable references to IPFS content, solving the problem of sharing updated content without distributing new CIDs.
Understanding IPNS
The Challenge with IPFS Content
IPFS provides content-addressed storage where every file is referenced by its unique CID. When content changes, its CID changes too.
How IPNS Solves This
IPNS creates a persistent, updateable name that can point to different CIDs over time.
Key concepts
| Term | Description |
|---|---|
| IPNS key | Ed25519 keypair that signs records; created and held server-side |
| IPNS name | Public identifier derived from that key (k51q…) |
| IPNS record | Signed statement binding the name to a CID |
| Version | This platform's addition: every publish is retained in history |
1. Create a pointer
POST /api/ipns/create generates a key and publishes the first record.
curl -X POST https://api.ekayana.com/api/ipns/create \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "cid": "QmXyz…", "file_name": "protocol.pdf" }'The returned record carries the stable name plus the CID it currently points at:
{
"id": 1,
"ipns_name": "k51qzi5uqu5d…",
"ipns_key_name": "…",
"current_cid": "QmXyz…",
"original_cid": "QmXyz…",
"file_name": "protocol.pdf",
"owner_user_id": 42,
"version": 1
}Share ipns_name. It never changes again.
2. Publish an update
Upload the new revision to get a new CID, then repoint the name:
curl -X POST https://api.ekayana.com/api/ipns/update \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "ipns_name": "k51qzi5uqu5d…", "cid": "QmNewCid…" }'version increments and current_cid moves; original_cid still records where the pointer started.
Updating requires write access: either you own the record, or you supply a UCAN granting write - as the optional ucan_token field in the body, or as an X-UCAN-Token header. That is how a collaborator publishes a revision without owning the pointer.
3. Read
| Operation | Endpoint |
|---|---|
| Record by name | GET /api/ipns/{ipns_name} |
| Record by original CID | GET /api/ipns/by-cid/{cid} |
| Resolve to current CID | GET /api/ipns/{ipns_name}/resolve |
| Version history | GET /api/ipns/{ipns_name}/versions |
curl https://api.ekayana.com/api/ipns/k51qzi5uqu5d…/resolve \
-H "Authorization: Bearer $TOKEN"{ "ipns_name": "k51qzi5uqu5d…", "cid": "QmNewCid…" }History is the point for research data
GET /api/ipns/{ipns_name}/versions returns every CID the name has pointed to:
{ "ipns_name": "k51qzi5uqu5d…", "versions": [ … ], "total": 3 }Superseding a dataset does not erase it. A paper citing version 1 keeps resolving to the exact bytes that were reviewed, because that CID is still pinned, while new readers following the name get the current revision. Mutable reference, immutable history.
Practical notes
- Resolution is slower than a CID fetch. It costs a DHT lookup, so cache the result and refresh in the background rather than resolving per request.
- Cite CIDs, publish IPNS names. Use the IPNS name where readers should track the latest revision; use the CID where a reference must never move.
- Keys live server-side. There is no export endpoint - a pointer is bound to the account that created it.