Documentation

Privacy Controls

IPFS was designed for open sharing. A CID is derived from the content, so on a public network anyone who learns the CID can ask for the bytes. For a public dataset that is the appeal. For a prepublication genome it is a problem. The platform's cluster is private, and even so, keeping a CID secret is not the privacy model.

The platform applies three controls, in increasing order of strength.

  • Access control at the API. A file is read through GET /api/download/{cid}, which serves it to its owner or to the holder of a valid pass and to nobody else. There is no gateway, allowlist or other side door in that decision.
  • Capabilities instead of permissions. Every grant is a UCAN pass the owner's wallet signs, naming one CID and the allowed actions among read, write and delete, with a signed expiry, revocable at any time.
  • Encryption before storage. Every upload is sealed before it reaches IPFS under a fresh AES-256-GCM content key, itself wrapped to the service's ML-KEM-1024 key. The CID addresses ciphertext.

The first two depend on the API enforcing rules. The third does not depend on anyone outside the service. The rest of this page follows that distinction.

Encrypt, then store

The strongest privacy control is the one that does not depend on an access control list being enforced correctly. Every payload is encrypted before it is added to IPFS, so the CID addresses ciphertext.

Fresh 32 byte content keyrandom, one per payloadSEALS THE DATASEALS THE KEYAES-256-GCMpayload + associated dataSealed payloadciphertextIPFSthis is what the CID addressesML-KEM-1024encapsulation -> shared secretHKDF-SHA256 -> AES-256-GCMderives a wrapping key, sealsSealed content keyopens only with the decap keyA leaked CID hands over the left column only - bytes with no key anywhere near them.
Envelope encryption. A fresh 32 byte content key per payload seals the payload with AES-256-GCM before it goes to IPFS, and is itself sealed by ML-KEM-1024 encapsulation through HKDF-SHA256 into AES-256-GCM.

What is stored is a DAG-CBOR envelope with four fields. v is the version, currently 1. iv is the AES-GCM nonce. ct is the ciphertext. key is the wrapped content key. The wrap is ML-KEM-1024 + HKDF-SHA256 + AES-256-GCM, implemented in pq-space. Five consequences follow.

  • A leaked CID is not a leaked dataset. The peers holding the block never join the public network, and anyone who obtains the envelope holds bytes that are useless without the ML-KEM decapsulation key, which never leaves the service.
  • The envelope is bound to its owner. The account id is the associated data of both the payload seal and the key wrap, so an envelope lifted into another account's context fails to open.
  • Tampering is detected. AES-256-GCM is authenticated, so a modified envelope fails to decrypt rather than returning corrupted data.
  • Every upload gets its own key. Two uploads of the same file produce two different envelopes and two different CIDs. Nothing links them.
  • There is no unencrypted path. Every file written through the API is sealed. An object that does not decode as a version 1 envelope is refused rather than passed through.

The service itself can read every payload, because it holds the decapsulation key. The encryption protects your data against the network, against replicas and against anyone who obtains the stored bytes. It does not make the platform blind to them. Metadata documents for DIDs are public by design. They carry the title, keywords, license and the researcher names and ORCIDs the owner chose to publish, and never an email address.

IPFS content is immutable, and a reader may have kept a copy, so deletion cannot be guaranteed by unpinning alone. Sealing makes erasure effective rather than best effort. A replica a third party fetched is an envelope nobody outside the service can open, and the service unpins its own copy when you delete. For the same reason nothing personal is ever anchored on-chain, only public keys, opaque fragments and URIs.

See Post Quantum Security for the algorithms and key management, and UCAN Authorization for how the same wrap can carry a content key inside a delegation.

Access control is capability based

Access is granted by minting a scoped, revocable pass rather than by editing an access control list. The API prepares the exact bytes, your wallet signs them, and the API records the result.

bash
curl -X POST https://api.ekayana.io/api/share/file/prepare \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "cid": "bafybeig...",
    "recipient_did": "did:bio:devnet:4Nq8...",
    "capabilities": ["read"],
    "expiration_days": 7
  }'
# -> { "signing_bytes": "<base64url>", "expires_at": 1753833600 }
# The wallet signs. Then post the same body plus signing_bytes and signature.
curl -X POST https://api.ekayana.io/api/share/file \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "cid": "bafybeig...", "recipient_did": "did:bio:devnet:4Nq8...",
        "capabilities": ["read"], "expiration_days": 7,
        "signing_bytes": "<base64url>", "signature": "<base64url>" }'

Revoke with DELETE /api/share/revoke/{token_hash}, where token_hash is the CID of the pass. Revocation is checked on every presentation before the chain is validated, and anything delegated beneath a revoked pass fails with it, because its proof no longer resolves.

What this gives you under GDPR

RequirementMechanism
Data minimisationOnly public keys, fragments and URIs are anchored on-chain, and metadata documents never carry contact details
Purpose limitationA pass names one CID and the actions allowed, nothing wider
Storage limitationEvery pass carries a signed expiry, and sessions expire
Right to erasureDelete unpins the platform's copy, and any replica is an envelope only the service could open
Integrity and confidentialityAES-256-GCM at rest, content keys wrapped with ML-KEM-1024, ownership bound into the envelope
Access on your termsEvery share is wallet signed, and the service can sign none on your behalf

The cluster is private, and the platform operator can read what it stores. Encryption at rest removes the peers, any gateway and any copy of the bytes from your threat model. It does not remove the operator. Keep that in mind when deciding what to upload, and read BioAgents Architecture before registering a paper, because a registered paper's text is indexed for every user of the agent.