Developer Guide
Add document signing to your app. Two integration paths depending on your platform:
| Path | For | SDK | How It Signs |
|---|---|---|---|
| Web Apps (OAuth) | Websites using "Sign in with Flowsta" | @flowsta/auth | API call — server signs via conductor |
| Desktop Apps (Vault IPC) | Holochain or Tauri apps running alongside Vault | @flowsta/holochain | IPC call — Vault signs locally, user approves |
Prerequisites
- Register your app at dev.flowsta.com to get a
clientId - Choose your SDK:
- Web apps:
npm install @flowsta/auth - Desktop Holochain apps:
npm install @flowsta/holochain
- Web apps:
Web Apps (OAuth)
Setup
import { FlowstaAuth, hashFile } from '@flowsta/auth';
const flowsta = new FlowstaAuth({
clientId: 'your_client_id',
redirectUri: 'https://your-app.com/callback',
scopes: ['profile', 'sign'], // Request signing permission
});The sign scope means the user will see "This app wants to sign files on your behalf" in the consent screen.
Sign a File
// 1. Hash the file client-side (never uploaded)
const hash = await hashFile(file);
// 2. Sign the hash
const result = await flowsta.signFile({
fileHash: hash,
intent: 'authorship',
contentRights: {
license: 'cc-by',
aiTraining: 'not_allowed',
contactPreference: 'allow_contact_requests',
},
});
// 3. Use the result
console.log(result.action_hash); // DHT record reference
console.log(result.agent_pub_key); // Signer's public keyThe signature is committed to the Holochain DHT immediately. Anyone can verify it at flowsta.com/sign-it by dropping the same file.
Batch Sign Multiple Files
const hashes = await Promise.all(files.map(f => hashFile(f)));
const result = await flowsta.signBatch({
files: hashes.map(h => ({ fileHash: h })),
sharedMetadata: {
intent: 'authorship',
aiGeneration: 'none',
contentRights: {
license: 'all-rights-reserved',
aiTraining: 'not_allowed',
},
},
});
console.log(`Signed ${result.signed} files, ${result.failed} failed`);Maximum 100 files per batch. Each file gets its own signature on the DHT.
Verify a File
const result = await flowsta.verifyFile(hash);
if (result.count > 0) {
result.signatures.forEach(sig => {
console.log(`Signed by: ${sig.signer_did || sig.signer}`);
console.log(`License: ${sig.content_rights?.license}`);
console.log(`AI Training: ${sig.content_rights?.ai_training}`);
console.log(`Revoked: ${sig.revoked}`);
});
}Verification is a public endpoint — no authentication required. Authenticated requests with the verify scope get higher rate limits.
Check Content Rights
const rights = await flowsta.getContentRights(hash);
if (rights.signed) {
console.log(`${rights.signerCount} signer(s)`);
rights.rights.forEach(r => {
if (r.aiTraining === 'not_allowed') {
console.log('Do not use for AI training');
}
});
}OAuth Scopes
| Scope | Purpose | Required For |
|---|---|---|
sign | Sign files on behalf of the user | signFile(), signBatch(), revocation |
verify | Higher rate limits for verification | verifyFile(), getContentRights() (optional — public endpoints work without auth) |
Desktop Apps (Vault IPC)
For Holochain apps or Tauri desktop apps running alongside Flowsta Vault. Signing happens locally via IPC — the user approves in Vault.
Setup
npm install @flowsta/holochainCheck if Vault is Available
import { getSigningStatus } from '@flowsta/holochain';
const status = await getSigningStatus();
if (status.available) {
// Vault is running, unlocked, and ready to sign
showSignButton();
} else if (status.vaultRunning && !status.vaultUnlocked) {
showMessage('Please unlock Flowsta Vault to sign files');
} else {
showMessage('Install Flowsta Vault to sign files');
}Sign a File
import { signDocument } from '@flowsta/holochain';
const result = await signDocument({
clientId: 'your_client_id', // From dev.flowsta.com
appName: 'ArtStudio', // Shown in Vault approval dialog
fileHash: 'a7f3b9c1e2d4...', // SHA-256 hex (64 chars)
label: 'Illustration.png', // Optional: shown in approval dialog
intent: 'authorship',
contentRights: {
license: 'cc-by',
aiTraining: 'not_allowed',
contactPreference: 'allow_contact_requests',
},
});
console.log(result.signature); // Base64 Ed25519 signature
console.log(result.agentPubKey); // uhCAk... format
console.log(result.actionHash); // DHT action hashThe user sees an approval dialog in Vault showing your app name, the file label, and the metadata. If they approve, Vault signs the hash with their Ed25519 key and commits the signature to the local Holochain conductor. The signature gossips to the server DHT automatically.
Handle Errors
import { signDocument, VaultNotFoundError, VaultLockedError, UserDeniedError } from '@flowsta/holochain';
try {
const result = await signDocument({ ... });
} catch (e) {
if (e instanceof VaultNotFoundError) {
// Vault is not running — prompt user to open it
} else if (e instanceof VaultLockedError) {
// Vault is locked — prompt user to unlock
} else if (e instanceof UserDeniedError) {
// User rejected the signing request
}
}Key Differences from OAuth Path
| OAuth (Web) | Vault IPC (Desktop) | |
|---|---|---|
| Where signing happens | Server conductor | Local Vault conductor |
| User approval | Implicit (OAuth consent) | Explicit dialog per request |
| File integrity checks | Not available | Vault runs 6 checks automatically |
| Perceptual hashing | Not available | Vault generates automatically |
| Gossip delay | None (direct to server) | Minutes (gossip from local to server) |
| Offline support | No | Yes (signature stored locally, gossips when online) |
Shared Features
These work the same regardless of which path you use.
Deep Linking to Verification
Link directly to the verification page for a specific file:
https://flowsta.com/sign-it/?hash=a7f3b9c1e2d4...The page auto-verifies and shows results.
Rate Limits
| Endpoint | Limit |
|---|---|
Verification (GET /verify) | 30 requests/minute per IP |
File upload verification (POST /verify-file) | 30 requests/minute per IP |
Contact relay (POST /contact) | 3 requests/hour per IP |
Signing (POST /sign, /sign-batch) | Per developer tier |
Next Steps
- Verification API — Raw API endpoints for custom integrations
- SDK Reference — Complete method reference
- Content Rights — All content rights fields and values