Overview
The Security Manager (SM) provides comprehensive security features for GenosDB, including:
- Identity Management: WebAuthn and mnemonic-based authentication
- Role-Based Access Control (RBAC): Configurable role hierarchies with permissions
- Access Control Lists (ACLs): Node-level permissions
- P2P Operation Security: Cryptographic signing and verification
- Local Data Encryption: User-specific encrypted storage
Enabling the Security Manager
import { gdb } from "genosdb"
const db = await gdb("my-db", {
rtc: true, // Required for SM
sm: {
superAdmins: ["0x1234...", "0x5678..."], // MANDATORY
customRoles: { /* optional */ },
acls: true // Enable node-level ACLs
}
})
Required Configuration: The superAdmins array is mandatory when enabling SM. The module will not initialize without it.
The Security Manager requires rtc: true because it relies on the Real-Time Communication module for P2P security middleware, signing, and verifying operations between peers.
Identity Management
Generate New Identity
const newIdentity = await db.sm.startNewUserRegistration()
if (newIdentity) {
console.log("Address:", newIdentity.address)
console.log("Mnemonic:", newIdentity.mnemonic)
console.log("Private Key:", newIdentity.privateKey)
// CRITICAL: User must save the mnemonic phrase securely
}
Protect Identity with WebAuthn
// After generating identity, protect it with biometrics/security key
const address = await db.sm.protectCurrentIdentityWithWebAuthn(
newIdentity.privateKey
)
if (address) {
console.log(`Identity protected: ${address}`)
}
Login with WebAuthn
// Requires prior WebAuthn registration
const address = await db.sm.loginCurrentUserWithWebAuthn()
if (address) {
console.log(`Logged in as: ${address}`)
}
Login with Mnemonic
const mnemonic = "your twelve word secret recovery phrase..."
const identity = await db.sm.loginOrRecoverUserWithMnemonic(mnemonic)
if (identity) {
console.log(`Logged in: ${identity.address}`)
}
Logout
await db.sm.clearSecurity()
console.log("User logged out")
Silent WebAuthn Resume
The SM automatically attempts silent session resumption on initialization:
- No biometric prompt on page reload
- Requires previous WebAuthn session on this browser
- Cleared on logout with
clearSecurity()
// Initialize - SM handles silent resume automatically
const db = await gdb("my-db", {
rtc: true,
sm: { superAdmins: ["0x..."] }
})
// Check if already logged in
if (db.sm.isSecurityActive()) {
console.log("Already logged in:", db.sm.getActiveEthAddress())
}
State Management
Security State Callback
db.sm.setSecurityStateChangeCallback((state) => {
console.log("Security state:", state)
if (state.isActive) {
// User is logged in
console.log("User:", state.abbrAddr) // "0x1234...abcd"
console.log("WebAuthn:", state.isWebAuthnProtected)
} else {
// User is logged out
console.log("WebAuthn available:", state.hasWebAuthnHardwareRegistration)
}
})
State Properties:
True if user session is active with signing capabilities
Ethereum address of active user, or null
Abbreviated address for display (e.g., “0x1234…abcd”)
True if current session uses WebAuthn
True if new identity generated but not yet secured
hasWebAuthnHardwareRegistration
True if WebAuthn credential exists for this browser
Helper Methods
// Get active user address
const address = db.sm.getActiveEthAddress()
// Check if logged in
const isLoggedIn = db.sm.isSecurityActive()
// Check if WebAuthn session
const isWebAuthn = db.sm.isCurrentSessionProtectedByWebAuthn()
// Check if WebAuthn available
const hasWebAuthn = db.sm.hasExistingWebAuthnRegistration()
// Get mnemonic (only for new/recovered identities)
const mnemonic = db.sm.getMnemonicForDisplayAfterRegistrationOrRecovery()
// Abbreviate address for display
const short = db.sm.abbrAddr("0x1234567890123456789012345678901234567890")
// Returns: "0x1234...7890"
Role-Based Access Control (RBAC)
Default Roles
{
superadmin: { can: ["assignRole", "deleteAny"], inherits: ["admin"] },
admin: { can: ["delete"], inherits: ["manager"] },
manager: { can: ["publish"], inherits: ["user"] },
user: { can: ["write", "link", "sync"], inherits: ["guest"] },
guest: { can: ["read", "sync"] }
}
Custom Roles
const db = await gdb("my-db", {
rtc: true,
sm: {
superAdmins: ["0x..."],
customRoles: {
editor: { can: ["write", "publish"], inherits: ["user"] },
viewer: { can: ["read"], inherits: ["guest"] }
}
}
})
Assign Roles
// Assign role to user (requires 'assignRole' permission)
await db.sm.assignRole(
"0xTargetUserAddress...",
"editor",
new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) // Expires in 30 days (optional)
)
Check Permissions
try {
const userAddress = await db.sm.executeWithPermission("delete")
console.log(`User ${userAddress} has delete permission`)
// Proceed with operation
await db.remove(nodeId)
} catch (error) {
console.error("Permission denied:", error.message)
}
Secure Data Storage
Encrypted Put/Get
// Store encrypted data (tied to user's identity)
const id = await db.sm.put(
{ secret: "confidential data" },
"my-secret-note"
)
// Retrieve and decrypt
const { result: node } = await db.sm.get("my-secret-note")
if (node && node.decrypted) {
console.log("Decrypted:", node.value)
} else {
console.warn("Could not decrypt (not owner or not logged in)")
}
Automatic Encryption: Data stored with db.sm.put() is automatically encrypted using a key derived from the active user’s Ethereum identity.
Access Control Lists (ACLs)
For node-level permissions, enable ACLs:
const db = await gdb("my-db", {
rtc: true,
sm: {
superAdmins: ["0x..."],
acls: true // Enable ACL module
}
})
See ACLs Module for detailed documentation.
P2P Security
When SM is enabled, all P2P operations are:
- Signed: Outgoing operations are cryptographically signed
- Verified: Incoming operations are signature-validated
- Permission-Checked: Operations are validated against RBAC/ACL rules
- Rejected if Invalid: Unauthorized operations are discarded
// This operation is automatically signed and verified
await db.put({ type: "Task", text: "Secure task" })
// Other peers verify:
// - Valid signature from known user
// - User has 'write' permission
// - Operation applies only if both checks pass
Best Practices
Registration Flow
class AuthManager {
async register() {
// 1. Generate identity
const identity = await db.sm.startNewUserRegistration()
if (!identity) throw new Error("Failed to generate identity")
// 2. Show mnemonic to user (CRITICAL)
this.displayMnemonic(identity.mnemonic)
// 3. User confirms they saved it
await this.waitForUserConfirmation()
// 4. Protect with WebAuthn
const address = await db.sm.protectCurrentIdentityWithWebAuthn(
identity.privateKey
)
if (address) {
console.log("Registration complete:", address)
}
}
displayMnemonic(mnemonic) {
alert(`SAVE THIS PHRASE: ${mnemonic}`)
// In production, use a proper UI with copy button
}
}
Login Flow
class AuthManager {
async login() {
// Try WebAuthn first (if available)
if (db.sm.hasExistingWebAuthnRegistration()) {
const address = await db.sm.loginCurrentUserWithWebAuthn()
if (address) return address
}
// Fall back to mnemonic
const mnemonic = prompt("Enter your recovery phrase:")
const identity = await db.sm.loginOrRecoverUserWithMnemonic(mnemonic)
if (identity) {
return identity.address
}
throw new Error("Login failed")
}
}
UI State Management
db.sm.setSecurityStateChangeCallback((state) => {
// Update UI based on state
if (state.isActive) {
showLoggedInView(state.abbrAddr)
hideLoginButtons()
} else {
showLoginView()
// Show/hide WebAuthn button
if (state.hasWebAuthnHardwareRegistration) {
showWebAuthnLoginButton()
} else {
hideWebAuthnLoginButton()
}
}
})
API Reference
For complete method signatures and advanced usage, refer to the SM module documentation in the GitHub repository.