Skip to main content

HCS-2: Decentralized Topic Registry

The HCS-2 module provides a decentralized registry for Hedera Consensus Service (HCS) topics. It allows for the discovery and management of topics in a standardized way, supporting both indexed and non-indexed registries.

What HCS-2 DoesDirect link to What HCS-2 Does

  • Creates Registries - Establishes new HCS topics to act as registries.
  • Manages Entries - Supports registering, updating, deleting, and migrating topic entries.
  • Standardized Memos - Uses a specific memo format for identifying HCS-2 registries.
  • Flexible Queries - Allows for fetching and parsing registry entries.

Architecture OverviewDirect link to Architecture Overview

HCS-2 provides a standardized way to manage topic registries using Hedera Consensus Service:

Registry Types: Indexed vs. Non-IndexedDirect link to Registry Types: Indexed vs. Non-Indexed

The HCS-2 standard defines two types of registry topics, each with different behaviors and use cases:

Key DifferencesDirect link to Key Differences

FeatureIndexed RegistryNon-Indexed Registry
Registry type enumHCS2RegistryType.INDEXED (0)HCS2RegistryType.NON_INDEXED (1)
Update operations✓ Supported✗ Not supported
Delete operations✓ Supported✗ Not supported
Message historyMaintains full historyOnly cares about latest message
Memory/storageHigher requirementsLower requirements
PerformanceMay be slower for large registriesFaster, only processes latest state
Use caseFull audit history neededOnly current state matters
Memo formathcs-2:0:ttlhcs-2:1:ttl

When to Use Each TypeDirect link to When to Use Each Type

  • Use Indexed Registries when:

    • You need to maintain a history of all entries
    • Entries need to be updated or deleted over time
    • Audit trails are important for your application
    • Example: Service registries that need change tracking, governance systems
  • Use Non-Indexed Registries when:

    • Only the latest state matters for your application
    • Storage efficiency is important
    • You have high-volume, simple registrations
    • Example: Status indicators, availability registries, lightweight discovery services

SDK ImplementationDirect link to SDK Implementation

The SDK validates operations based on registry type:

  • Update and Delete operations will throw an error if attempted on a non-indexed registry
  • GetRegistry operation for non-indexed registries returns only the latest entry per topic
  • Creation allows you to specify which type you need (defaults to indexed):
// Create an indexed registry (default)
const indexedRegistry = await client.createRegistry();

// Create a non-indexed registry
const nonIndexedRegistry = await client.createRegistry({
registryType: HCS2RegistryType.NON_INDEXED
});

Getting StartedDirect link to Getting Started

InstallationDirect link to Installation

npm install @hashgraphonline/standards-sdk

Basic Setup (Node.js)Direct link to Basic Setup (Node.js)

For server-side applications, use HCS2Client.

import { HCS2Client, HCS2RegistryType } from '@hashgraphonline/standards-sdk';

// Initialize the HCS-2 client
const client = new HCS2Client({
network: 'testnet',
operatorId: 'your-operator-id',
operatorKey: 'your-operator-private-key',
logLevel: 'info',
});

Basic Setup (Browser)Direct link to Basic Setup (Browser)

For client-side applications, use BrowserHCS2Client with a wallet connection.

import { BrowserHCS2Client, HCS2RegistryType } from '@hashgraphonline/standards-sdk';
import { HashinalsWalletConnectSDK } from '@hashgraphonline/hashinal-wc';

// Initialize Hashinals WalletConnect
const hwc = new HashinalsWalletConnectSDK();
await hwc.init(/** wallet connect options */);
await hwc.connect(/** connection options */);

// Initialize the HCS-2 browser client
const browserClient = new BrowserHCS2Client({
network: 'testnet',
hwc: hwc,
logLevel: 'info',
});

Implementation WorkflowDirect link to Implementation Workflow

1. Creating a RegistryDirect link to 1. Creating a Registry

First, you need to create a topic that will serve as your registry:

Example code:

// Create an indexed registry (default)
const response = await client.createRegistry({
registryType: HCS2RegistryType.INDEXED,
ttl: 3600, // Time-to-live in seconds
memo: 'My Indexed Registry',
adminKey: true, // Use operator key as admin key
});

if (response.success) {
console.log(`Registry created with topic ID: ${response.topicId}`);
} else {
console.error(`Error: ${response.error}`);
}

// Create a non-indexed registry
const nonIndexedResponse = await client.createRegistry({
registryType: HCS2RegistryType.NON_INDEXED,
ttl: 3600,
memo: 'My Non-Indexed Registry',
});

2. Registering EntriesDirect link to 2. Registering Entries

Once you have a registry, you can register entries (topics) in it:

Example code:

const registryTopicId = '0.0.12345'; // Your registry topic ID

const registerResponse = await client.registerEntry(registryTopicId, {
targetTopicId: '0.0.67890', // The topic to register
metadata: 'https://example.com/metadata.json',
memo: 'Initial registration',
});

if (registerResponse.success) {
console.log(`Entry registered. Sequence number: ${registerResponse.sequenceNumber}`);
} else {
console.error(`Error: ${registerResponse.error}`);
}

3. Updating EntriesDirect link to 3. Updating Entries

In indexed registries, you can update existing entries:

Example code:

// This only works with indexed registries
const updateResponse = await client.updateEntry(registryTopicId, {
uid: '1', // The sequence number of the message to update
targetTopicId: '0.0.98765', // New target topic
metadata: 'https://example.com/new-metadata.json',
memo: 'Updated registration',
});

if (updateResponse.success) {
console.log(`Entry updated. New sequence number: ${updateResponse.sequenceNumber}`);
} else {
console.error(`Error: ${updateResponse.error}`);
}

4. Deleting EntriesDirect link to 4. Deleting Entries

Remove entries from an indexed registry:

Example code:

// This only works with indexed registries
const deleteResponse = await client.deleteEntry(registryTopicId, {
uid: '1', // The sequence number of the message to delete
memo: 'Entry deleted',
});

if (deleteResponse.success) {
console.log(`Entry deleted. Sequence number: ${deleteResponse.sequenceNumber}`);
} else {
console.error(`Error: ${deleteResponse.error}`);
}

5. Migrating a RegistryDirect link to 5. Migrating a Registry

Point an entire registry to a new topic for versioning or upgrades:

Example code:

const oldRegistryTopicId = '0.0.12345';
const newRegistryTopicId = '0.0.67890';

const migrateResponse = await client.migrateRegistry(oldRegistryTopicId, {
newTopicId: newRegistryTopicId,
memo: 'Migrating to new registry topic',
});

if (migrateResponse.success) {
console.log(`Registry migrated to: ${newRegistryTopicId}`);
} else {
console.error(`Error: ${migrateResponse.error}`);
}

Querying RegistriesDirect link to Querying Registries

Retrieve entries from a registry and process them:

Example code:

const registry = await client.getRegistry(registryTopicId);
console.log('Registry entries:', registry.entries);

For non-indexed registries, only the latest entry per topic is returned.

Full Lifecycle ExampleDirect link to Full Lifecycle Example

Use Case: Building a Decentralized Service DirectoryDirect link to Use Case: Building a Decentralized Service Directory

One common use case for HCS-2 is building a service directory where different applications can register their APIs or services:

Implementation steps:

  1. Create an indexed registry topic
  2. Services register their endpoints with metadata
  3. Applications query the registry to discover available services
  4. When services update their APIs, they can update their registry entries
  5. If a service is deprecated, it can delete its entry

API ReferenceDirect link to API Reference

HCS2Client / BrowserHCS2ClientDirect link to HCS2Client / BrowserHCS2Client

The API is consistent between the Node.js and Browser clients.

createRegistry(options: CreateRegistryOptions): Promise<TopicRegistrationResponse>Direct link to createregistryoptions-createregistryoptions-promisetopicregistrationresponse

Creates a new HCS-2 registry topic.

registerEntry(registryTopicId: string, options: RegisterEntryOptions): Promise<RegistryOperationResponse>Direct link to registerentryregistrytopicid-string-options-registerentryoptions-promiseregistryoperationresponse

Registers a new topic in the registry.

updateEntry(registryTopicId: string, options: UpdateEntryOptions): Promise<RegistryOperationResponse>Direct link to updateentryregistrytopicid-string-options-updateentryoptions-promiseregistryoperationresponse

Updates an existing entry in an indexed registry.

deleteEntry(registryTopicId: string, options: DeleteEntryOptions): Promise<RegistryOperationResponse>Direct link to deleteentryregistrytopicid-string-options-deleteentryoptions-promiseregistryoperationresponse

Deletes an entry from an indexed registry.

migrateRegistry(registryTopicId: string, options: MigrateTopicOptions): Promise<RegistryOperationResponse>Direct link to migrateregistryregistrytopicid-string-options-migratetopicoptions-promiseregistryoperationresponse

Submits a migration message to a registry topic.

getRegistry(topicId: string, options?: QueryRegistryOptions): Promise<TopicRegistry>Direct link to getregistrytopicid-string-options-queryregistryoptions-promisetopicregistry

Retrieves and parses all entries from a registry topic.

TypesDirect link to Types

enum HCS2RegistryType {
INDEXED = 0,
NON_INDEXED = 1
}

interface CreateRegistryOptions {
memo?: string;
ttl?: number;
adminKey?: boolean | string | PrivateKey;
submitKey?: boolean | string | PrivateKey;
registryType?: HCS2RegistryType;
}

interface RegisterEntryOptions {
targetTopicId: string;
metadata?: string;
memo?: string;
}

interface UpdateEntryOptions {
targetTopicId: string;
uid: string;
}

interface DeleteEntryOptions {
uid: string;
memo?: string;
}

interface MigrateTopicOptions {
newTopicId: string;
memo?: string;
}