Advanced Topics
This section covers advanced concepts and best practices for building robust, scalable, and secure applications with the Lens SDK. It is intended for developers who are already familiar with the topics covered in the Quick Start Guide and Core Concepts.
1. Using a Wallet for User Identity
The most powerful feature for building dApps with the Lens SDK is the ability to use an external wallet (like MetaMask) as the user’s identity. This allows users to sign for all actions directly, ensuring they are the true owners of their content.
To do this, you need to create a Peerbit-compatible Identity
object from an ethers.js
signer.
Step 1: Add Dependencies
You will need ethers
to interact with browser wallets.
pnpm install ethers
Step 2: Create the Identity from a Signer
The SDK provides a utility function, createIdentityFromSigner
, to make this easy. In a browser application, you would get the signer from the window.ethereum
provider.
import { ethers } from 'ethers';
import { LensService, createIdentityFromSigner } from '@riffcc/lens-sdk';
async function setupWithWallet() {
// In a browser environment:
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// Create a Peerbit-compatible identity from the ethers signer.
const walletIdentity = await createIdentityFromSigner(signer);
// Instantiate the LensService, passing the custom identity.
const lens = new LensService({
identity: walletIdentity,
debug: true,
});
// Now, all calls like `lens.addRelease()` will trigger a signature
// request in the user's wallet.
await lens.init();
// ... rest of your application logic
}
Step 3: Creating a Site with the Wallet Identity
If the user is the first to create a Site
, their wallet’s public key must be used as the root administrator. You can pass the walletIdentity.publicKey
directly to the Site
constructor.
import { Site } from '@riffcc/lens-sdk/programs';
// ... assuming lens and walletIdentity are set up from the previous step
// The user's wallet public key becomes the root of trust for the new Site.
const rootAdminKey = walletIdentity.publicKey;
const newSite = new Site(rootAdminKey);
// Open the site. The user's wallet will sign for this action.
await lens.openSite(newSite);
console.log(`Site created successfully! Address: ${lens.siteProgram.address}`);
This pattern is fundamental for creating non-custodial applications where users have full control.
2. Managing Peerbit Client Externally
In some complex applications, you may need to manage the lifecycle of the Peerbit P2P client yourself, especially if your application uses other Peerbit programs alongside the Site
program.
The LensService
constructor accepts an optional peerbit
client instance. When a client is provided this way, the service will not attempt to manage its lifecycle.
Use Case: Your application needs to run a Site
program and a separate custom Chat
program on the same Peerbit node.
import { Peerbit } from 'peerbit';
import { LensService } from '@riffcc/lens-sdk';
import { ChatProgram } from './my-chat-program'; // Your custom program
async function setup() {
// 1. Create and manage the Peerbit client yourself.
const peerbit = await Peerbit.create({ directory: './shared-p2p-node' });
// 2. Pass the external client to the LensService.
const lens = new LensService({ peerbit: peerbit, debug: true });
// 3. You can now open a Site...
await lens.openSite('EXISTING_SITE_ADDRESS');
// 4. ...and also open your other custom programs on the same node.
const chat = await peerbit.open(new ChatProgram());
// ... application logic ...
// 5. You are responsible for stopping the client.
// The lens.stop() call will NOT stop the external client.
await lens.stop(); // Only stops the Site program and FederationManager
await peerbit.stop();
}
3. Customizing Replication (SiteArgs
)
By default, a Site
program attempts to replicate its data stores with a low replication factor to save resources. For applications requiring higher availability or performance, you can provide custom replication arguments when opening a site.
The SiteArgs
object allows you to specify replicate
options for each data store within the Site
.
Use Case: You are running a dedicated “pinning” node that should store a complete copy of all data for maximum availability.
import { Site } from '@riffcc/lens-sdk/programs';
const dedicatedPinningNodeArgs = {
// Setting 'replicate: true' means "replicate everything you can find."
releasesArgs: { replicate: true },
featuredReleasesArgs: { replicate: true },
contentCategoriesArgs: { replicate: true },
// ACLs and Subscriptions should always be fully replicated on an admin node.
membersArg: { replicate: true },
administratorsArgs: { replicate: true },
subscriptionsArgs: { replicate: true }
};
const site = new Site(myPublicKey);
await lens.openSite(site, { siteArgs: dedicatedPinningNodeArgs });
For detailed replication options, refer to the Peerbit documentation on ReplicationOptions
.
4. Understanding Federation Performance
The FederationManager
is designed to be efficient, but in large-scale networks, it’s helpful to understand its behavior:
- Historical Sync: This is the most resource-intensive part of federation. When a site with thousands of releases is subscribed to, the initial sync can take time and consume bandwidth. This process runs in the background and does not block other operations.
- Live Sync: Live updates via pub/sub are extremely lightweight. A
FederationUpdate
message only contains the cryptographic hashes and metadata of changed entries, not the full data, making real-time updates very fast. - Network Topology: Federation performance is dependent on the underlying libp2p network. For optimal performance, ensure that nodes (especially those that frequently federate with each other) are well-connected. You can use
peerbit.dial()
to manually establish connections between nodes if needed.
5. Production-Level Content Moderation (BlockedContent
and Denylists)
The Lens SDK is designed with a powerful content moderation architecture that extends beyond simple in-app filtering to the core infrastructure. This section outlines the full vision, separating what is currently available from features that are in progress or planned for the future.
Logical Deletes vs. Hard Deletes
It is important to distinguish between the two forms of content removal:
deleteRelease()
(Logical Delete): This is a standard feature. When an administrator callsdeleteRelease()
, it removes theRelease
document from the site’s database. This is a “soft” or “logical” delete within the application’s view. The underlying data file on IPFS is not immediately affected.BlockedContent
(Hard Delete Foundation): This schema is the foundation for a “hard delete” process. It is designed to create a permanent, verifiable record that a piece of content (identified by its CID) should be purged not just from the app, but from the entire storage infrastructure.
The “Bad Bits” Moderation Pattern: Current State and Future Vision
The recommended pattern for robust moderation follows the principles of established denylist formats like the “Bad Bits” denylist.
Here is the workflow, including the status of each component:
Administrator Action: An administrator identifies a piece of content by its IPFS Content Identifier (CID) that needs to be blocked.
Double Hashing and Record Creation (In Progress):
- Functionality: To protect privacy, the original CID is double-hashed (e.g., SHA256(SHA256(CID))) before being stored. This prevents casual observers from identifying the content on the blocklist.
- Status: The high-level
LensService
methods to perform this action (e.g.,blockContent()
) are currently in progress. This will provide a simple, secure way for administrators to add entries to theblockedContent
store without needing direct program interaction.
Infrastructure Synchronization (Future Implementation):
- Vision: The long-term vision is for a trusted backend service or “Operator” to monitor the
Site
’sblockedContent
store. This Operator will generate a standard denylist file from the stored hashes and distribute it to all core infrastructure. - Status: The implementation of this Operator service is planned for the future. When complete, it will enable the
blockedContent
store to act as a decentralized source of truth that can instruct IPFS nodes, clusters, and CDNs to refuse to serve and permanently delete blocked content.
- Vision: The long-term vision is for a trusted backend service or “Operator” to monitor the
Summary of Moderation Features
Feature | Status | Description |
---|---|---|
Logical Deletion (deleteRelease ) | ✅ Implemented | Removes content metadata from the Site ’s database. |
Blocklist Schema (BlockedContent ) | ✅ Implemented | The on-chain data structure for recording moderation decisions exists. |
Service API for Blocking (blockContent ) | ⏳ In Progress | High-level LensService methods for easy and secure management of the blockedContent store. |
Operator for Infrastructure Sync | 🗺️ Future | A service to automate the syncing of the on-chain blocklist to IPFS nodes, clusters, and CDNs. |
This roadmap provides a clear path from the current capabilities to a comprehensive, end-to-end content moderation system that is both powerful and privacy-preserving.
6. Direct Program Interaction (For Advanced Use Cases)
While the LensService
should be used for 99% of interactions, there may be rare cases where direct interaction with the Site
program is necessary (e.g., in server-side administrative scripts).
The active Site
program instance is available via lensService.siteProgram
.
Use Case: A script needs to inspect the low-level replication state of a specific database.
// This is NOT a typical application pattern. Use with caution.
const site: Site | null = lens.siteProgram;
if (site) {
// Accessing the underlying log of a store
const releaseLog = site.releases.log;
// Get the cryptographic heads of the log
const heads = await releaseLog.log.getHeads().all();
console.log('Current release log heads:', heads.map(h => h.hash));
// Directly interacting with ACLs
await site._authorise(AccountType.MEMBER, 'some_public_key');
}
Warning: Bypassing the LensService
means you lose its safety checks, error handling, and stable API contract. This should only be done when you have a deep understanding of the Peerbit framework and the internal logic of the Site
program.