JavaScript/TypeScript client library for the Arweave decentralized permanent data storage network
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Silo protocol implementation for encrypted data storage with URI-based access control. Enables secure, encrypted data storage on Arweave with controlled access through cryptographic keys.
Create an encrypted transaction using the Silo protocol.
/**
* Create encrypted Silo transaction
* @param attributes - Transaction attributes (must include data)
* @param jwk - Wallet key for signing
* @param siloUri - Silo URI containing encryption parameters
* @returns Promise resolving to encrypted Transaction
*/
createSiloTransaction(
attributes: Partial<CreateTransactionInterface>,
jwk: JWKInterface,
siloUri: string
): Promise<Transaction>;Usage Example:
import Arweave from "arweave";
const arweave = Arweave.init();
const key = await arweave.wallets.generate();
// Create Silo URI (in practice, this would come from a Silo provider)
const siloUri = "silo://access-key@encryption-key";
// Create encrypted transaction
const secretData = "This is confidential information";
const siloTransaction = await arweave.createSiloTransaction({
data: secretData,
tags: [
{ name: "Content-Type", value: "text/plain" },
{ name: "Encrypted", value: "true" }
]
}, key, siloUri);
await arweave.transactions.sign(siloTransaction, key);
await arweave.transactions.post(siloTransaction);Retrieve and decrypt data from a Silo URI.
/**
* Retrieve and decrypt data from Silo URI
* @param siloURI - Silo URI containing access and encryption keys
* @returns Promise resolving to decrypted data
*/
get(siloURI: string): Promise<Uint8Array>;Usage Example:
// Retrieve encrypted data using the same Silo URI
const siloUri = "silo://access-key@encryption-key";
const decryptedData = await arweave.silo.get(siloUri);
// Convert to string if it's text data
const textData = Arweave.utils.bufferToString(decryptedData);
console.log("Decrypted data:", textData);Decrypt Silo transaction data using the Silo URI.
/**
* Decrypt Silo transaction data
* @param transaction - Transaction containing encrypted Silo data
* @param siloURI - Silo URI with decryption keys
* @returns Promise resolving to decrypted data
*/
readTransactionData(transaction: Transaction, siloURI: string): Promise<Uint8Array>;Usage Example:
// Get Silo transaction and decrypt its data
const transactionId = "silo-transaction-id";
const transaction = await arweave.transactions.get(transactionId);
const siloUri = "silo://access-key@encryption-key";
const decryptedData = await arweave.silo.readTransactionData(transaction, siloUri);
const textData = Arweave.utils.bufferToString(decryptedData);
console.log("Decrypted transaction data:", textData);Parse a Silo URI to extract access and encryption keys.
/**
* Parse Silo URI into access keys
* @param siloURI - Silo URI to parse
* @returns Promise resolving to SiloResource
*/
parseUri(siloURI: string): Promise<SiloResource>;Usage Example:
const siloUri = "silo://my-access-key@my-encryption-key";
const siloResource = await arweave.silo.parseUri(siloUri);
console.log("Access key:", siloResource.getAccessKey());
console.log("URI:", siloResource.getUri());
console.log("Encryption key length:", siloResource.getEncryptionKey().length);Represents parsed Silo URI with access to encryption parameters.
class SiloResource {
/**
* Get the original Silo URI
* @returns Silo URI string
*/
getUri(): string;
/**
* Get the access key for Silo identification
* @returns Access key string
*/
getAccessKey(): string;
/**
* Get the encryption key for data encryption/decryption
* @returns Encryption key as Uint8Array
*/
getEncryptionKey(): Uint8Array;
}// Store encrypted document with metadata
async function storeSecureDocument(
document: string,
metadata: { title: string; author: string; date: string },
key: JWKInterface,
siloUri: string
): Promise<string> {
// Combine document and metadata
const documentData = {
content: document,
metadata: metadata,
timestamp: Date.now()
};
const jsonData = JSON.stringify(documentData);
// Create encrypted Silo transaction
const transaction = await arweave.createSiloTransaction({
data: jsonData,
tags: [
{ name: "Content-Type", value: "application/json" },
{ name: "Document-Type", value: "secure-document" },
{ name: "Author", value: metadata.author }
]
}, key, siloUri);
await arweave.transactions.sign(transaction, key);
await arweave.transactions.post(transaction);
return transaction.id;
}
// Usage
const key = await arweave.wallets.generate();
const siloUri = "silo://doc-access@doc-encryption-key";
const documentId = await storeSecureDocument(
"This is a confidential document",
{ title: "Secret Report", author: "John Doe", date: "2024-01-01" },
key,
siloUri
);// Share encrypted file with specific access controls
async function shareEncryptedFile(
fileData: Uint8Array,
fileName: string,
fileType: string,
key: JWKInterface
): Promise<{ transactionId: string; siloUri: string }> {
// Generate unique encryption keys for this file
const accessKey = generateRandomString(32);
const encryptionKey = generateRandomString(64);
const siloUri = `silo://${accessKey}@${encryptionKey}`;
// Create encrypted transaction
const transaction = await arweave.createSiloTransaction({
data: fileData,
tags: [
{ name: "Content-Type", value: fileType },
{ name: "File-Name", value: fileName },
{ name: "Encrypted", value: "true" }
]
}, key, siloUri);
await arweave.transactions.sign(transaction, key);
await arweave.transactions.post(transaction);
return {
transactionId: transaction.id,
siloUri: siloUri
};
}
// Retrieve shared encrypted file
async function retrieveSharedFile(siloUri: string): Promise<{
data: Uint8Array;
fileName: string;
fileType: string;
}> {
// Decrypt the data
const decryptedData = await arweave.silo.get(siloUri);
// Parse Silo URI to get access key
const siloResource = await arweave.silo.parseUri(siloUri);
const accessKey = siloResource.getAccessKey();
// Find transaction by Silo access key
// (In practice, you'd store the transaction ID with the Silo URI)
return {
data: decryptedData,
fileName: "retrieved-file",
fileType: "application/octet-stream"
};
}// Create Silo with multiple access keys for team sharing
class TeamSilo {
private baseUri: string;
private teamMembers: Map<string, string> = new Map();
constructor(baseAccessKey: string, baseEncryptionKey: string) {
this.baseUri = `silo://${baseAccessKey}@${baseEncryptionKey}`;
}
// Add team member with their own access key
addMember(memberId: string, memberAccessKey: string) {
this.teamMembers.set(memberId, memberAccessKey);
}
// Create Silo URI for specific member
getSiloUri(memberId: string): string {
const memberAccessKey = this.teamMembers.get(memberId);
if (!memberAccessKey) {
throw new Error(`Member ${memberId} not found`);
}
const siloResource = parseManualSiloUri(this.baseUri);
return `silo://${memberAccessKey}@${siloResource.encryptionKey}`;
}
// Store data accessible to all team members
async storeTeamData(
data: string,
key: JWKInterface,
metadata: { type: string; title: string }
): Promise<string> {
const transaction = await arweave.createSiloTransaction({
data: data,
tags: [
{ name: "Content-Type", value: "text/plain" },
{ name: "Team-Resource", value: "true" },
{ name: "Resource-Type", value: metadata.type },
{ name: "Title", value: metadata.title }
]
}, key, this.baseUri);
await arweave.transactions.sign(transaction, key);
await arweave.transactions.post(transaction);
return transaction.id;
}
}
// Usage
const teamSilo = new TeamSilo("team-access-key", "team-encryption-key");
teamSilo.addMember("alice", "alice-access-key");
teamSilo.addMember("bob", "bob-access-key");
// Alice stores data
const aliceKey = await arweave.wallets.generate();
const dataId = await teamSilo.storeTeamData(
"Team meeting notes",
aliceKey,
{ type: "notes", title: "Weekly Standup" }
);
// Bob retrieves data using his access
const bobSiloUri = teamSilo.getSiloUri("bob");
const decryptedNotes = await arweave.silo.get(bobSiloUri);// ✅ Good: Generate cryptographically secure keys
function generateSecureSiloKeys(): { accessKey: string; encryptionKey: string } {
const accessKey = crypto.getRandomValues(new Uint8Array(32));
const encryptionKey = crypto.getRandomValues(new Uint8Array(64));
return {
accessKey: Arweave.utils.bufferTob64Url(accessKey),
encryptionKey: Arweave.utils.bufferTob64Url(encryptionKey)
};
}
// ❌ Bad: Predictable or weak keys
const weakKeys = {
accessKey: "simple-key",
encryptionKey: "another-simple-key"
};// ✅ Good: Validate Silo URI format
function validateSiloUri(uri: string): boolean {
const siloPattern = /^silo:\/\/[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+$/;
return siloPattern.test(uri);
}
// ✅ Good: Implement access logging
async function auditedSiloAccess(siloUri: string): Promise<Uint8Array> {
console.log(`Accessing Silo at ${Date.now()}: ${siloUri.substring(0, 20)}...`);
try {
const data = await arweave.silo.get(siloUri);
console.log(`Successfully retrieved ${data.length} bytes`);
return data;
} catch (error) {
console.error(`Failed to access Silo: ${error.message}`);
throw error;
}
}/** Silo URI format: silo://access-key@encryption-key */
type SiloUri = string;/** Silo protocol version identifier */
const SILO_VERSION: string = "0.1.0";
/** Default Silo URI scheme */
const SILO_SCHEME: string = "silo://";// Utility functions for working with Silo URIs
function generateRandomString(length: number): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
const randomBytes = crypto.getRandomValues(new Uint8Array(length));
return Array.from(randomBytes)
.map(byte => chars[byte % chars.length])
.join('');
}
function parseManualSiloUri(uri: string): { accessKey: string; encryptionKey: string } {
const match = uri.match(/^silo:\/\/([^@]+)@(.+)$/);
if (!match) {
throw new Error("Invalid Silo URI format");
}
return {
accessKey: match[1],
encryptionKey: match[2]
};
}