Catalog PyPI
PACKAGE EXPLORERView in registry

0g-storage-sdk

PyPI

Official Python SDK for 0G Storage - A decentralized storage network with merkle tree verification

0gblockchaincryptographydecentralizedmerklestorageweb3
Versions collected1
Direct dependencies6From release 0.4.0
Last observed20 Oct, 15:02 UTC

README

Version 0.4.0 documentation

0G Storage — Python SDK

Python 3.8+ License: MIT PyPI

Python SDK for the 0G Storage Network — a decentralized storage layer that spreads files across sharded nodes and anchors integrity with on-chain merkle proofs.

A direct port of the official TypeScript SDK @0glabs/0g-ts-sdk. Files uploaded with either SDK produce byte-exact identical merkle roots.

📚 Full developer documentation: og-py.vercel.app

What you get

  • Merkle tree generation — Keccak256, 256-byte chunks, 256 KB segments — output identical to the TS SDK
  • File upload — Flow contract submission, parallel segment uploads, automatic shard routing, retry on transient failures
  • File download — by root hash, with optional proof verification per segment
  • Splitable upload — for files larger than 4 GB, with batched fragment processing
  • Fragment download — reassemble multi-fragment files in order
  • KV storage — append-only key-value streams with version-aware reads, batched writes, and per-key access control
  • Storage node RPC — 14-method client for direct node interaction (get_status, upload/download segments, shard configs, sector proofs)

Installation

pip install 0g-storage-sdk

Requires Python 3.8+.

The package name 0g-storage-sdk isn't a valid Python identifier, so the SDK ships its top-level modules directly:

from core.indexer import Indexer
from core.file import ZgFile
from core.merkle import MerkleTree
from core.kv import KvClient, StreamDataBuilder, Batcher

Dependencies

Auto-installed:

  • pycryptodome — Keccak256 hashing
  • web3 — blockchain RPC and contract calls
  • eth-account — transaction signing
  • requests — HTTP client

Quickstart — upload & download

from core.indexer import Indexer
from core.file import ZgFile
from eth_account import Account

INDEXER_RPC    = "https://indexer-storage-testnet-turbo.0g.ai"
BLOCKCHAIN_RPC = "https://evmrpc-testnet.0g.ai"
PRIVATE_KEY    = "0xYOUR_PRIVATE_KEY"

indexer = Indexer(INDEXER_RPC)
account = Account.from_key(PRIVATE_KEY)

# Upload
file = ZgFile.from_file_path("./data.txt")
result, err = indexer.upload(
    file,
    BLOCKCHAIN_RPC,
    account,
    {
        "tags": b"\x00",
        "finalityRequired": True,
        "expectedReplica": 1,
        "account": account,
    },
)
file.close()

if err is None:
    print(f"Uploaded: {result['rootHash']}")
    print(f"Tx:       {result['txHash']}")

# Download (wait 3–5 minutes for shard propagation first)
err = indexer.download(result["rootHash"], "./output.txt")

Client-side encryption

Files can be encrypted before upload using AES-256-CTR (v1) or ECIES on secp256k1 (v2). Both schemes are wire-compatible with the TS SDK and the Go storage client.

import os
from core.indexer import Indexer

indexer = Indexer(INDEXER_URL)
file = ZgFile.from_file_path("./secret.txt")

# v1 — symmetric (you generate and manage a 32-byte key out-of-band)
key = os.urandom(32)
result, err = indexer.upload(file, BLOCKCHAIN_RPC, signer, upload_opts={
    "encryption": {"type": "aes256", "key": key},
})

# Download + auto-decrypt (best-effort; raw bytes on disk if no match)
indexer.download(result["rootHash"], "./secret.txt", symmetric_key=key)

# v2 — ECIES (anyone with `recipient_pub` can encrypt; only the holder of
# the matching private key can decrypt)
recipient_pub = "0x031b84c5...078f"  # 33-byte compressed secp256k1 pubkey
result, err = indexer.upload(file, BLOCKCHAIN_RPC, signer, upload_opts={
    "encryption": {"type": "ecies", "recipient_pub_key": recipient_pub},
})
indexer.download(result["rootHash"], "./secret.txt", private_key=PRIVATE_KEY_HEX)

To detect whether a stored file is encrypted before downloading:

header, err = indexer.peek_header(root_hash)
if header is None:
    print("Plaintext file")
elif header.version == 1:
    print("AES-256 — supply symmetric_key=...")
elif header.version == 2:
    print("ECIES — supply private_key=...")

Compute a merkle root locally

No network calls — pure local hashing:

from core.file import ZgFile

file = ZgFile.from_bytes(b"hello, 0G Storage")
tree, err = file.merkle_tree()
print(tree.root_hash())
file.close()

Large files (>4 GB)

from core.uploader import Uploader

# After selecting nodes via indexer.select_nodes(...)
uploader = Uploader(clients, BLOCKCHAIN_RPC, flow)

result, err = uploader.splitable_upload(
    file,
    {
        "account":          account,
        "fragmentSize":     4 * 1024 * 1024 * 1024,   # 4 GB per fragment
        "finalityRequired": True,
    },
)

# result["rootHashes"] is a list — one hash per fragment, in order

Download with Downloader.download_fragments(roots, filename) — fragments are concatenated in the order you pass them.

KV storage

from core.kv import Batcher

batcher = Batcher(version=1, clients=clients, flow=flow, provider=BLOCKCHAIN_RPC)

stream_id = "0x" + "11" * 32
batcher.set(stream_id, b"user:42",  b'{"name": "Alice"}')
batcher.set(stream_id, b"user:101", b'{"name": "Bob"}')

result, err = batcher.exec({"account": account})

Read back via KvClient:

from core.kv import KvClient

kv = KvClient("http://storage-node.example.com:5678")
value = kv.get_value(stream_id, b"user:42")
# value.data is base64-encoded

Generate proofs

proof = tree.proof_at(2)            # proof for the leaf at index 2

err = proof.validate(
    root_hash      = tree.root_hash(),
    content        = b"chunk 3 data",
    position       = 2,
    num_leaf_nodes = len(tree.leaves),
)

if err is None:
    print("Valid proof")

Public API surface

Everything you need is exported at module level:

SymbolPurpose
Indexer(url)Indexer RPC client — upload, download, select_nodes, get_file_locations
ZgFile.from_file_path(path) / .from_bytes(data)File abstraction with merkle tree generation
MerkleTree, Proof, LeafNodeDirect merkle tree construction and verification
Uploader, DownloaderLower-level upload/download with retry options
StorageNode(url)Direct RPC to a storage node (14 methods)
KvClient(rpc)KV reads — get_value, get_first/last/next/prev, iterators
StreamDataBuilder(version)Build StreamData blobs (writes + access control)
Batcher(version, clients, flow, provider)High-level batched KV writes
KvIteratorCursor-style traversal over a stream
select_nodes, check_replica, is_valid_configNode selection helpers
RetryOpts, tx_with_gas_adjustment, submit_with_gas_adjustmentRetry and gas utilities
Exceptions: StorageError, UploadError, DownloadError, MerkleTreeError, ContractError, RetryableError, ...Structured exception hierarchy with error_code, context, cause

For the full per-class API and worked examples, see the Storage SDK docs.

Network configuration

NetworkChain IDBlockchain RPCIndexer RPC
Mainnet16661https://evmrpc.0g.aihttps://indexer-storage-turbo.0g.ai
Testnet (Galileo)16602https://evmrpc-testnet.0g.aihttps://indexer-storage-testnet-turbo.0g.ai

Get testnet tokens at faucet.0g.ai.

Contract addresses

The SDK reads the Flow contract address from each storage node's networkIdentity automatically — direct addressing is rarely needed.

ContractTestnetMainnet
Flow0x22E03a6A89B950F1c82ec5e74F8eCa321a1052960x62D4144dB0F0a6fBBaeb6296c785C71B3D57C526

Constants

DEFAULT_CHUNK_SIZE = 256          # bytes per merkle leaf
DEFAULT_SEGMENT_SIZE = 262144     # 256 KB per segment (1024 chunks)
SMALL_FILE_SIZE_THRESHOLD = 256 * 1024
DEFAULT_FRAGMENT_SIZE = 4 * 1024 * 1024 * 1024   # 4 GB

Testing

cd 0g_py_storage
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
pytest tests/ -v

Verifying parity with the TypeScript SDK

from core.file import ZgFile

tree, _ = ZgFile.from_bytes(b"0G Storage parity check").merkle_tree()
print(tree.root_hash())

Run the equivalent in the TypeScript SDK — the printed hashes match byte-for-byte.

Error handling

from utils.error_handler import is_retryable

result, err = indexer.upload(file, BLOCKCHAIN_RPC, account, opts)
if err is not None:
    if is_retryable(err):
        # transient — retry with backoff
        ...
    else:
        raise err

For a tour of the full exception hierarchy and ErrorContext / wrap_with_context helpers, see the Error Handling docs.

Contributing

Contributions should preserve TypeScript SDK parity. Each PR should:

  1. Match the TS SDK behavior — verify outputs match byte-for-byte
  2. Include tests for new functionality
  3. Update the README and docs as needed
  4. Reference TS SDK line numbers in code comments where applicable

License

MIT

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.4.018 May 20266

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
0glabs/0g-py-sdk

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 15:02 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:b0569cd1035e5504b0c6a39932a448c6011d4948cb3a8047bcbcce1927d48443