Agent Linking
Link your Holochain app's agent key with the user's Flowsta Vault identity.
Flowsta Vault acts as a local identity provider for Holochain apps. When a user approves a link request, the Vault signs a cryptographic attestation that is committed to your app's DHT as an IsSamePersonEntry. Anyone on your DHT can verify the link using Ed25519 cryptography - no shared DNA, no API dependency, no one in between.
How It Works
The 78-Byte Payload
Vault constructs the signing payload itself (apps cannot influence what gets signed):
| Bytes | Content |
|---|---|
| 0-38 | First agent public key (39 bytes: 3-byte prefix + 32-byte key + 4-byte checksum) |
| 39-77 | Second agent public key (39 bytes, same format) |
The two 39-byte Holochain AgentPubKeys are sorted lexicographically to produce a canonical 78-byte payload, and both agents sign the raw 78 bytes (no MessagePack encoding). The dual-signature design means:
- The app agent attests "I am the same person as this Vault agent"
- The Vault agent attests "I am the same person as this app agent"
- Anyone can verify both signatures using the public keys in the payload
Because the attestation is a public claim that two keys are one person, one app agent links to one Flowsta identity. A second live link from the same app agent to a different identity would claim that two people are one; the zome refuses it, and so does the Vault. See Identity switching.
Prerequisites
- Register your app at dev.flowsta.com to get a
client_id - Add the
flowsta-agent-linkingzomes to your DNA (see Integration Guide) - Install the SDK:
npm install @flowsta/holochain
Quick Start
import { linkFlowstaIdentity, getFlowstaIdentity } from '@flowsta/holochain';
import { decodeHashFromBase64 } from '@holochain/client';
// Link identity
const result = await linkFlowstaIdentity({
appName: 'ChessChain',
clientId: 'flowsta_app_abc123...', // from dev.flowsta.com
localAgentPubKey: myAgentKey, // uhCAk... format
});
// The Vault's signature arrives base64-encoded; the zome wants the raw 64 bytes.
const signatureBytes = (b64: string) =>
Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
// Commit to your DHT
await appWebsocket.callZome({
role_name: 'chess',
zome_name: 'agent_linking',
fn_name: 'create_external_link',
payload: {
external_agent: decodeHashFromBase64(result.payload.vaultAgentPubKey),
external_signature: signatureBytes(result.payload.vaultSignature),
},
});
// Query linked identities
const linked = await getFlowstaIdentity({
appWebsocket,
roleName: 'chess',
agentPubKey: myAgentKey,
});Integration Guide
1. Add zomes to your DNA
Add the flowsta-agent-linking crates (MIT) to your DNA's integrity and coordinator zomes, pinned to a release tag:
# integrity Cargo.toml
[dependencies]
flowsta-agent-linking-integrity = { git = "https://github.com/WeAreFlowsta/flowsta-agent-linking", tag = "v0.3.0" }
# coordinator Cargo.toml
[dependencies]
flowsta-agent-linking-coordinator = { git = "https://github.com/WeAreFlowsta/flowsta-agent-linking", tag = "v0.3.0" }Tag v0.3.0 (integrity 0.2.0, coordinator 0.3.0) targets Holochain 0.6. The crates pin exact HDK versions, so your DNA's hdi and hdk must be on the same line - hdi = "=0.7.0" and hdk = "=0.6.0" for this tag. Build with RUSTFLAGS='--cfg getrandom_backend="custom"' cargo build --release --target wasm32-unknown-unknown.
Reference them in your dna.yaml:
integrity:
zomes:
- name: agent_linking_integrity
bundled: ../../target/wasm32-unknown-unknown/release/flowsta_agent_linking_integrity.wasm
coordinator:
zomes:
- name: agent_linking
bundled: ../../target/wasm32-unknown-unknown/release/flowsta_agent_linking_coordinator.wasm
dependencies:
- name: agent_linking_integrity2. Install SDK
npm install @flowsta/holochain3. Register your app
Go to dev.flowsta.com and create an app to get a client_id.
API Reference
SDK Functions
| Function | Description |
|---|---|
linkFlowstaIdentity(options) | Request identity link from Vault. Also binds the app to that identity (v3) |
getFlowstaIdentity(options) | Query linked agents on your DHT |
getVaultStatus(ipcUrl?) | Check if Vault is running/unlocked; did (v3.6.0), activeIdentity, identityEpoch, instanceId (v3.5.0) |
revokeFlowstaIdentity(options) | Notify Vault of revocation (removes the app from the Vault's connected list; the DHT entry is revoked with revoke_link) |
getFlowstaLinkStatus(options) | Check link status - three-state result (linked/unlinked/offline). Recommended. (v2.3.0) |
checkFlowstaLinkStatus(options) | Check if Vault still considers agent linked. Deprecated since v2.3.0; use getFlowstaLinkStatus. |
getVaultIdentity(ipcUrl?) | The unlocked agent key or null, one status read (v3.4.0) |
onIdentityChanged(cb, opts?) | Poll for an identity switch in the Vault; compares the identity epoch on Vault 1.5.0+ (v3.0.0, epoch v3.5.0) |
reconnectIdentity(options) | After a switch: rebind silently when the identity now in the Vault already links this app, else approval_needed (v3.4.0) |
partitionKeyFor(agentPubKey) | Folder-safe key to keep one identity's data apart from another's (v3.3.0) |
resolveVaultUrl(ipcUrl?) | Probe ports 27777-27779 in parallel and pick the best Vault (v3.0.0, parallel since v3.4.0) |
The full list, with the backup and signing functions, is on the SDK page.
Zome Functions
These are the Holochain zome calls available after adding the flowsta-agent-linking zomes to your DNA:
| Function | Input | Output | Description |
|---|---|---|---|
create_external_link | ExternalLinkInput | ActionHash | Commit attestation to DHT. Refuses when the calling agent already holds a live link to a different external agent |
get_linked_agents | AgentPubKey | Vec<AgentPubKey> | Get all linked agents (non-deleted) |
are_agents_linked | AgentPair | bool | Check if two agents are linked |
revoke_link | ActionHash | ActionHash | Revoke a link. Called by the app-side agent on your DHT; the integrity zome rejects a delete by anyone but the two agents named in the entry |
ExternalLinkInput
{
external_agent: Uint8Array, // 39-byte Holochain AgentPubKey
external_signature: Uint8Array, // 64-byte Ed25519 signature
}create_external_link first refuses if your agent already holds a live link to a different external agent (linking the same one again is allowed - reinstall, restore; revoke_link the old entry to move an agent on purpose). It then verifies the external agent's signature over the 78-byte payload, adds a local signature from your app's agent key, commits the IsSamePersonEntry with both signatures, and creates lookup links for querying.
What the integrity zome enforces on every peer (a modified coordinator cannot get around it): an entry carries two different keys in canonical order, its author is one of them, and both raw Ed25519 signatures verify over the sorted 78-byte pair; only one of the two agents can delete (revoke) it; a lookup link must start from one of the two agents and be written by one of them. The "one agent, one external identity" refusal is a coordinator rule, since it has to read the DHT.
Error Handling
| Error | Cause | Suggested UX |
|---|---|---|
VaultNotFoundError | Vault not running or not installed | "Install or start Flowsta Vault" |
VaultBlockedError (v3.1.0) | The browser refused the loopback request (Chrome 142+ Local Network Access permission); the Vault may be running | "Allow local network access for this site in the browser" |
VaultLockedError | Vault is locked (password required) | "Please unlock your Flowsta Vault" |
UserDeniedError | User rejected the approval dialog | "Identity linking canceled" |
InvalidClientIdError | client_id not registered, disabled, or invalid | Developer error - check registration |
MissingClientIdError | No client_id provided | Developer error |
ApiUnreachableError | Can't reach Flowsta API to verify app (the first link needs internet) | "Check internet connection" |
IdentityMismatchError (v3.0.0) | Thrown by later calls (backups, signDocument, authenticateWithVault) when the Vault holds a different identity than the one this app linked with | Stop. "Your Vault is signed in as someone else" - offer switch back / open as / disconnect, never re-link |
FlowstaHolochainError with code: 'agent_linked_elsewhere' | Vault 1.5.0+ answered 409: another identity in this Vault already links this app agent | Show error.description (it names the identity); the person opens the app as that identity or disconnects it there |
FlowstaHolochainError with code: 'timeout' | The user did not answer the Vault dialog within 60 seconds (408) | "No answer from the Vault - try again" |
Data Backups
Once linked, your app can back up user data to Flowsta Vault's encrypted local storage. Users can view, export, or delete their backups at any time from the Vault UI.
If your app is licensed under the Cryptographic Autonomy License (CAL), §4.2.1 requires that users can get a copy of their own data and the keys needed to use it. Flowsta Vault handles the key export - your app just needs to back up the user's own data (not the entire DHT).
See the Backup functions in the SDK reference for implementation details and examples.
Security
- User-custodied keys: Private keys never leave the user's device (Flowsta Vault)
- Purpose-specific signatures: Vault computes the signing payload itself, preventing apps from tricking users into signing arbitrary data
- User approval required: Every link request shows an approval dialog in Vault
- Verifiable on-chain: Anyone on your DHT can verify the attestation using Ed25519 public key cryptography
- One app agent, one identity: A second attestation from the same agent to another identity is a public claim that two people are one - the zome and the Vault both refuse it
- Revocable: The app-side agent can revoke a link on your DHT at any time (
revoke_link);revokeFlowstaIdentitytells the Vault so the app leaves the user's connected list - No shared infrastructure: Verification requires no API calls or shared DNAs - no one in between
Next Steps
- Building Holochain Apps - Step-by-step integration guide
- Identity Switching - What a linked app does when the Vault holds another identity
- SDK Reference - Full
@flowsta/holochaindocumentation - Backup Guide - CAL-compliant data backups
- IPC Endpoints - Raw IPC API for custom implementations
- Holochain Architecture - How Flowsta's Holochain infrastructure works