Building Holochain Apps
Let users prove their Flowsta identity on your Holochain app's DHT.
When users link their Flowsta identity to your app, a cryptographic attestation (IsSamePersonEntry) is committed to your DHT. Anyone on your network can verify the link using Ed25519 cryptography - no shared DNA or API dependency required.
Prerequisites
- A Holochain application with its own DNA
- A registered app at dev.flowsta.com (for
client_id) - Users have Flowsta Vault installed on their desktop
Works with any framework
This guide applies to Tauri, Electron, and any other desktop framework. The @flowsta/holochain SDK communicates with Flowsta Vault via its localhost IPC server and works in any JavaScript environment — no framework-specific adapter needed.
Step 1: Add Agent-Linking Zomes
Add the flowsta-agent-linking crate to your DNA:
# integrity/Cargo.toml
[dependencies]
flowsta-agent-linking-integrity = { git = "https://github.com/WeAreFlowsta/flowsta-agent-linking" }# coordinator/Cargo.toml
[dependencies]
flowsta-agent-linking-coordinator = { git = "https://github.com/WeAreFlowsta/flowsta-agent-linking" }Reference them in your dna.yaml:
integrity:
zomes:
- name: agent_linking_integrity
bundled: ../../target/.../flowsta_agent_linking_integrity.wasm
coordinator:
zomes:
- name: agent_linking
bundled: ../../target/.../flowsta_agent_linking_coordinator.wasm
dependencies:
- name: agent_linking_integrityStep 2: Install the SDK
npm install @flowsta/holochainStep 3: Configure Scopes
In your app's settings at dev.flowsta.com, select the scopes your app needs. Scopes control which Flowsta profile fields are exposed to your app via the Vault's local IPC server (GET /status). The user sees the scope list in the Vault approval dialog before they approve.
| Scope | What you receive | Typical use |
|---|---|---|
openid | Basic identity (auto-included) | Always present — not shown to the user |
did | User's decentralized identifier | Unique identity across your app |
public_key | Vault's Holochain agent public key | Agent linking ceremony |
holochain | Holochain identity access | Required for agent linking |
display_name | User's display name | Profile UI |
username | User's @username | Profile UI |
profile_picture | Avatar URL | Profile UI |
Fields for scopes you haven't selected are returned as null from /status, even if the user has that data in their Vault. Scope changes at dev.flowsta.com take effect immediately — no app rebuild needed.
Step 4: Request Identity Linking
import { linkFlowstaIdentity } from '@flowsta/holochain';
// Request link from Vault
const result = await linkFlowstaIdentity({
appName: 'ChessChain',
clientId: 'flowsta_app_abc123', // from dev.flowsta.com
localAgentPubKey: myAgentKey, // uhCAk... format
});
// result.payload contains:
// - vaultAgentPubKey: the Vault's agent key
// - vaultSignature: Ed25519 signature of the 78-byte linking payloadStep 5: Commit to Your DHT
import { decodeHashFromBase64 } from '@holochain/client';
await appWebsocket.callZome({
role_name: 'my-role',
zome_name: 'agent_linking',
fn_name: 'create_direct_link',
payload: {
other_agent: decodeHashFromBase64(result.payload.vaultAgentPubKey),
other_signature: base64ToSignature(result.payload.vaultSignature),
},
});The create_direct_link function:
- Verifies the Vault's Ed25519 signature
- Creates a local signature from your app's agent key
- Commits an
IsSamePersonEntrywith both signatures - Creates lookup links for querying
Step 6: Query Linked Agents
import { getFlowstaIdentity } from '@flowsta/holochain';
const linkedAgents = await getFlowstaIdentity({
appWebsocket,
roleName: 'my-role',
agentPubKey: someAgentKey,
});
// linkedAgents is Uint8Array[] - array of linked agent public keys
if (linkedAgents.length > 0) {
console.log(`Linked to ${linkedAgents.length} Flowsta identities`);
}Or call the zome directly:
const linkedAgents = await appWebsocket.callZome({
role_name: 'my-role',
zome_name: 'agent_linking',
fn_name: 'get_linked_agents',
payload: myAgentKey,
});Error Handling
Handle Vault availability gracefully:
import { getVaultStatus, linkFlowstaIdentity } from '@flowsta/holochain';
// Check if Vault is available first
const status = await getVaultStatus();
if (!status.running) {
showMessage('Please install and start Flowsta Vault');
return;
}
if (!status.unlocked) {
showMessage('Please unlock your Flowsta Vault');
return;
}
try {
const result = await linkFlowstaIdentity({ /* ... */ });
} catch (error) {
if (error.name === 'UserDeniedError') {
showMessage('Identity linking was cancelled');
} else if (error.name === 'InvalidClientIdError') {
showMessage('App registration error');
}
}See the full error reference in Agent Linking.
User Data Backups
If your app stores user data on Holochain, integrate auto-backups to store data securely in users' Vaults. Backups work while the Vault is locked and create timestamped snapshots (up to 10 per app).
Include both public and private data in your backups. If your app uses encrypted entries, decrypt them before including in the backup — the Vault encrypts the backup at rest.
Encrypted Private Data
Your app can store private data on the public DHT using client-side encryption. Entries are encrypted with lair's xsalsa20poly1305 crypto_box (256-bit) before being committed. Peers replicate the ciphertext for backup resilience, but only the author can decrypt.
Use a generic "private" hint on all encrypted entries — don't leak metadata about what type of private data is stored.
See Encrypted Entries on Public DHT for the full pattern, and ProofPoll for a working implementation.
Next Steps
- Agent Linking — Detailed attestation mechanics and API reference
- Encrypted Entries — Private data on public DHT
- @flowsta/holochain SDK — Backup API reference
- IPC Endpoints — Raw IPC API reference