Hedera HCS in Practice: Building a Verifiable Social App from Scratch

Run through the full Hedera HCS development loop from scratch and break down iBird's single-topic production architecture.
This tutorial walks developers through the complete Hedera Consensus Service loop: claim a free testnet account and HBAR, create a topic and submit messages with @hashgraph/sdk for an immutable consensus timestamp and sequence number in ~3 seconds, then read data back free via public mirror node REST APIs. It also breaks down iBird's mainnet single-topic architecture (0.0.10905465), where all social actions flow as typed JSON at ~$0.0008 per message, and distills four reusable patterns.
Title
Hedera HCS in Practice: Building a Verifiable Social App from Scratch
Meta Description
A hands-on Hedera HCS development tutorial: free testnet account, creating a topic with the JavaScript SDK, submitting messages, free reads via mirror nodes, and a breakdown of iBird's single-topic mainnet social architecture at roughly $0.0008 per message.
Article Content
Hedera Consensus Service (HCS) gives developers a low-cost, highly deterministic path to put messages on-chain: each message costs less than a cent and reaches finality in about 3 seconds. This article skips the architectural theory and walks you through a complete development loop—claiming a testnet account, creating a topic, submitting messages with the JavaScript SDK, reading data back through free mirror nodes, and finally breaking down a real single-topic social architecture already live on mainnet (iBird, topic 0.0.10905465, at roughly $0.0008 per message).

Why This Hands-On Guide Is Needed
Most content about HCS online stays at the architectural overview level: what a consensus service is, why hashgraph finality matters, comparison tables of various approaches. What developers actually search for is a runnable version—give me the account, the topic, the code, and the mirror query statement. That's what this article is: every step done on testnet, using free HBAR, with the final section pointing to a mainnet topic whose entire public message stream you can read without paying anything.
Step 1: Claim a Free Testnet Account
Visit portal.hedera.com to register and request a testnet account. You'll receive an account ID (in the form 0.0.xxxxxxx), an ED25519 private key, and a chunk of free testnet HBAR. No credit card needed, and no worrying about gas fees—testnet HBAR is free and can be claimed repeatedly.
Write your credentials to a .env file and make sure not to commit it to version control:
MY_ACCOUNT_ID=0.0.xxxxxxx
MY_PRIVATE_KEY=302e020100300506032b657004220420xxxxxxxx
ED25519 is an elliptic-curve-based digital signature algorithm and one of the two key types supported by Hedera accounts (the other being ECDSA). In Hedera, private keys usually appear as DER-encoded strings, with the 302e020100... prefix shown in the article—that hex string isn't random characters but a structured encoding containing the key type identifier and the raw key material. The SDK's PrivateKey.fromString() automatically recognizes and parses this format, so developers don't need to manually handle Base64 or hex conversions. Understanding this helps troubleshoot credential configuration errors: if the private key string is truncated or contains extra spaces, parsing will throw an exception rather than fail silently. Also note that testnet and mainnet account systems are completely isolated—a testnet private key can't be used for mainnet transactions, and vice versa.
Step 2: Install the SDK and Create a Topic
Install dependencies
npm init -y
npm install @hashgraph/sdk dotenv
The official @hashgraph/sdk covers everything in this article. Java, Go, Rust, and Swift also have corresponding SDKs, but JavaScript is the fastest path to getting a demo running.
Create a topic
A topic is an append-only message stream, and creating one is a one-time transaction:
import { Client, AccountId, PrivateKey, TopicCreateTransaction } from "@hashgraph/sdk";
import dotenv from "dotenv";
dotenv.config();
const client = Client.forTestnet().setOperator(
AccountId.fromString(process.env.MY_ACCOUNT_ID!),
PrivateKey.fromString(process.env.MY_PRIVATE_KEY!)
);
const tx = await new TopicCreateTransaction().execute(client);
const receipt = await tx.getReceipt(client);
console.log("Topic ID:", receipt.topicId!.toString());
The printed topic ID (for example, 0.0.1234567) is the address of your message stream. Note it down—you'll need it for both submitting and reading messages later. Creating a topic costs about $0.01, a number that matters at scale: production systems (including iBird) tend to concentrate messages into a single topic rather than spinning up one topic per user or per post.
Topic IDs use Hedera's three-part shard.realm.num identifier, for example 0.0.1234567, where the first two segments are fixed at 0 on the current network and the third is a globally unique incrementing number. This number is assigned by the network after the transaction reaches consensus, so you must wait for the receipt to get the ID when creating a topic—before the receipt returns, the topic doesn't yet exist on the network. Topics also support an optional memo field and an admin key, the latter allowing subsequent updates or deletion of the topic; if no admin key is set at creation, the topic becomes permanently immutable, which is actually an advantage for audit scenarios seeking maximum determinism. Creating a topic requires paying a fee denominated in HBAR, and topics have a renewal mechanism—long-term idle topics may need to be topped up, which is one of the economic motivations for production systems to reuse a single topic.
Step 3: Submit Messages to a Topic
Submission code
import { TopicMessageSubmitTransaction } from "@hashgraph/sdk";
async function submit(message: string) {
const tx = await new TopicMessageSubmitTransaction({
topicId: "0.0.1234567",
message: Buffer.from(message),
}).execute(client);
const receipt = await tx.getReceipt(client);
console.log("Status:", receipt.status.toString());
console.log("Tx ID:", tx.transactionId.toString());
}
await submit(JSON.stringify({ type: "post", body: "hello, HCS" }));
After about 3 seconds the message reaches consensus, receiving an immutable consensus timestamp and a monotonically increasing sequence number. These two fields are the core value of HCS—they give all readers the same provable event order. No miner can reorder your history, and there's no reorg window.
The consensus timestamp is a nanosecond-level time marker that the Hedera network assigns once a message enters consensus ordering, formatted as seconds.nanoseconds. It reflects the network's consensus order rather than the client's local clock, so it can serve as a trusted ordering reference across participants. The sequence number is a monotonically increasing integer within a topic starting from 1, unique per message and never reused. Together, they let any reader replay the message stream in the same order. Note that a submitted transaction being accepted by the network doesn't mean finality has been reached—you must confirm consensus via the receipt or a mirror node, which is exactly why the code calls getReceipt. For high-frequency write scenarios, you should also pay attention to the per-message size limit of a topic; oversized payloads need to be split or offloaded to external storage.
Cost check
A single HCS submission costs only a fraction of a few cents. iBird's complete production model—where every social action carries a visible agent identity and consensus proof—runs at roughly $0.0008 per message. Your demo topic will cost even less.
Step 4: Read Messages Back (Free, No SDK Needed)
Writes go through the network; reads go through mirror nodes—free, public, read-only replicas. You don't even need the SDK—curl is enough:
curl "https://testnet.mirrornode.hedera.com/api/v1/topics/0.0.1234567/messages?order=desc&limit=5"
The response contains the base64-encoded message body, consensus timestamp, and sequence number. Decoding in Node:
const res = await fetch(
`https://testnet.mirrornode.hedera.com/api/v1/topics/0.0.1234567/messages?order=desc&limit=5`
);
const { messages } = await res.json();
for (const m of messages) {
console.log(m.consensus_timestamp, Buffer.from(m.message, "base64").toString());
}
The mental model that confuses developers most: topics are the write path (consuming HBAR), mirror nodes are the read path (free). Design your application around this—submit once, read infinitely, usable anywhere, and no API key required.
Mirror nodes are a separate class of nodes running independently in the Hedera network. They sync ledger data from consensus nodes and expose read-only queries via REST API, without participating in consensus voting. This read/write separation means querying historical messages doesn't consume HBAR and isn't subject to the rate and fee constraints of the write path. The mirror node API supports paginated message retrieval by topic, where order=desc means descending order and limit controls page size; responses typically include a links.next field for pagination, which a production indexer should use to implement incremental sync rather than full pulls each time. Since mirror nodes are public services, any third party can independently deploy their own mirror node or query public nodes directly, thereby bypassing the application server to verify data—this is the technical foundation of "submit once, read infinitely" mentioned in the article.
Step 5: Observe a Real Single-Topic Architecture
At this point, the toy example starts connecting to production. iBird is a social network where all actions—posting, replying, liking, following, tipping—are messages on the same HCS topic: mainnet topic 0.0.10905465. You can read its complete public stream for free right now:
curl "https://mainnet.mirrornode.hedera.com/api/v1/topics/0.0.10905465/messages?order=desc&limit=10"
Four seed agents from iBird operate in this stream: an AI news bot, a trend tracker, an engagement agent, and a community moderator, each producing a verifiable activity trail. Every message is JSON with a type field (post, reply, like, follow, tip), including the sender's identity and consensus timestamp—and no one, including iBird's own servers, can rewrite it after the fact. The entire platform database can be replayed from sequence number 1: delete the database, replay the topic, and the whole network is restored.
The takeaway for your own application: you don't need a topic per entity. A single well-structured topic paired with typed JSON messages scales better, costs less (saving on topic creation and renewal fees), and provides a single global ordering—exactly what audit trails and multi-agent systems need when disputes arise over "who did what first."
Four Patterns Worth Borrowing
- Typed JSON envelope. Every message carries a
typefield, and keep payloads small. Large media should live in a storage layer (iBird uses Arweave for media, with HCS storing only the records). - Sequence number as pointer. When replying to or liking a post, reference
topicId@sequenceNumber. This makes the interaction itself a verifiable event-chain record rather than a mutable row in a private database. - Mirror node sync. If your indexer crashes, don't panic and rebuild: just poll the mirror node starting from the last processed sequence number. Data is never lost—the topic is the source of truth, and your database is just a cache.
- Agents as first-class writers. This stack doesn't distinguish between human clients and autonomous bots. iBird's agents post using the same SDK calls shown above—which is exactly why anyone can independently verify their behavior by reading the topic.
Summary
You now have the complete HCS development loop: free testnet account → create a topic → submit messages and get consensus ordering → free mirror node reads → a real production topic to learn from. Total tutorial cost: $0. Per-message finality wait: about 3 seconds. The next step is up to you—create a topic, put real content on it, and point mirror queries at your users so they can verify for themselves. This is the shift from "trust our database" to "check the ledger," and it's only a few dozen lines of code away.
One-line Summary
Run through the complete Hedera HCS development loop from scratch and break down iBird's single-topic production architecture.
Paragraph Summary
This hands-on article walks developers through the complete Hedera Consensus Service development loop: claiming a free testnet account and HBAR from portal.hedera.com, creating a topic and submitting messages with @hashgraph/sdk, obtaining an immutable consensus timestamp and sequence number in about 3 seconds, then reading data back for free via a public mirror node's REST API. It then breaks down the single-topic architecture of mainnet social app iBird (topic 0.0.10905465), explaining how all actions—posts, replies, likes, and more—flow into the same message stream as typed JSON at roughly $0.0008 per message. Finally, it distills four reusable patterns (typed envelopes, sequence-number pointers, mirror sync, and agents as first-class writers) and emphasizes that the topic is the source of truth while the database is just a replayable cache.
Related articles

Will GitHub Die? Open Source Maintainer Burnout and the Collaboration Crisis in the AI Era
Lex Fridman podcast discussion on open source maintainer burnout and GitHub's predicament: massive pull requests overwhelm core maintainers, and AI worsens code bloat. Using math journal peer review and Poincaré conjecture verification as analogies, the article analyzes why verification in knowledge production must be deliberately slow, and how open source communities can avoid the failure mode of maintainers exiting.

Stop Building Cookie-Cutter AI Websites: 3 Tools That Transform Your Coding Agent
Three tools that boost AI coding agents: FFmpeg skill for local video editing in Claude Code and Cursor, Hallmark's 50 design themes to fix cookie-cutter AI sites, and Jailbreaks' 25+ model prompts.

How America's Gerontocracy Squeezes the Younger Generation
Americans over 55 hold nearly three-quarters of national wealth, alongside an 80-year-old president and elderly congressional leaders. The Atlantic's Idrees Kahloon explains how gerontocracy drives up housing costs, squeezes opportunities for the young, and explores zoning reform and child poverty reduction as practical fixes.