TON Storage
Distributed file storage for the TON Blockchain using torrent-like technology and on-chain payment contracts
TON Storage is a distributed file storage system on the TON Network. Files are shared using a torrent-like protocol, with optional on-chain smart contracts for paid storage guarantees. The TON Blockchain uses TON Storage to distribute archive copies of blocks and state snapshots.
The storage provider guide below explains how to deploy, configure, and operate a TON Storage provider.
Bags
Files are organized into bags, each identified by a unique 256-bit BagID (the hash of the torrent info cell). A bag can contain a single file or a directory.
Files in a bag are split into 128 KiB chunks. A Merkle tree built from SHA-256 hashes allows verification of individual chunks without downloading the full bag. Bag metadata can be exported as a metafile.
Peer discovery
Nodes that store a bag register in the TON DHT under a key derived from the BagID. Clients query the DHT to find seeder addresses for a given bag.
Storage daemon
storage-daemon is the official implementation, distributed as part of the TON software suite. To start:
storage-daemon -v 3 -C global.config.json -I <IP>:3333 -p 5555 -D storage-dbThere,
-v- log verbosity, where3= INFO-C- path to the global network config-I- ADNL listen IP and port, where<IP>is the public or reachable address of the host-p- TCP port for the console interface-D- path to the daemon database — CLI keys are generated inside thecli-keys/subdirectory on first start
Manage bags
| Command | Description |
|---|---|
create <path> -d "description" | Create a new bag from a file or directory |
add-by-hash <hash> -d <dir> | Add a bag by its BagID |
add-by-meta <metafile> -d <dir> | Add a bag from a metafile |
list | List all bags |
list --hashes | List bags with their BagIDs |
get <BagID> | Show full bag information |
get-peers <BagID> | Show peers connected for a bag |
get-meta <BagID> <file> | Export the bag metafile |
download-pause <BagID> | Pause a download |
download-resume <BagID> | Resume a download |
priority-name <BagID> <name> <N> | Set download priority for a file (0 = skip, 255 = highest) |
To download only specific files from a bag, use the --partial flag when adding:
add-by-hash <BagID> --partial file1.txt file2.txtFiles not listed are assigned priority 0 and are not downloaded.
Storage providers
A storage provider is a node that stores bags for a fee, backed by an on-chain smart contract. The provider system has two components:
- Smart contract: deployed on the TON Blockchain, stores the Merkle tree hash of each bag, issues proof challenges, and manages client payments.
storage-daemon: runs on the provider's machine, downloads bags, serves data to peers, and submits storage proofs to the contract.
Provider workflow:
- The provider deploys a main smart contract and shares its address with clients.
- A client creates a bag and sends a storage request to the provider contract.
- The provider contract deploys a per-bag storage contract and notifies the client.
- The provider downloads the bag and activates the per-bag contract.
- The client transfers payment. The provider submits periodic Merkle proofs to prove data possession.
- When the client balance reaches zero or either party closes the contract, remaining funds return to the client and the contract self-destructs.
Storage pricing is expressed in nanogram per megabyte per day.
Integration with TON DNS
A .ton domain can point to a bag using the dns_storage_address record:
dns_storage_address#7473 bag_id:bits256 = DNSRecord;See TON DNS for all record types.
Ecosystem use cases
- NFT metadata: NFT collections can reference off-chain media and metadata stored as bags, using the
BagIDas a stable content identifier. Individual files within a bag are addressed using thetonstorage://<BagID>/pathURI scheme. - Static TON Sites: a bag containing HTML and static assets can be served as a TON Site by combining TON Storage, TON DNS, and TON Proxy.
Run a storage provider
Run the storage-daemon in provider mode, deploy its main contract, and manage paid storage contracts.
Funds and provider data at risk
Provider commands deploy contracts, transfer funds, close storage contracts, and may delete local bag files. Test the complete workflow on testnet first. Mainnet transactions and contract closures cannot be rolled back.
Keep independent backups of required files, verify every address and network, and use a small initialization transfer.
Prerequisites
storage-daemonandstorage-daemon-clifrom the same TON release- A global configuration file for the target network: mainnet or testnet
- A funded wallet on the target network
- A public IP address and reachable ADNL port
Start provider mode
-
Start the
storage-daemonwith-P:storage-daemon -v 3 -P \ -C <GLOBAL_CONFIG_PATH> \ -I <PUBLIC_IP>:<ADNL_PORT> \ -p <CONTROL_PORT> \ -D <STORAGE_DB_DIR> \There,
<GLOBAL_CONFIG_PATH>is the target network's global configuration file.<PUBLIC_IP>and<ADNL_PORT>identify the public ADNL endpoint.<CONTROL_PORT>is the local CLI control port.<STORAGE_DB_DIR>is the daemon's persistent data directory.
On its first launch, the daemon creates CLI keys under
<STORAGE_DB_DIR>/cli-keys. -
Connect
storage-daemon-clito the control interface:storage-daemon-cli \ -I 127.0.0.1:<CONTROL_PORT> \ -k <STORAGE_DB_DIR>/cli-keys/client \ -p <STORAGE_DB_DIR>/cli-keys/server.pub
Deploy the provider contract
Run the deployment command inside storage-daemon-cli:
deploy-providerInitialization transfer
Use testnet for the first deployment. The command prints an address that requires a non-bounceable initialization message containing 1 GRAM. Verify the network and address before sending funds. A transfer to the wrong network or address cannot be recovered.
Do not attach a large balance to the initialization message. Verify the deployed provider:
get-provider-infoFund the deployed provider contract separately to cover later transaction fees. Increase the balance only after verifying the deployment.
Configure capacity
Set the maximum number and total size of managed storage contracts:
set-provider-config \
--max-contracts <MAX_CONTRACTS> \
--max-total-size <MAX_TOTAL_SIZE_BYTES>There,
<MAX_CONTRACTS>is the maximum number of concurrent storage contracts.<MAX_TOTAL_SIZE_BYTES>is the maximum combined bag size in bytes.
Configure contract terms
Keep new requests disabled while configuring the on-chain terms:
set-provider-params --accept 0 \
--rate <RATE_NANOGRAM_PER_MB_DAY> \
--max-span <MAX_PROOF_SPAN_SECONDS> \
--min-file-size <MIN_BAG_SIZE_BYTES> \
--max-file-size <MAX_BAG_SIZE_BYTES>There,
<RATE_NANOGRAM_PER_MB_DAY>is the price per megabyte per day in nanograms.<MAX_PROOF_SPAN_SECONDS>is the maximum interval between storage proofs.<MIN_BAG_SIZE_BYTES>and<MAX_BAG_SIZE_BYTES>define the accepted bag-size range.
Omitted flags retain their prior values. Wait for each on-chain update and verify it with get-provider-info before submitting another update.
Enable requests after verifying every parameter:
set-provider-params --accept 1The daemon then downloads accepted bags, distributes them to peers, and submits storage proofs.
Request storage from a provider
-
Inspect a provider's on-chain terms by its contract address:
get-provider-params <PROVIDER_ADDRESS>The result states whether the provider accepts contracts, its bag-size limits, its rate, and its maximum proof interval.
-
Create a bag with the storage daemon bag commands, record its
BagID, and generate a request body:new-contract-message <BAG_ID> \ <MESSAGE_BODY_PATH> \ --query-id <QUERY_ID> \ --provider <PROVIDER_ADDRESS>There,
<BAG_ID>is the bag's 256-bit identifier.<MESSAGE_BODY_PATH>receives the internal message body, not a complete message.<QUERY_ID>is an integer from0through2^64 - 1.
Large bags can take longer to process. Review the rate and proof interval printed by the command before sending the body. The provider contract rejects the request if its parameters change before processing.
-
Send the generated body in a bounceable internal message to the provider contract. A successful deployment returns
0xbf7bd0c1with the original query ID. After the provider downloads the bag and activates the contract, it returns0xd4caedcd.
Monitor the client balance
The storage contract deducts provider earnings from the client balance according to the configured rate. The initial balance comes from the storage request message. Any wallet can top up the storage contract with a transfer.
Call get_storage_contract_data to inspect the contract. Its second return value, balance, is the unpaid client balance.
A storage contract can close when:
- The provider declines it before activation.
- Its client balance reaches
0. - The provider closes it.
- The client sends
0x79f937eawith any 64-bit query ID from the client's wallet.
Operate active contracts
List active contracts and their balances:
get-provider-info --contracts --balancesClient$ is the unpaid client balance. The difference between Contract$ and Client$ is available provider earnings.
Withdraw earnings from one per-bag storage contract managed by the provider:
withdraw <STORAGE_CONTRACT_ADDRESS>Withdraw from every contract with at least 1 GRAM available:
withdraw-allClose a storage contract
Local bag files may be deleted
Closing a contract may delete its local bag files when no other active contract uses the bag. Keep an independent copy before closing the contract. The on-chain closure cannot be rolled back.
Close one provider-managed contract:
close-contract <STORAGE_CONTRACT_ADDRESS>Closing transfers available provider earnings to the main provider contract.
Transfer provider funds
Provider funds at risk
Test transfers with a small amount on testnet. Verify the destination, amount, and network before sending. Mainnet transfers cannot be rolled back.
Transfer nanograms from the main provider contract:
send-coins <DESTINATION_ADDRESS> <AMOUNT_NANOGRAM>
send-coins <DESTINATION_ADDRESS> <AMOUNT_NANOGRAM> --message "<MESSAGE>"There,
<DESTINATION_ADDRESS>is the receiving account.<AMOUNT_NANOGRAM>is the transferred amount in nanograms.<MESSAGE>is an optional text message.
Protect provider data
The list command shows all bags managed by the daemon. Do not remove these bags or use the provider daemon to manage unrelated bags. Keep independent copies of data that must remain available after a storage contract closes.