NOVA SDK for JavaScript
A JavaScript/TypeScript SDK for interacting with the NOVA secure file-sharing on the NEAR blockchain. Provides encrypted, decentralized file storage using IPFS and NEAR smart contracts with group-based access control.
Features
🔐 AES-256-CBC Encryption - Client-side encryption for data privacy
🌐 IPFS Storage - Decentralized file storage via Pinata
⛓️ NEAR Blockchain - Immutable transaction records and access control
👥 Group Management - Fine-grained access control with member authorization
🔑 Key Rotation - Automatic key rotation on member revocation
🚀 Composite Operations - Simplified high-level workflows
📦 TypeScript Support - Full type definitions included
Installation
npm install nova-sdk-js
Quick Start
import { NovaSdk } from 'nova-sdk-js';
async function main() {
// Initialize SDK
const sdk = new NovaSdk(
'https://rpc.testnet.near.org',
'nova-contract.testnet',
'your_pinata_api_key',
'your_pinata_secret_key'
);
// Attach signer
await sdk.withSigner(
'ed25519:your_private_key',
'your-account.testnet'
);
// Upload encrypted file
const data = Buffer.from('Confidential data');
const result = await sdk.compositeUpload(
'project_alpha', // group_id
'alice.testnet', // user_id
data, // data buffer
'report.txt' // filename
);
console.log('✅ Uploaded to IPFS:', result.cid);
console.log('📝 Transaction ID:', result.trans_id);
console.log('🔒 File Hash:', result.file_hash);
// Retrieve and decrypt file
const retrieved = await sdk.compositeRetrieve(
'project_alpha',
result.cid
);
const content = retrieved.data.toString('utf8');
console.log('📄 Content:', content);
}
main().catch(console.error);
Core Concepts
Groups
Groups provide isolated access control domains. Each group has:
A unique identifier (
group_id
)An owner who manages membership
A shared encryption key
A list of authorized members
Access Control
// Register new group (owner only)
await sdk.registerGroup('secure_vault');
// Add members
await sdk.addGroupMember('secure_vault', 'bob.testnet');
// Check authorization
const authorized = await sdk.isAuthorized('secure_vault', 'bob.testnet');
// Revoke member (automatically rotates key)
await sdk.revokeGroupMember('secure_vault', 'bob.testnet');
Encryption
All data is encrypted client-side using AES-256-CBC:
256-bit symmetric keys
Random IV per encryption
PKCS7 padding
Keys stored encrypted on blockchain
Transaction Recording
Every file operation creates an immutable blockchain record:
const transId = await sdk.recordTransaction(
'my_group',
'alice.testnet',
'file_hash_sha256',
'QmIPFSHash'
);
// Query group transactions
const txs = await sdk.getTransactionsForGroup('my_group', 'alice.testnet');
API Overview
Initialization
const sdk = new NovaSdk(
rpcUrl: string,
contractId: string,
pinataKey: string,
pinataSecret: string
);
// Attach signer for write operations
await sdk.withSigner(privateKey: string, accountId: string);
Group Management
// Create new group (requires deposit)
await sdk.registerGroup(groupId: string): Promise<string>
// Grant access to user
await sdk.addGroupMember(groupId: string, userId: string): Promise<string>
// Revoke access and rotate key
await sdk.revokeGroupMember(groupId: string, userId: string): Promise<string>
// Check user authorization
await sdk.isAuthorized(groupId: string, userId: string): Promise<boolean>
Key Management
// Store encryption key (owner only)
await sdk.storeGroupKey(groupId: string, keyB64: string): Promise<string>
// Retrieve key (authorized users only)
await sdk.getGroupKey(groupId: string, userId: string): Promise<string>
File Operations
// Encrypt, upload to IPFS, record transaction
await sdk.compositeUpload(
groupId: string,
userId: string,
data: Buffer,
filename: string
): Promise<CompositeUploadResult>
// Fetch from IPFS and decrypt
await sdk.compositeRetrieve(
groupId: string,
ipfsHash: string
): Promise<CompositeRetrieveResult>
// Record file metadata on blockchain
await sdk.recordTransaction(
groupId: string,
userId: string,
fileHash: string,
ipfsHash: string
): Promise<string>
// Query transaction history
await sdk.getTransactionsForGroup(
groupId: string,
userId: string
): Promise<Transaction[]>
Utilities
// Check NEAR account balance (in yoctoNEAR)
await sdk.getBalance(accountId: string): Promise<string>
// Transfer NEAR tokens
await sdk.transferTokens(toAccount: string, amountYocto: string): Promise<string>
Types
interface Transaction {
group_id: string;
user_id: string;
file_hash: string;
ipfs_hash: string;
}
interface CompositeUploadResult {
cid: string; // IPFS content identifier
trans_id: string; // NEAR transaction ID
file_hash: string; // SHA256 hash (hex)
}
interface CompositeRetrieveResult {
data: Buffer; // Decrypted file data
file_hash: string; // SHA256 hash for verification
}
class NovaError extends Error {
constructor(message: string, cause?: Error);
}
Environment Setup
For testing and development, set these environment variables:
# Required for integration tests
TEST_NEAR_ACCOUNT_ID=your-account.testnet
TEST_NEAR_PRIVATE_KEY=ed25519:your_private_key
PINATA_API_KEY=your_pinata_key
PINATA_SECRET_KEY=your_pinata_secret
Create a .env
file:
TEST_NEAR_ACCOUNT_ID=alice.testnet
TEST_NEAR_PRIVATE_KEY=ed25519:...
PINATA_API_KEY=...
PINATA_SECRET_KEY=...
Testing
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run with coverage
npm run test:coverage
Error Handling
The SDK uses a custom NovaError
class:
import { NovaError } from 'nova-sdk-js';
try {
const key = await sdk.getGroupKey('my_group', 'user.testnet');
console.log('Key:', key);
} catch (error) {
if (error instanceof NovaError) {
console.error('NOVA error:', error.message);
if (error.cause) {
console.error('Caused by:', error.cause);
}
} else {
console.error('Unexpected error:', error);
}
}
Security Considerations
⚠️ Important Security Notes:
Private Keys - Never commit private keys to version control
Key Storage - Store encryption keys securely
IPFS Privacy - IPFS content is public; encryption is essential
Access Control - Verify user authorization before operations
Key Rotation - Revoked members cannot decrypt new content
Buffer Handling - Use Buffer for binary data to avoid encoding issues
NEAR Token Deposits
Some operations require NEAR token deposits:
registerGroup()
- 0.1 NEARaddGroupMember()
- 0.0005 NEARrevokeGroupMember()
- 0.0005 NEARstoreGroupKey()
- 0.0005 NEARrecordTransaction()
- 0.002 NEAR
Ensure your account has sufficient balance before calling these methods.
Examples
Basic File Upload
const sdk = new NovaSdk(rpcUrl, contractId, pinataKey, pinataSecret);
await sdk.withSigner(privateKey, accountId);
const fileData = Buffer.from('My secret document');
const result = await sdk.compositeUpload(
'my_group',
'user.testnet',
fileData,
'secret.txt'
);
console.log('Uploaded:', result.cid);
Group Management
// Create group
await sdk.registerGroup('team_alpha');
// Add team members
await sdk.addGroupMember('team_alpha', 'bob.testnet');
await sdk.addGroupMember('team_alpha', 'carol.testnet');
// Verify access
const bobAuth = await sdk.isAuthorized('team_alpha', 'bob.testnet');
console.log('Bob authorized:', bobAuth);
// Remove member
await sdk.revokeGroupMember('team_alpha', 'bob.testnet');
Transaction History
const transactions = await sdk.getTransactionsForGroup(
'my_group',
'user.testnet'
);
transactions.forEach(tx => {
console.log(`User: ${tx.user_id}`);
console.log(`File: ${tx.file_hash}`);
console.log(`IPFS: ${tx.ipfs_hash}`);
});
Building from Source
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run tests
npm test
Contributing
Contributions are welcome! Please:
Fork the repository
Create a feature branch
Add tests for new functionality
Ensure all tests pass (
npm test
)Submit a pull request
License
This project is licensed under the MIT License - see LICENSE file for details.
Resources
Support
Issues: GitHub Issues
Discussions: GitHub Discussions
Last updated