Anonymous, censor-resistant news powered by World ID, Lit Protocol, and IPFS.
This is a comprehensive description of the Anonymous News Forum project, outlining its core mission and complex technical architecture.
Anonymous News Forum: Project Description The Anonymous News Forum is a decentralized application (dApp) engineered to provide a platform for citizen journalism that prioritizes anonymity, censorship resistance, and verifiable identity. The core mission is to create a secure, open environment for whistleblowers and truth-tellers to publish sensitive news and evidence without fear of reprisal or platform censorship.
Technical Architecture & Core Principles The dApp is a sophisticated integration of three major Web3 technologies built on the Ethereum Sepolia testnet.
Function: Before a user can submit a post, they must verify they are a unique human being using World ID’s Zero-Knowledge Proof (ZKP) system.
Outcome: This critical step prevents spam and Sybil attacks (where one person creates multiple fake accounts to manipulate the forum) without ever requiring the user to reveal their real-world identity or wallet address to the smart contract. The smart contract validates the cryptographic proof on-chain, ensuring one unique person corresponds to one anonymous ID (the nullifier hash).
Function: All news content (the text of the story and any associated evidence files, like images) is uploaded to Lighthouse, which pins the data to the InterPlanetary File System (IPFS).
Outcome: This guarantees censorship resistance. Once published, the data is decentralized and permanent, making it impossible for any single authority, company, or government to take down the news story or its supporting evidence.
Function: Sensitive evidence files (e.g., photos or internal documents) are encrypted locally on the user's device before being uploaded to IPFS. The encryption key is secured by the Lit Protocol network, which acts as a decentralized gatekeeper.
Outcome: Access to decrypt the evidence can be protected by on-chain conditions (e.g., the viewer must hold a minimum amount of ETH). This ensures that while the content is permanently stored, only authorized or trusted community members who meet specific criteria can view the sensitive proof, adding a vital layer of security and controlled disclosure.
Frontend and Smart Contract The Frontend is built with React and the Vite build tool, providing a fast, modern user interface with real-time updates of the news feed.
The core Smart Contract (ForumWorldID.sol) is written in Solidity and deployed on Sepolia. It stores only the IPFS hash linking to the content and the user's anonymous nullifier hash, ensuring no personally identifiable information (PII) is ever recorded on the public blockchain.
The project successfully integrates these complex Web3 mechanisms to deliver on its promise of a truly secure, anonymous, and verifiable platform for digital journalism.
How It's Pieced Together
User Flow: The App.jsx component manages the entire state, including post text, the selected evidence image (postImage), and the uploading status (isUploading).
The World ID Gateway: The <IDKitWidget> triggers the core publication workflow. It is configured to use the VerificationLevel.Device (for easy staging) and a specific action name (e.g., "anonymous-news-forum15"), which is matched precisely by the deployed smart contract.
Real-Time Updates: The app connects to the Sepolia testnet via a WebSocketProvider (using Alchemy). It sets up an on("PostPublished", ...) listener on the smart contract. Whenever a post is successfully mined on-chain, the frontend instantly receives the event and updates the post feed without requiring a page refresh.
Encryption (Lit Protocol):
The browser first contacts the Lit Protocol network to establish an authorized signature (authSig).
The evidence file (postImage) is then encrypted using LitJsSdk.encryptFile(), generating an encrypted file blob and a symmetric key.
The symmetric key is then encrypted based on a defined access control condition (e.g., requiring the user's wallet to hold a minimum amount of ETH). This encrypted key is secured by the Lit network using litNodeClient.saveEncryptionKey().
Decentralized Storage (Lighthouse/IPFS):
The encrypted evidence blob and the post metadata (text + encrypted key details) are uploaded to Lighthouse, which pins them to IPFS. Lighthouse returns an IPFS hash for the post metadata.
On-Chain Verification (World ID/Solidity):
The application calls the smart contract's publishPost function, passing the IPFS metadata hash and the World ID proof data (merkle root, nullifier hash, and proof array).
The contract's first action is to call the World ID router to verify the ZKP. If the proof is valid and the nullifier has not been used for this action, the transaction proceeds.
The contract then records the IPFS hash and the anonymous author ID (the nullifier hash) on the blockchain, and emits a PostPublished event, which the frontend detects.
Notable Hacks and Challenges The project required overcoming several deep technical hurdles:
The Dependency War: The initial build failed repeatedly due to cascading conflicts between ethers.js (v6), Lit Protocol (v2), and older polyfills. This was ultimately solved by using a specific, stable version of Lit Protocol (2.2.39) and using the modern vite-plugin-node-polyfills to correctly inject Node.js globals (Buffer, process) needed by the Web3 libraries into the browser environment.
The Final Deployment Fix (The Nitty-Gritty Hack): The most subtle and difficult bug involved the smart contract deployment. The official documentation required a specific, complex calculation for the App ID and Action ID. The solution involved correctly implementing BigInt(ethers.id(string)) >> 8n for both IDs in the Hardhat deploy script, ensuring the contract was deployed with the mathematically correct IDs that the World ID network expected. Every previous deployment failed because of an incorrect calculation here.
Decryption Control Flow: A necessary architectural hack was wrapping the Lit Protocol initialization in a manual window.onload check. This was needed to prevent a subtle race condition where the React useEffect hook would fire and try to initialize the Lit client before the polyfills injected by Vite were fully loaded, causing an instant crash (errConstructorFunc). Waiting for the entire window load guarantees stability.

