Skip to main content

Petals (HCS-15)

Goal: start with a plain Hedera account and finish with a production-ready Petal that publishes an HCS-11 profile and proves membership via HCS-15.


0. Understand HCS-15 (Petal accounts)

The HCS-15 spec (see /docs/standards/hcs-15.md) is explicit about what makes a Petal “valid.” It’s worth restating the rules because every downstream workflow—HCS-16 thresholds, HCS-17 hashes, HCS-21 adapters—assumes you obey them.

Canonical requirements

RequirementWhy it matters
Key reuse (no alias)Floras can compare public keys between a base and a Petal to prove membership without extra signatures.
HCS-11 profileDiscovery services (HCS-18) and registries need a canonical place to read Petal metadata.
Ledger challengeBefore a Petal is trusted with state updates or escrow, it must prove control of its key (registry broker challenge, schedule sign, etc.).
Discovery announcementNot mandatory, but recommended: Petals should announce availability over HCS-18 topics so other Floras can discover ready members.

All the SDK helpers we’ll use (HCS15Client, HCS11Client, registry-broker client) exist to satisfy those requirements without hand-coding transactions.


1. Environment & dependencies

  1. Install Node.js ≥ 20 and pnpm ≥ 10.
  2. Create a project and install the SDK:
    pnpm add @hol-org/standards-sdk @hashgraph/sdk
  3. Prepare .env (never commit secrets):
    HEDERA_NETWORK=testnet
    HEDERA_OPERATOR_ID=0.0.1234 # base account that funds Petals
    HEDERA_OPERATOR_KEY=302e02...

    # optional: location to cache generated Petal accounts
    PETAL_CACHE=./.petals.json
  4. Smoke-test the credentials:
    pnpm dlx hedera-cli account:balance --account $HEDERA_OPERATOR_ID --key $HEDERA_OPERATOR_KEY --network $HEDERA_NETWORK
    You should see the account balance printed without errors before moving on.

2. Generate/reuse a base key

Petals must reuse the base account ECDSA key. If the base account lives in a hardware wallet or custodial setup, export the ECDSA private key first (HIP-32 style) and skip this step. Otherwise generate a fresh one:

import { PrivateKey } from '@hashgraph/sdk';
import fs from 'node:fs';

const petalBaseKey = PrivateKey.generateECDSA();
fs.writeFileSync('.petal-key.txt', petalBaseKey.toStringDer());

Fund the corresponding account with a few HBAR (e.g., via the Hedera Portal faucet) and note the account ID.

📝 Tip: the repo includes demo/hcs-15/petal-accounts-demo.ts, which will generate the key, fund it via a configured faucet, and persist .env entries for you. Run pnpm tsx demo/hcs-15/petal-accounts-demo.ts if you prefer the scripted workflow.


3. Create the Petal account

// src/petals/create-petal.ts
import { HCS15Client } from '@hol-org/standards-sdk';
import { PrivateKey } from '@hashgraph/sdk';

const operatorId = process.env.HEDERA_OPERATOR_ID!;
const operatorKey = process.env.HEDERA_OPERATOR_KEY!;

const hcs15 = new HCS15Client({
network: (process.env.HEDERA_NETWORK || 'testnet') as 'testnet' | 'mainnet',
operatorId,
operatorKey,
});

async function createPetalAccount() {
const basePrivateKey = PrivateKey.fromStringECDSA(
process.env.PETAL_BASE_KEY!,
);

const { accountId } = await hcs15.createPetalAccount({
basePrivateKey,
initialBalance: 5, // HBAR
accountMemo: 'HCS-15 Petal',
});

console.log('Petal created:', accountId);
return { accountId, basePrivateKey };
}

createPetalAccount().catch(console.error);

This executes the buildHcs15PetalAccountCreateTx path, so the resulting account automatically shares the base key.

After the transaction settles, you should see:

Petal created: 0.0.910001

Mirror node verification (next step) will confirm the keys match. If you want deterministic naming, set accountMemo: 'Flora Demo • Petal A' and it will appear in Hedera explorers.


4. Verify the Petal (mirror-node check)

import { HCS15Client } from '@hol-org/standards-sdk';

async function verifyPetal(petalAccountId: string, baseAccountId: string) {
const ok = await hcs15.verifyPetalAccount(petalAccountId, baseAccountId);
if (!ok) {
throw new Error('Petal verification failed – keys did not match');
}
console.log('Petal account verified via mirror node');
}

verifyPetalAccount fetches both accounts from the mirror node and ensures their public keys are identical.

CLI alternative (uses the same API under the hood):

pnpm tsx demo/hcs-15/petal-accounts-demo.ts verify --petal 0.0.910001 --base $HEDERA_OPERATOR_ID

If verification fails:

SymptomLikely causeFix
Petal verification failedThe Petal was created with a different key (maybe ED25519)Re-create the Petal using PrivateKey.fromStringECDSA
Mirror node errorAccount hasn’t propagated yetWait ~5s on testnet and retry
NOT_FOUNDAccount ID typoDouble-check .env and script arguments

5. Publish an HCS-11 Petal profile

Floras and discovery registries expect HCS-11 profiles. Use HCS11Client to publish one that references your ledger info:

import { HCS11Client } from '@hol-org/standards-sdk';

const hcs11 = new HCS11Client({
network: process.env.HEDERA_NETWORK || 'testnet',
operatorId,
operatorKey,
});

await hcs11.publishProfile({
accountId: petalAccountId,
profile: {
version: '1.0',
display_name: 'Demo Petal A',
type: 1, // AI agent / Petal
socials: [
{ platform: 'x', handle: '@demo-petal' },
],
flora: {
config: {
uri: process.env.FLORA_CONFIG_URI, // pointer to flora.yaml/appnet.yaml
},
},
},
});

Now any consumer can query the Petal’s account memo (hcs-11:hcs://1/...) and resolve its profile.

Recommended fields for Petal profiles:

FieldNotes
display_nameHuman-readable identifier (“Flora Oracle Petal A”).
typeUse 1 (AI agent) or whichever value you’ve allocated for Petals.
socialsProvide at least one contact channel (X, Discord, Matrix).
flora.config.uriPoints to flora.yaml so matchmakers can ensure compatibility.
ledger.challenge (optional)Include instructions for ledger challenge endpoints if you run your own.

To verify the memo + retrieval flow:

pnpm dlx hedera-cli account:memo --account $PETAL_ACCOUNT
# should show hcs-11:hcs://1/<topic>

6. Pass the ledger authentication challenge

Before a Petal is trusted with registry interactions (credit purchases, encryption key registration, x402 payments), it must prove control of its ledger key. The registry broker exposes a challenge/response flow. Example:

import { RegistryBrokerClient } from '@hol-org/standards-sdk/dist/services/registry-broker/client';

const broker = new RegistryBrokerClient({
baseUrl: 'https://hol.org/registry/api/v1',
userAgent: '@hol-org/petal',
});

await broker.verifyLedgerAccess({
accountId: petalAccountId,
network: 'hedera:testnet',
hederaPrivateKey: process.env.PETAL_BASE_KEY!,
label: 'petal-auth',
});

console.log('Ledger challenge passed');

This is the same utility used in /demo/hcs-15/petal-accounts-demo.ts. Run it whenever you rotate keys or migrate to a new registry broker API.


7. Optional: announce on HCS-18

If you want other Floras to discover your Petal automatically, emit an HCS-18 announcement. The SDK’s HCS18Client (see demo/hcs-18/flora-discovery-demo.ts) will build the petal_available message for you. Minimal example:

import { HCS18Client } from '@hol-org/standards-sdk';

const hcs18 = new HCS18Client({
network: process.env.HEDERA_NETWORK || 'testnet',
operatorId: petalAccountId,
operatorKey: process.env.PETAL_BASE_KEY!,
});

await hcs18.sendPetalAnnouncement({
discoveryTopicId: process.env.HCS18_DISCOVERY_TOPIC!,
floraPreferences: {
adapterSetUri: process.env.FLORA_CONFIG_URI,
minThreshold: 2,
},
});

Downstream Floras can stream that topic and invite Petals that match their adapter set or latency requirements.


8. CLI workflows (from this repo)

CommandDescription
pnpm tsx demo/hcs-15/petal-accounts-demo.tsInteractive flow that creates a Petal, publishes the HCS-11 profile, and stores the credentials in .env.
pnpm tsx demo/hcs-18/flora-discovery-demo.tsEnd-to-end discovery scenario (Petals announce, Floras respond, join requests exchanged).
pnpm tsx demo/hcs-16/flora-e2e-demo.tsCreates multiple Petals, sets up a Flora, and walks through join votes/join acceptance.

Use these demos both as a learning tool and as regression tests for your own modifications.


9. Checklist & troubleshooting

  • Base ECDSA key generated and backed up (hardware or .petal-key.txt).
  • Petal account created via HCS15Client#createPetalAccount.
  • verifyPetalAccount returns true.
  • HCS-11 profile published (memo = hcs-11:hcs://…).
  • Ledger challenge with registry broker passes.
  • Optional: HCS-18 announcement emitted.
  • Subscribed to Flora C/T/S topics and adapters installed.

Common issues

IssueFix
Petal account creation fails with KEY_MISMATCHEnsure PrivateKey.fromStringECDSA (not ED25519) and that the base account is ECDSA-enabled.
HCS-11 publish fails (INVALID_SIGNATURE)Make sure operatorId/operatorKey correspond to the Petal, not the base operator.
Registry broker challenge failsPetal account needs at least a few HBAR; also ensure the base key is funded and not throttled.

Once everything passes, proceed to Floras · Part 1 to wire these Petals into a threshold Flora, then Floras · Part 2 to build the price oracle.