Binary Storage

Oak consensus stores structured content and binary references. The binary bytes live in one of two places: storage the author runs (author-hosted) or the validator's configured BlobStore (validator-hosted). Either way, the bytes are not replicated as Oak segment content. A CID proves content identity, not continued availability, and CID encodings are variable length.

The truth-provenance-edge separation also means binary integrity stays verifiable end-to-end without relying on platform trust: any consumer can hash the bytes they fetched and compare against the CID stored in Oak. Follow the upload and retrieval flow below to publish one binary and verify its CID-backed integrity.

Two Hosting Modes

Data Table
Mode
Who stores the bytes
How you write
Author-hosted
You, in IPFS or another store you pin and pay for
Upload the file yourself, then put its CID in the message of a normal write. The validator stores the CID as data and never fetches the bytes.
Validator-hosted
The validator, in its BlobStore (IPFS by default)
Send the file with the write (a multipart file part, up to 100 MB, or base64 binaryData plus mimeType). The validator stores the bytes before queueing and records jcr:data, jcr:blobId, and ipfsCid on the content node.

Validator-hosted upload is on by default (-Doak.proposal.validator.binary.upload.enabled). In sepolia mode, its settlement must set CAPABILITY_VALIDATOR_HOSTED_BINARY on the payment contract's settleWrite call, or the validator rejects the write. Mock mode, the default, skips that check. See Smart Contract.

The validator also serves a lazy-upload handshake: POST /v1/binary/declare-intent returns an intentToken, GET /v1/binary/check-intent/{token} reports its status, and POST /v1/binary/complete-upload records a CID. On current trunk the session never moves past PENDING on its own, and the recorded CID is not written to the content node, so this flow is not yet usable end to end.

The ipfsCid write parameter is limited to registered enterprise clients and to CIDs the validator already knows.

The Truth → Provenance → Edge Flow

Oak Chain is the source of truth for CIDs, while the bytes flow from storage to edge CDNs.

binary-flow

How It Works

  1. Oak Chain stores the CID (content-addressed hash). This is the truth.
  2. Author or validator storage hosts the binary (IPFS, Azure Blob, or a pinning service). This is provenance.
  3. Edge CDN caches the binary globally (Cloudflare R2, Fastly). This is delivery.
  4. User can verify binary integrity by hashing and comparing to the CID

Why Split It This Way

Data Table
Property
Benefit
Immutable provenance
CID in Oak Chain proves what the binary SHOULD be
Decentralized truth
Validators agree on the CID, not on binary storage
Perfect cache key
CID never changes = CDN can cache forever
Trustless verification
Anyone can hash binary and compare to Oak Chain CID
Economic separation
Validators handle consensus, storage is billed and run separately, CDN handles delivery

Architecture

graph LR
    subgraph Oak["Oak Segment Store"]
        META[Metadata Node
+ CID reference]
    end

    subgraph IPFS["IPFS Network"]
        BIN[Binary Content]
    end

    META -->|CID| BIN

    style META fill:#627EEA,color:#fff
    style BIN fill:#4ade80,color:#000

Why IPFS?

Data Table
Benefit
Description
Content-addressed
CID = hash of content, immutable
Decentralized
No single point of failure
Deduplication
Same content = same CID
Author-owned
In author-hosted mode, authors control their storage

Author-Hosted Flow

1. Author Uploads to IPFS

# Upload binary to IPFS
ipfs add my-image.jpg
# Returns: QmXyz...abc (CID)

2. Author Writes CID to Oak

curl -X POST http://localhost:8090/v1/propose-write \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "proposalId=0x1111111111111111111111111111111111111111111111111111111111111111" \
  -d "walletAddress=0x742d35cc..." \
  -d "organization=PixelPirates" \
  --data-urlencode "message={\"jcr:primaryType\":\"dam:Asset\",\"jcr:content\":{\"renditions\":{\"original\":{\"ipfs:cid\":\"QmXyz...abc\",\"jcr:mimeType\":\"image/jpeg\",\"size\":1048576}}}}" \
  -d "contentType=asset" \
  -d "ethereumTxHash=<tx hash; in mock mode, reuse the proposalId>" \
  -d "signature=<personal_sign of the exact message>"

3. Readers Fetch from IPFS

# Get the node from Oak (clusterId comes from /v1/explorer/content/nav)
curl "http://localhost:8090/v1/explorer/content/clusters/{clusterId}/node?path=/oak-chain/.../content/asset-..."

# The response lists the node's properties; the CID is inside the stored message
{
  "properties": [
    { "name": "message", "type": "STRING", "value": "{\"jcr:primaryType\":\"dam:Asset\", ... \"ipfs:cid\":\"QmXyz...abc\" ...}" },
    ...
  ]
}

# Fetch binary from IPFS gateway
curl https://ipfs.io/ipfs/QmXyz...abc > image.jpg

Validator-Hosted Flow

curl -X POST http://localhost:8090/v1/propose-write \
  -F "proposalId=0x1111111111111111111111111111111111111111111111111111111111111111" \
  -F "walletAddress=0x742d35cc..." \
  -F "organization=PixelPirates" \
  -F "message={\"title\":\"Hero image\"}" \
  -F "contentType=asset" \
  -F "ethereumTxHash=<tx hash; in mock mode, reuse the proposalId>" \
  -F "signature=<personal_sign of the exact message>" \
  -F "file=@my-image.jpg;type=image/jpeg"

The committed node carries the CID in its ipfsCid property and a gateway URL in ipfsGateway.

Direct Binary Access

For performance, use direct IPFS access:

async function getAsset(clusterId, path) {
  // 1. Get the node from Oak
  const res = await fetch(
    `http://validator:8090/v1/explorer/content/clusters/${clusterId}/node?path=${encodeURIComponent(path)}`
  );
  const { properties } = await res.json();

  // 2. Get binary from IPFS (direct, no validator). Validator-hosted
  // writes store the CID in ipfsCid; author-hosted writes keep it in message.
  const cid = properties.find((p) => p.name === 'ipfsCid')?.value;
  const binary = await fetch(`https://ipfs.io/ipfs/${cid}`);

  return binary.blob();
}

Storage Model

Consensus Stores Binary References

Validator Storage:
├── Oak Segments (structured content + binary references)
└── BlobStore (binary blobs, only for validator-hosted uploads)

Authors Own Binaries (Author-Hosted Mode)

Authors are responsible for:

This separates:

Pinning Services

Data Table
Service
Description
Pinata
Managed IPFS pinning
Infura IPFS
Enterprise IPFS
Filecoin
Decentralized storage deals

Example: Image Upload Flow

// Upload the file to your own IPFS node (Kubo RPC API) or pinning service first.
async function addToIpfs(file) {
  const form = new FormData();
  form.append('file', file);
  const res = await fetch('http://127.0.0.1:5001/api/v0/add', { method: 'POST', body: form });
  const { Hash } = await res.json();
  return Hash; // the CID
}

async function uploadImage(file, wallet, org, proposalId, paymentTxHash) {
  // 1. Upload to IPFS
  const cid = await addToIpfs(file);

  // 2. Sign the exact message you will submit
  const message = JSON.stringify({
    'jcr:primaryType': 'dam:Asset',
    'ipfs:cid': cid,
    'jcr:mimeType': file.type,
    'size': file.size,
    'uploadedAt': new Date().toISOString()
  });
  const signature = await window.ethereum.request({
    method: 'personal_sign',
    params: [message, wallet],
  });

  // 3. Write metadata to Oak
  const response = await fetch('http://validator:8090/v1/propose-write', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      proposalId,
      walletAddress: wallet,
      organization: org,
      message,
      contentType: 'asset',
      ethereumTxHash: paymentTxHash, // From settleWrite
      signature,
    })
  });

  // 202 Accepted: the body includes storagePath and operationId
  const result = await response.json();
  return { cid, oakPath: result.storagePath };
}

For author-owned IPFS flows, the important fields are still proposalId, ethereumTxHash, signature, and the CID-backed metadata. The ethereumTxHash comes from the settleWrite call on ValidatorPaymentV5; capability flags are part of that on-chain call, not the validator request.

Garbage Collection

When content is deleted from Oak:

  1. CID reference removed from Oak
  2. Binary remains in IPFS (content-addressed)
  3. Author can unpin if no longer needed
  4. IPFS GC eventually removes unpinned content

Persistence Surface

The Oak/JCR property surface is the constant. Structured content, metadata, and relationships live in the segment store, and Oak Chain hardens that write path with signatures and consensus.

Binary persistence is deployment-specific. The runtime currently contains IPFS, Azure, and AWS BlobStore paths. A CID remains stable for a given byte sequence, but availability requires a reachable pin or storage provider and cache duration remains a delivery policy.

Next Steps