Decentralized Storage (IPFS)
Private IPFS network for MPC enclave storage, Vault ejection capabilities, and decentralized content addressing across Sonr's node infrastructure
Decentralized Storage with IPFS
Scope
This document covers Sonr's private IPFS network implementation for MPC share storage, Vault ejection, and content addressing. It includes network architecture, node integration, security models, and data management. This document does not cover public IPFS integration or low-level libp2p protocols—see the IPFS reference documentation for protocol details.
Audience
Infrastructure engineers operating Sonr nodes. Developers building applications with decentralized storage. System architects designing distributed systems. Prerequisites: Understanding of distributed storage concepts, basic IPFS knowledge, and familiarity with content-addressed storage.
Summary
Sonr operates a private IPFS network for Multi-Party Computation (MPC) share storage and Vault ejection. All node types—snrd (blockchain), hway (proxy), and motr (enclave)—automatically join this network. The system enables Vaults to operate independently of the blockchain while maintaining functionality. Data remains isolated from public IPFS through network-level security.
Network Architecture
Private Network Isolation
Sonr's IPFS network operates separately from public IPFS:
# Private network configuration
export LIBP2P_FORCE_PNET=1
export IPFS_SWARM_KEY="/key/swarm/psk/1.0.0/..."
# Ensures:
# - No public IPFS connections
# - Only authorized nodes join
# - Data remains within Sonr
# - Network-level access control
Node Integration
All Sonr infrastructure components connect to private IPFS:
MPC Share Storage
Distributed Key Management
MPC shares distribute across multiple nodes:
interface MPCShareStorage {
shareID: string;
threshold: number; // Shares needed to reconstruct
totalShares: number; // Total shares created
encryptedShares: {
nodeID: string; // Node storing this share
ipfsCID: string; // Content identifier
encryptedData: string;
verificationHash: string;
}[];
}
Share Resolution
Vaults retrieve MPC shares dynamically:
func (v *Vault) resolveMPCShares(ctx context.Context, shareID string) (*MPCShares, error) {
// Get share metadata
metadata, err := v.ipfs.Get(shareID)
if err != nil {
return nil, err
}
var shares MPCShareStorage
json.Unmarshal(metadata, &shares)
// Collect threshold shares
collected := []EncryptedShare{}
for _, share := range shares.EncryptedShares {
// Verify and retrieve share
if exists, _ := v.ipfs.Exists(share.IPFSCID); exists {
data, _ := v.ipfs.Get(share.IPFSCID)
collected = append(collected, EncryptedShare{
NodeID: share.NodeID,
Data: data,
})
}
// Stop when threshold reached
if len(collected) >= shares.Threshold {
break
}
}
return &MPCShares{
ShareID: shareID,
Shares: collected,
}, nil
}
Vault Ejection
Blockchain Independence
Vaults can eject from the blockchain while maintaining functionality:
class EjectableVault {
async ejectFromBlockchain(): Promise<EjectedVault> {
// 1. Export state to IPFS
const stateCID = await this.ipfs.add(await this.exportState());
// 2. Verify MPC shares available
await this.verifyMPCShareAvailability();
// 3. Create ejected configuration
const ejectedConfig = {
type: "ejected",
ipfsConfig: {
network: "sonr-private",
stateCID: stateCID,
mpcShareID: this.mpcShares.shareID,
},
capabilities: {
payments: true,
crossChain: true,
storage: true,
computation: true,
},
blockchainConnection: null,
};
// 4. Store ejection manifest
const manifestCID = await this.ipfs.add(ejectedConfig);
return new EjectedVault(manifestCID, ejectedConfig);
}
}
Ejected Capabilities
Ejected Vaults maintain full operations:
class EjectedVault {
// Payments via cached channels
async processPayment(payment: PaymentRequest) {
const channels = await this.loadPaymentChannels();
return this.executePaymentViaChannel(payment, channels);
}
// Cross-chain via cached IBC config
async crossChainTransfer(transfer: IBCTransfer) {
const ibcConfig = await this.loadIBCConfiguration();
return this.executeIBCTransfer(transfer, ibcConfig);
}
// Storage continues via IPFS
async storeData(data: any): Promise<string> {
return this.ipfs.add(JSON.stringify(data));
}
// Local WASM computation
async executeComputation(wasmModule: Uint8Array, input: any) {
const instance = await WebAssembly.instantiate(wasmModule);
return instance.exports.compute(input);
}
}
Node Bootstrap
Automatic Network Join
Every node joins IPFS during initialization:
// snrd node bootstrap
func (n *SnrdNode) Bootstrap(ctx context.Context) error {
// Initialize blockchain
if err := n.initBlockchainNode(); err != nil {
return err
}
// Join private IPFS
ipfsNode, err := n.initIPFSNode()
if err != nil {
return err
}
// Connect to bootstrap peers
for _, peer := range n.config.IPFSBootstrapPeers {
ipfsNode.Connect(ctx, peer)
}
// Register as MPC storage node
return n.registerMPCStorageNode()
}
// hway node bootstrap
func (h *HwayNode) Bootstrap(ctx context.Context) error {
// Initialize HTTP server
if err := h.initHTTPServer(); err != nil {
return err
}
// Join IPFS for caching
if err := h.initIPFSNode(); err != nil {
return err
}
return h.initVaultCache()
}
// motr node bootstrap
func (m *MotrNode) Bootstrap(ctx context.Context) error {
// Initialize WASM runtime
if err := m.initWASMRuntime(); err != nil {
return err
}
// Join IPFS for storage
if err := m.initIPFSNode(); err != nil {
return err
}
return m.registerMPCComputeNode()
}
Data Management
Content Addressing
IPFS provides automatic deduplication:
func StoreVaultData(data []byte, ipfs ipfs.Client) (string, error) {
// Content-addressed storage
cid, err := ipfs.Add(data)
if err != nil {
return "", err
}
// Pin important data
if isImportantData(data) {
ipfs.Pin(cid, generatePinName(data))
}
return cid, nil
}
func RetrieveVaultData(cid string, ipfs ipfs.Client) ([]byte, error) {
// Check local availability
if exists, _ := ipfs.Exists(cid); !exists {
return nil, fmt.Errorf("data not available: %s", cid)
}
return ipfs.Get(cid)
}
Vault Configuration Storage
Configurations are versioned and stored:
interface VaultConfigIPFS {
version: string;
created: number;
updated: number;
owner: string; // DID
mpcConfig: {
threshold: number;
shareNodes: string[];
};
capabilities: {
payments: boolean;
crossChain: boolean;
storage: boolean;
computation: boolean;
};
}
async function storeVaultConfig(
config: VaultConfigIPFS,
ipfs: IPFSClient,
): Promise<string> {
// Add metadata
const configWithMeta = {
...config,
updated: Date.now(),
version: generateVersion(config),
};
// Store in IPFS
const cid = await ipfs.add(JSON.stringify(configWithMeta));
// Pin for persistence
await ipfs.pin(cid, `vault-config-${config.owner}`);
// Update mutable reference
await ipfs.name.publish(cid, { key: `vault-${config.owner}` });
return cid;
}
Security Model
Network Access Control
Private network implements multiple security layers:
# Swarm key for isolation
/key/swarm/psk/1.0.0/
/base16/
[network_key_hex]
# Peer authentication
IPFS_AUTH_METHOD="signature"
IPFS_PEER_ALLOWLIST="/path/to/allowed_peers.json"
# TLS encryption
LIBP2P_TLS=true
LIBP2P_NOISE=true
Data Encryption
Sensitive data encrypts before storage:
class SecureIPFSStorage {
async storeEncrypted(data: any, policy: AccessPolicy): Promise<string> {
// Serialize data
const serialized = JSON.stringify(data);
// Encrypt with Vault key
const encrypted = await this.encrypt(serialized);
// Create metadata
const metadata = {
encrypted: true,
accessPolicy: policy,
timestamp: Date.now(),
algorithm: "AES-256-GCM",
};
// Combine and store
const package = {
metadata: metadata,
data: encrypted,
};
return this.ipfs.add(JSON.stringify(package));
}
async retrieveDecrypted(cid: string, requester: string): Promise<any> {
// Get from IPFS
const packageData = await this.ipfs.get(cid);
const package = JSON.parse(packageData);
// Check access
if (!this.checkAccess(package.metadata.accessPolicy, requester)) {
throw new Error("Access denied");
}
// Decrypt and return
const decrypted = await this.decrypt(package.data);
return JSON.parse(decrypted);
}
}
Performance Optimization
Caching Strategies
Different nodes optimize for their workloads:
Node Type | Cache Focus | Strategy |
---|---|---|
snrd | MPC shares, Vault configs | LRU cache |
hway | Static assets, Sessions | CDN + Redis |
motr | WASM modules, Compute results | File + LRU |
Intelligent Prefetching
Prefetch related content for performance:
class IPFSPrefetcher {
async prefetchVaultDependencies(vaultCID: string): Promise<void> {
const config = await this.ipfs.get(vaultCID);
const vault = JSON.parse(config);
// Prefetch MPC shares
if (vault.mpcConfig) {
vault.mpcConfig.shareNodes.forEach((shareID) => {
this.ipfs.get(shareID).catch(() => {}); // Background
});
}
// Prefetch pinned content
if (vault.ipfsConfig?.pinned) {
vault.ipfsConfig.pinned.forEach((cid) => {
this.ipfs.get(cid).catch(() => {}); // Background
});
}
}
}
Developer Integration
SDK Usage
Simple SDK for IPFS operations:
import { SonrIPFS } from "@sonr/ipfs-sdk";
// Connect to private network
const ipfs = new SonrIPFS({
network: "private",
authentication: {
did: "did:sonr:developer",
signature: "signed_token",
},
});
// Store with encryption
const cid = await ipfs.vault.store({
data: { user: "alice", balance: 1000 },
encryption: true,
replication: 3,
});
// Retrieve and decrypt
const data = await ipfs.vault.retrieve(cid, {
requester: "did:sonr:alice",
decrypt: true,
});
// Check MPC shares
const status = await ipfs.mpc.checkShares("share_id");
console.log(`Available: ${status.available}/${status.total}`);
Monitoring
Network Health
Track IPFS network metrics:
interface IPFSMetrics {
connectedPeers: number;
totalStorage: number;
replicationHealth: {
underReplicated: string[];
overReplicated: string[];
};
performance: {
avgRetrieval: number; // ms
avgStorage: number; // ms
bandwidth: number; // bytes/sec
};
}
class IPFSMonitor {
async collectMetrics(): Promise<IPFSMetrics> {
const peers = await this.ipfs.swarm.peers();
const stats = await this.ipfs.stats.repo();
return {
connectedPeers: peers.length,
totalStorage: stats.size,
replicationHealth: await this.checkReplication(),
performance: await this.measurePerformance(),
};
}
}
Best Practices
For Operators
- Monitor replication: Ensure adequate share distribution
- Manage storage: Set appropriate garbage collection policies
- Secure bootstrap: Protect bootstrap node access
- Update peers: Maintain peer allowlist
- Monitor bandwidth: Track network usage
For Developers
- Encrypt sensitive data: Always encrypt before storage
- Pin important data: Prevent garbage collection
- Use content addressing: Leverage deduplication
- Implement caching: Reduce network requests
- Handle failures: Design for network partitions
Next Steps
Ready to Build?
Operators: Read the Node Setup Guide for IPFS configuration.
Developers: Explore the IPFS SDK Reference for integration.
Architects: Review MPC Architecture for share management.
Learn how IPFS integrates with other Sonr components by exploring the DWN Module for Vault storage or Identity System for access control.