DocShards: Large File Handling
Complete guide to TELA's DocShards system for deploying large files that exceed smart contract size limits.
What are DocShards?
DocShards are TELA's solution for handling files larger than the 18KB smart contract limit. Large files are split into smaller "shards" that can each be deployed as separate TELA-DOC-1 contracts, then automatically reconstructed when the application is served.
The Problem
Traditional Web:
Large File (50KB) → Web Server → Browser ✅
TELA Without DocShards:
Large File (50KB) → Smart Contract ❌ (18KB limit exceeded)
TELA With DocShards:
Large File (50KB) → Split into 3 shards → 3 Smart Contracts → Reconstruct → Browser ✅The Solution
DocShards Process:
1. large-file.js (50KB)
2. Split → large-file-1.js (17.5KB) + large-file-2.js (17.5KB) + large-file-3.js (15KB)
3. Deploy → 3 separate DOC contracts with .shard tags
4. INDEX → References all shards with .shards tag
5. Serve → Automatically reconstructs original large-file.jsHow DocShards Work
Technical Implementation
// TELA constants for sharding
const SHARD_SIZE = int64(17500) // 17.5KB per shard
// Calculate shard requirements
func analyzeForSharding(content []byte) {
totalShards, fileSize := tela.GetTotalShards(content)
fmt.Printf("File size: %d bytes\n", fileSize)
fmt.Printf("Shards needed: %d\n", totalShards)
fmt.Printf("Shard size: ~%.1fKB each\n", float64(SHARD_SIZE)/1024)
}Shard Creation Process
- File Analysis - Determine if sharding is needed
- Content Splitting - Split into SHARD_SIZE chunks
- Shard Naming - Create numbered shard files
- Individual Deployment - Deploy each shard as DOC with
.shardtag - INDEX Creation - Create INDEX with
.shardstag for reconstruction
Creating DocShards
Using TELA-CLI
# 1. Analyze file size
file-info large-application.js
# Output: Size: 45.2 KB ❌ Exceeds limit, needs sharding
# 2. Create shard files
file-shard large-application.js
# Creates: large-application-1.js, large-application-2.js, large-application-3.js
# 3. Verify reconstruction works
file-construct large-application-1.js
# Recreates: large-application.js
# 4. Deploy each shard
install-doc large-application-1.js # dURL: large-application-1.tela.shard
install-doc large-application-2.js # dURL: large-application-2.tela.shard
install-doc large-application-3.js # dURL: large-application-3.tela.shard
# 5. Create INDEX with .shards tag
install-index MyApp # dURL: myapp.tela.shards
# Include all shard SCIDs in the INDEXUsing Go API
package main
import (
"fmt"
"os"
"github.com/civilware/tela"
"github.com/deroproject/derohe/walletapi"
)
func deployLargeFile(wallet *walletapi.Wallet_Disk, filePath string) error {
// 1. Read file content
content, err := os.ReadFile(filePath)
if err != nil {
return err
}
// 2. Check if sharding is needed
size := tela.GetCodeSizeInKB(string(content))
if size <= 18.0 {
return fmt.Errorf("file doesn't need sharding (%.2fKB)", size)
}
fmt.Printf("📊 File needs sharding: %.2fKB\n", size)
// 3. Create shard files
err = tela.CreateShardFiles(filePath, "", nil)
if err != nil {
return fmt.Errorf("failed to create shards: %v", err)
}
// 4. Calculate shard count
shardCount, _ := tela.GetTotalShards(content)
fmt.Printf("📦 Created %d shard files\n", shardCount)
// 5. Deploy each shard
var shardSCIDs []string
fileName := filepath.Base(filePath)
for i := 1; i <= shardCount; i++ {
shardName := fmt.Sprintf("%s-%d%s",
strings.TrimSuffix(fileName, filepath.Ext(fileName)),
i,
filepath.Ext(fileName))
shardContent, err := os.ReadFile(filepath.Join(filepath.Dir(filePath), shardName))
if err != nil {
return err
}
// Create DOC for shard
shardDOC := &tela.DOC{
DocType: tela.ParseDocType(fileName), // Original file type
Code: string(shardContent),
DURL: fmt.Sprintf("%s.tela.shard", strings.TrimSuffix(shardName, filepath.Ext(shardName))),
Headers: tela.Headers{
NameHdr: shardName,
DescrHdr: fmt.Sprintf("Shard %d of %s", i, fileName),
},
}
txid, err := tela.Installer(wallet, 2, shardDOC)
if err != nil {
return fmt.Errorf("failed to deploy shard %d: %v", i, err)
}
shardSCIDs = append(shardSCIDs, txid)
fmt.Printf("✅ Shard %d deployed: %s\n", i, txid)
}
// 6. Create INDEX with .shards tag
index := &tela.INDEX{
DURL: fmt.Sprintf("%s.tela.shards", strings.TrimSuffix(fileName, filepath.Ext(fileName))),
DOCs: shardSCIDs,
Headers: tela.Headers{
NameHdr: fmt.Sprintf("Sharded %s", fileName),
DescrHdr: fmt.Sprintf("Large file deployed as %d shards", shardCount),
},
}
indexTXID, err := tela.Installer(wallet, 2, index)
if err != nil {
return fmt.Errorf("failed to deploy INDEX: %v", err)
}
fmt.Printf("🎉 Sharded file deployed: %s\n", indexTXID)
return nil
}DocShards Best Practices
Optimal Sharding Strategy
Sharding Guidelines
- Shard files larger than 18KB after compression
- Use compression before sharding when possible
- Keep related content together in shards
- Test reconstruction locally before deployment
// Intelligent sharding decision
func shouldUseSharding(filePath string) (bool, string, error) {
content, err := os.ReadFile(filePath)
if err != nil {
return false, "", err
}
originalSize := tela.GetCodeSizeInKB(string(content))
// Try compression first
compressed, err := tela.Compress(content, ".gz")
if err == nil {
compressedSize := tela.GetCodeSizeInKB(compressed)
if compressedSize <= 18.0 {
return false, "use compression", nil
}
if compressedSize < originalSize * 0.7 { // 30%+ reduction
return true, "compress then shard", nil
}
}
if originalSize > 18.0 {
return true, "shard without compression", nil
}
return false, "deploy directly", nil
}Shard Organization
Project Structure for Sharding:
my-large-app/
├── src/
│ ├── small-file.js (12KB) → Deploy directly
│ ├── medium-file.js (16KB) → Compress then deploy
│ └── large-file.js (45KB) → Compress then shard
├── shards/ (auto-generated)
│ ├── large-file-1.js.gz
│ ├── large-file-2.js.gz
│ └── large-file-3.js.gz
└── deployment-plan.json (tracking)Deployment Tracking
// Track complex deployments
type DeploymentPlan struct {
ProjectName string `json:"projectName"`
Files []FileDeploymentInfo `json:"files"`
Shards []ShardDeploymentInfo `json:"shards"`
INDEX IndexDeploymentInfo `json:"index"`
}
type FileDeploymentInfo struct {
FileName string `json:"fileName"`
FilePath string `json:"filePath"`
Size float64 `json:"size"`
Compressed bool `json:"compressed"`
SCID string `json:"scid"`
Status string `json:"status"`
}
type ShardDeploymentInfo struct {
OriginalFile string `json:"originalFile"`
ShardFiles []string `json:"shardFiles"`
ShardSCIDs []string `json:"shardSCIDs"`
Status string `json:"status"`
}
// Save deployment progress
func saveDeploymentPlan(plan *DeploymentPlan) error {
data, err := json.MarshalIndent(plan, "", " ")
if err != nil {
return err
}
return os.WriteFile("deployment-plan.json", data, 0644)
}Advanced DocShards Patterns
Multi-File Sharding
For projects with multiple large files:
func deployMultipleShardedFiles(wallet *walletapi.Wallet_Disk, largeFiles []string) (*tela.INDEX, error) {
var allDOCs []string
for _, filePath := range largeFiles {
fmt.Printf("Processing large file: %s\n", filePath)
// Create shards
err := tela.CreateShardFiles(filePath, ".gz", nil)
if err != nil {
return nil, err
}
// Deploy shards
shardSCIDs, err := deployFileShards(wallet, filePath)
if err != nil {
return nil, err
}
allDOCs = append(allDOCs, shardSCIDs...)
}
// Create master INDEX
index := &tela.INDEX{
DURL: "large-app.tela.shards",
DOCs: allDOCs,
Headers: tela.Headers{
NameHdr: "Large Application",
DescrHdr: fmt.Sprintf("Application with %d sharded files", len(largeFiles)),
},
}
return index, nil
}Conditional Sharding
// Client-side: Handle sharded vs non-sharded content
class TELAContentLoader {
async loadContent(scid) {
const info = await this.getContentInfo(scid);
if (info.dURL.endsWith('.shards')) {
// Handle sharded content
return await this.loadShardedContent(scid);
} else {
// Handle regular content
return await this.loadRegularContent(scid);
}
}
async loadShardedContent(scid) {
console.log('📦 Loading sharded content...');
// TELA automatically handles reconstruction
// Your app just needs to be aware it might take longer
return await this.fetchWithProgress(scid);
}
async fetchWithProgress(scid) {
return new Promise((resolve, reject) => {
// Show loading indicator for sharded content
this.showLoadingIndicator('Reconstructing large files...');
// Fetch normally - TELA handles the complexity
fetch(`/content/${scid}`)
.then(response => response.text())
.then(content => {
this.hideLoadingIndicator();
resolve(content);
})
.catch(reject);
});
}
}Troubleshooting DocShards
Common Issues
Problem: Shard reconstruction fails
# Check shard integrity
file-construct large-file-1.js
# If this fails, shards may be corrupted
# Recreate shards
rm large-file-*.js # Remove old shards
file-shard original-large-file.js # RecreateProblem: Deployment order issues
# Deploy shards in correct order
install-doc large-file-1.js # First shard
install-doc large-file-2.js # Second shard
install-doc large-file-3.js # Last shard
# Then create INDEX
install-index MyApp # With .shards tagProblem: Performance issues with many shards
// Optimize shard deployment
func deployShardsOptimally(wallet *walletapi.Wallet_Disk, shardFiles []string) error {
// Deploy in parallel (be careful with rate limits)
semaphore := make(chan struct{}, 3) // Max 3 concurrent
for _, shardFile := range shardFiles {
go func(file string) {
semaphore <- struct{}{} // Acquire
defer func() { <-semaphore }() // Release
// Deploy shard
deployShardFile(wallet, file)
}(shardFile)
}
return nil
}Advanced Sharding Techniques
Intelligent Content Splitting
// Smart content-aware sharding
func intelligentSharding(content []byte, contentType string) ([][]byte, error) {
switch contentType {
case "TELA-JS-1":
// Split JavaScript at function boundaries
return splitJavaScriptIntelligently(content)
case "TELA-CSS-1":
// Split CSS at rule boundaries
return splitCSSIntelligently(content)
case "TELA-HTML-1":
// Split HTML at logical sections
return splitHTMLIntelligently(content)
default:
// Use standard byte-based splitting
return splitByBytes(content)
}
}
func splitJavaScriptIntelligently(content []byte) ([][]byte, error) {
jsCode := string(content)
// Find function boundaries
functions := extractJSFunctions(jsCode)
var shards [][]byte
var currentShard []byte
for _, function := range functions {
functionBytes := []byte(function)
// If adding this function would exceed shard size, start new shard
if len(currentShard)+len(functionBytes) > int(SHARD_SIZE) {
if len(currentShard) > 0 {
shards = append(shards, currentShard)
currentShard = nil
}
}
currentShard = append(currentShard, functionBytes...)
}
// Add final shard
if len(currentShard) > 0 {
shards = append(shards, currentShard)
}
return shards, nil
}Compression + Sharding
Combine compression with sharding for maximum efficiency:
func optimizedSharding(filePath string) error {
content, err := os.ReadFile(filePath)
if err != nil {
return err
}
originalSize := tela.GetCodeSizeInKB(string(content))
fmt.Printf("Original size: %.2fKB\n", originalSize)
// Try compression first
compressed, err := tela.Compress(content, ".gz")
if err != nil {
return err
}
compressedSize := tela.GetCodeSizeInKB(compressed)
fmt.Printf("Compressed size: %.2fKB\n", compressedSize)
if compressedSize <= 18.0 {
fmt.Println("✅ Compression sufficient, no sharding needed")
return nil
}
// Create compressed shards
err = tela.CreateShardFiles(filePath, ".gz", []byte(compressed))
if err != nil {
return err
}
shardCount, _ := tela.GetTotalShards([]byte(compressed))
fmt.Printf("🧩 Created %d compressed shards\n", shardCount)
return nil
}Real-World DocShards Examples
Large JavaScript Framework
// Example: Deploying React or Vue.js to TELA
func deployJSFramework(wallet *walletapi.Wallet_Disk, frameworkPath string) error {
// 1. Analyze framework size
content, err := os.ReadFile(frameworkPath)
if err != nil {
return err
}
size := tela.GetCodeSizeInKB(string(content))
fmt.Printf("📊 Framework size: %.2fKB\n", size)
if size <= 18.0 {
// Small framework, deploy directly
return deployRegularDOC(wallet, frameworkPath)
}
// 2. Create optimized shards
// First try compression
compressed, err := tela.Compress(content, ".gz")
if err != nil {
return err
}
// Create shards from compressed content
err = tela.CreateShardFiles(frameworkPath, ".gz", []byte(compressed))
if err != nil {
return err
}
// 3. Deploy framework shards
shardCount, _ := tela.GetTotalShards([]byte(compressed))
var shardSCIDs []string
for i := 1; i <= shardCount; i++ {
shardSCID, err := deployFrameworkShard(wallet, frameworkPath, i)
if err != nil {
return err
}
shardSCIDs = append(shardSCIDs, shardSCID)
}
// 4. Create framework INDEX
frameworkIndex := &tela.INDEX{
DURL: "react.tela.shards", // or vue.tela.shards
DOCs: shardSCIDs,
Headers: tela.Headers{
NameHdr: "React Framework",
DescrHdr: "React.js deployed as DocShards",
},
}
indexTXID, err := tela.Installer(wallet, 2, frameworkIndex)
if err != nil {
return err
}
fmt.Printf("🎉 Framework deployed: %s\n", indexTXID)
return nil
}Large Dataset Handling
// Example: Deploying large JSON datasets
func deployLargeDataset(wallet *walletapi.Wallet_Disk, dataPath string) error {
// 1. Load and validate JSON
content, err := os.ReadFile(dataPath)
if err != nil {
return err
}
// Validate JSON
var jsonData interface{}
if err := json.Unmarshal(content, &jsonData); err != nil {
return fmt.Errorf("invalid JSON: %v", err)
}
size := tela.GetCodeSizeInKB(string(content))
fmt.Printf("📊 Dataset size: %.2fKB\n", size)
if size <= 18.0 {
return deployRegularDOC(wallet, dataPath)
}
// 2. Compress JSON (usually very effective)
compressed, err := tela.Compress(content, ".gz")
if err != nil {
return err
}
compressedSize := tela.GetCodeSizeInKB(compressed)
fmt.Printf("📦 Compressed to: %.2fKB (%.1f%% reduction)\n",
compressedSize, (1-compressedSize/size)*100)
if compressedSize <= 18.0 {
// Compression sufficient
doc := &tela.DOC{
DocType: tela.DOC_JSON,
Code: compressed,
DURL: "dataset.tela",
Compression: ".gz",
Headers: tela.Headers{
NameHdr: filepath.Base(dataPath) + ".gz",
DescrHdr: "Compressed JSON dataset",
},
}
txid, err := tela.Installer(wallet, 2, doc)
if err != nil {
return err
}
fmt.Printf("✅ Compressed dataset deployed: %s\n", txid)
return nil
}
// 3. Need sharding even with compression
err = tela.CreateShardFiles(dataPath, ".gz", []byte(compressed))
if err != nil {
return err
}
// Deploy shards and INDEX
return deployDatasetShards(wallet, dataPath)
}Performance Considerations
Reconstruction Performance
// Client-side: Optimize sharded content loading
class ShardedContentManager {
constructor() {
this.loadingCache = new Map();
this.reconstructionWorkers = new Map();
}
async loadShardedContent(scid) {
// Check if already loading
if (this.loadingCache.has(scid)) {
return await this.loadingCache.get(scid);
}
// Create loading promise
const loadingPromise = this.performShardedLoad(scid);
this.loadingCache.set(scid, loadingPromise);
try {
const result = await loadingPromise;
return result;
} finally {
this.loadingCache.delete(scid);
}
}
async performShardedLoad(scid) {
// Show progress for large reconstructions
this.showProgress('Loading sharded content...', 0);
// TELA handles the actual reconstruction
// We just provide user feedback
const startTime = Date.now();
try {
const content = await fetch(`/content/${scid}`).then(r => r.text());
const loadTime = Date.now() - startTime;
console.log(`📦 Sharded content loaded in ${loadTime}ms`);
this.hideProgress();
return content;
} catch (error) {
this.hideProgress();
throw error;
}
}
showProgress(message, percent) {
// Implement progress UI
const progressEl = document.getElementById('loading-progress');
if (progressEl) {
progressEl.textContent = `${message} ${percent}%`;
progressEl.style.display = 'block';
}
}
hideProgress() {
const progressEl = document.getElementById('loading-progress');
if (progressEl) {
progressEl.style.display = 'none';
}
}
}DocShards vs Alternatives
When to Use DocShards
| Scenario | Recommended Approach | Reason |
|---|---|---|
| File 10-18KB | Compression only | Simpler deployment |
| File 18-50KB | Compression + DocShards | Optimal balance |
| File 50KB+ | Architecture redesign | Consider splitting functionality |
| Multiple large files | DocShards + Libraries | Modular approach |
| Dynamic content | External loading | Consider if TELA is appropriate |
Alternatives to Consider
-
Content Splitting:
- Split large files into logical components
- Deploy each component separately
- Load components dynamically
-
External References:
- Store large assets off-chain
- Reference via IPFS or traditional CDN
- Use TELA for application logic only
-
Lazy Loading:
- Load core application first
- Load additional features on demand
- Progressive enhancement approach
DocShards enable TELA to handle arbitrarily large files while maintaining the security and decentralization benefits of blockchain storage. Use this system when you need to deploy large frameworks, datasets, or complex applications that exceed standard smart contract limits.
Next: Compression & Optimization - Learn how to minimize storage costs and maximize performance.