A blog rendered entirely from a VerusID's contentmultimap. No database, no CMS — just on-chain data. Look up any identity to read their blog.
Select a post below and publish it to joe.bitcoins@'s contentmultimap. The post will be stored on-chain using the vrsc::identity.blog.post VDXF key and will appear in the blog above once confirmed (~1 min).
This page reads a VerusID's contentmultimap and renders any blog content it finds. The data lives entirely on the Verus blockchain — no server, no database. The page is a static HTML file that makes one RPC call to getidentity and displays the result.
Anyone with a VerusID can publish a blog by writing JSON entries to their contentmultimap. The page picks them up automatically.
Blog data is stored under standard VDXF keys so any app in the ecosystem can recognize the content. Keys follow the vrsc::identity.blog.* namespace.
Each key resolves to a deterministic i-address via getvdxfid. These addresses are the same on every node — they're derived from the key name, not generated randomly.
// Blog VDXF keys — generated via: ./verus getvdxfid "keyname" // Content types vrsc::identity.blog.profile iPoVhbEjPCPXLE6gL3R1WEtpcUHfzVBtH1 vrsc::identity.blog.post i7uLoCqnFbjJPRq1W2z3qS1P1qSkVysZyW // Profile fields vrsc::identity.blog.displayname iBkGDSZ4ew1hKEDJJL76VyRSwZcSVaGEEA vrsc::identity.blog.bio iE3Y58uCrc1posw2AydaAeDNjkDAcchyuM vrsc::identity.blog.avatar iBsvP71qRr5cKBWCzfZVUArbUFZoMdB4fu vrsc::identity.blog.links iL11UnHkRi9pYMVw2ShjiqHKXxtc4EwtNp // Post fields vrsc::identity.blog.post.title iN1bnLFLmVowWmPsdLwWDKDhAaEAkkxVNh vrsc::identity.blog.post.content iLv3Ke5YvBiitYX5aDcFD79EZYWBqf6ssj vrsc::identity.blog.post.date iNjVt8AWTGnoRjy74QvfSWYAX2iBPEVK1f vrsc::identity.blog.post.tags i4Lt349A8Rnny6DUU5cAm42DGN3bBMm2dT
The naming convention follows the existing vrsc::identity.* vocabulary from verus-typescript-primitives. The blog namespace groups all blog-related keys, with sub-namespaces for profile and post fields.
Why this structure? Any app that understands the vrsc::identity.blog.* namespace can read and display blog content from any VerusID. You don't need to know how the data was written — just look for the standard keys. This is how interoperability works without smart contracts.
Each entry in the contentmultimap is a hex-encoded JSON object with a type field. The page recognizes two types:
// Profile — author info (one per identity) { "type": "profile", "displayName": "Your Name", // max 50 chars "bio": "A short bio", // max 280 chars "avatar": "https://...pic.jpg", // https only "links": { "github": "username", // alphanumeric only "twitter": "handle", // alphanumeric only "website": "https://yoursite.com", // https only "discord": "https://discord.gg/..." // https only } } // Post — a blog entry (multiple allowed, max 50) { "type": "post", "title": "Post title", // max 150 chars "content": "Plain text body...", // max 4000 chars "date": 1711123200, // unix timestamp "tags": ["verus", "dev"] // max 8 tags, 30 chars each }
Only these fields are read. Everything else is silently ignored. Content is rendered as plain text — no HTML, no scripts, no embeds.
You need a VerusID and access to a Verus node (local CLI or Verus Desktop). Each entry must be hex-encoded JSON stored under the blog VDXF key.
# 1. Get the blog VDXF key (same on every node) ./verus getvdxfid "vrsc::identity.blog.post" # Returns: i7uLoCqnFbjJPRq1W2z3qS1P1qSkVysZyW # 2. Create your JSON content as hex PROFILE=$(echo -n '{"type":"profile","displayName":"Alice","bio":"Building on Verus."}' | xxd -p | tr -d '\n') POST=$(echo -n '{"type":"post","title":"My First Post","content":"Hello from the chain!","date":1711123200,"tags":["verus"]}' | xxd -p | tr -d '\n') # 3. Push to your identity under the blog key ./verus updateidentity '{ "name": "yourname", "parent": "iParentIdHere", "contentmultimap": { "i7uLoCqnFbjJPRq1W2z3qS1P1qSkVysZyW": [ "'$PROFILE'", "'$POST'" ] } }' # 4. Done — load your ID on this page to see it
To add more posts, fetch your current contentmultimap with getidentity, append the new post to the array, and call updateidentity again with the full array.
This page is a read-only renderer. All data from the chain passes through three layers of protection:
Schema validation — Only recognized fields with the type field set to "profile" or "post" are extracted. Unknown types and fields are dropped. Strings are length-capped. URLs must be https://. GitHub and Twitter handles are stripped to alphanumeric characters only.
Output escaping — All text goes through escapeHtml() before rendering. Content is displayed as plain text with line breaks. No HTML, no markdown rendering, no script execution from user data.
Content Security Policy — The page sets a strict CSP header: scripts can only run from the page itself, network requests only go to api.verus.services, images only load over HTTPS. Even if the other layers fail, the browser blocks anything unexpected.