Store binary data
Synapse blob documents hold opaque binary payloads in a content-addressed store. VoltDrive uses the same mechanism for binary files; any synapse client can use the blob APIs directly.
api
Use the Sync API blob methods:
- HasSynapseBlob — check whether a hash is already stored.
- PutSynapseBlob — upload bytes. The first message must be
startwithsynapse_id, the expected SHA-256 (unpadded base64url), andsize; later messages carry payload chunks. - GetSynapseBlob — download bytes by hash.
- SetSynapseDocumentMetadata — declare a single
type: "blob"schema entry and bind the document withblob_hash.
The hash is SHA-256 of the raw payload bytes, encoded as unpadded base64url. Empty payloads are never stored: bind the document to the SHA-256 of empty bytes and skip PutSynapseBlob.
permissions
HasSynapseBlob and GetSynapseBlob require volt:database-read on the synapse (volt:database-write also implies read). PutSynapseBlob and SetSynapseDocumentMetadata require volt:database-write.
cli
There is no dedicated blob CLI command. Folder sync will upload and download binary files as blobs when the drive is configured to include them:
volt create-drive-instance "photos" ./photos @photos-drive --include-binary-filesBy default binary files are skipped. See create-drive-instance and Drive.
javascript
Hash the payload, upload it if needed, then bind a blob document.
import crypto from "crypto";import fs from "fs";import grpc from "@grpc/grpc-js";import { VoltClient } from "@tdxvolt/volt-client-grpc";
const synapseId = "@photos-drive";const documentId = "photo-001";const payload = fs.readFileSync("./photo.jpg");const hash = crypto.createHash("sha256").update(payload).digest("base64url");
const client = new VoltClient(grpc);await client.initialise("./volt.config.json");
const has = await client.HasSynapseBlob({ synapse_id: synapseId, hash,});
if (!has.exists && payload.length > 0) { await new Promise((resolve, reject) => { const put = client.PutSynapseBlob(); put.on("error", reject); put.on("end", resolve); put.write({ start: { synapse_id: synapseId, hash, size: payload.length, }, }); put.write({ block: payload }); put.end(); });}
await client.SetSynapseDocumentMetadata({ synapse_id: synapseId, document_id: documentId, metadata: [{ name: "content", type: "blob" }], blob_hash: hash, json: JSON.stringify({ name: "photo.jpg", s: payload.length }),});Download by hash once GetSynapseDocumentState reports SYNAPSE_DOCUMENT_STATE_READY on this Volt (metadata can arrive before the bytes):
const chunks = [];const get = client.GetSynapseBlob({ synapse_id: synapseId, hash });
get.on("data", (response) => { if (response.block) { chunks.push(Buffer.from(response.block, "base64")); }});
get.on("end", () => { const bytes = Buffer.concat(chunks); fs.writeFileSync("./photo-download.jpg", bytes);});Wait for READY before treating the local copy as complete. For a blob document, READY means this Volt holds the payload in its blob store.
C++
std::string hash;tdx::crypto::CryptoManager crypto;crypto.sha256Base64(payload, hash);
tdx::volt_api::volt::v1::HasSynapseBlobRequest hasReq;hasReq.set_synapse_id(synapseId);hasReq.set_hash(hash);tdx::volt_api::volt::v1::HasSynapseBlobResponse hasResp;auto result = voltApi->hasSynapseBlob(hasReq, hasResp);
if (result == tdx::error_code::ok && !hasResp.exists() && !payload.empty()) { result = voltApi->putSynapseBlobSync(synapseId, hash, payload);}
tdx::volt_api::volt::v1::SetSynapseDocumentMetadataRequest metaReq;metaReq.set_synapse_id(synapseId);metaReq.set_document_id(documentId);auto* md = metaReq.add_metadata();md->set_name("content");md->set_type("blob");metaReq.set_blob_hash(hash);tdx::volt_api::volt::v1::SetSynapseDocumentMetadataResponse metaResp;voltApi->setSynapseDocumentMetadata(metaReq, metaResp);
std::string downloaded;voltApi->getSynapseBlobSync(synapseId, hash, downloaded);