This document explains FRAME's security model, including capability-based permissions, sandbox execution, cryptographic receipts, and identity isolation.
FRAME enforces security through multiple layers:
- Capability-Based Permissions: dApps only access granted capabilities
- Sandbox Execution: dApps run in isolated environment
- Cryptographic Receipts: All state transitions are signed
- Identity Isolation: Each identity has separate storage and chains
- Code Hash Verification: dApp code integrity verified
- State Root Verification: State integrity verified
FRAME uses a capability-based permission system inspired by UCAN (User-Controlled Authorization Networks).
Capabilities are defined in a frozen taxonomy:
Location: ui/runtime/engine.js → CAPABILITY_SCHEMA
Example Capabilities:
storage.read: Read from scoped storagestorage.write: Write to scoped storagewallet.read: Read wallet balancewallet.send: Send paymentsidentity.read: Read identity informationnetwork.request: Make HTTP requests
Frozen Schema: Capability taxonomy cannot be modified after boot.
dApps declare required capabilities in their manifest:
{
"name": "Wallet",
"id": "wallet",
"intents": ["wallet.balance", "wallet.send"],
"capabilities": ["wallet.read", "wallet.send", "storage.read"]
}Capabilities are granted per identity and per dApp:
Storage Key: permissions:<identity>
Structure:
{
"wallet": ["wallet.read", "wallet.send"],
"notes": ["storage.read", "storage.write"]
}Grant Process:
- User receives permission request
- User approves or denies
- Grant stored in
permissions:<identity> - Runtime checks grants before execution
Capabilities are enforced at multiple levels:
1. Permission Check (before execution):
if (!hasPermission(identity, dappId, capability)) {
return { type: 'permission_request', ... };
}2. Capability Guard (during execution):
function capabilityGuard(capability, fn) {
return function(...args) {
logCapabilityUse(capability);
if (!hasPermission(identity, dappId, capability)) {
throw new Error(`Capability ${capability} not granted`);
}
return fn(...args);
};
}3. Scoped API (only granted capabilities):
function buildScopedApi(dappId, grantedCaps) {
var api = {};
if (grantedCaps.indexOf('storage.read') !== -1) {
api.storage = { read: capabilityGuard('storage.read', storageRead) };
}
return api;
}Used capabilities are logged during execution:
Location: _executionCapLog in engine.js
Process:
- Each capability use logged
- Logged capabilities included in receipt as
capabilitiesUsed - Receipt certifies which capabilities were actually used
Receipt Field:
{
capabilitiesDeclared: ["wallet.read", "wallet.send"],
capabilitiesUsed: ["wallet.read"] // Only read was used
}dApps execute in a deterministic sandbox that prevents unauthorized access:
Time Freezing:
Date.now()frozen to execution timestamp- Prevents time-dependent nondeterminism
Random Seeding:
Math.random()seeded deterministically- Prevents random-dependent nondeterminism
Async Blocking:
fetch(),setTimeout(),WebSocketblocked- Prevents network-dependent nondeterminism
dApps only receive scoped APIs with granted capabilities:
Example:
// dApp receives:
{
storage: {
read: (key) => { /* capability guard */ }
},
wallet: {
getBalance: () => { /* capability guard */ }
}
}
// dApp cannot access:
window.localStorage // Blocked
window.fetch // Blocked
Date.now() // Frozen
Math.random() // SeededdApps are loaded via dynamic import:
var dAppModule = await import(`./dapps/${dappId}/index.js`);
var result = await dAppModule.run(intent, scopedApi);Isolation Guarantees:
- No direct access to global scope
- No access to runtime internals
- Only scoped API available
All state transitions are cryptographically signed:
Algorithm: Ed25519 (Edwards-curve Digital Signature Algorithm)
Process:
- Build receipt object
- Compute signable payload (excludes
receiptHash,signature) - Hash signable payload →
receiptHash - Sign
receiptHashwith identity's private key - Include signature and public key in receipt
Verification:
var signable = receiptSignablePayload(receipt);
var computedHash = await sha256(JSON.stringify(signable));
var isValid = await verifySignature(
receipt.signature,
receipt.publicKey,
computedHash
);Receipts form a hash chain:
Structure:
Receipt 0: previousReceiptHash = null
Receipt 1: previousReceiptHash = hash(Receipt 0)
Receipt 2: previousReceiptHash = hash(Receipt 1)
...
Security:
- Tampering detected via hash mismatch
- Chain integrity verified on replay
- Cannot modify past receipts without breaking chain
Receipts are verified during replay:
Checks:
- Receipt structure matches expected fields
receiptHashmatches computed hash- Signature valid against public key
previousReceiptHashmatches previous receiptinputHashmatchesinputPayloadresultHashmatches execution result
Each identity has isolated storage and receipt chains:
Per-Identity Storage:
- Receipt chain:
chain_<identity>.log - Capability grants:
permissions:<identity> - Application state:
storage:<key>(scoped by identity)
Isolation:
- Identities cannot access each other's storage
- Identities cannot access each other's receipt chains
- Identities cannot access each other's capability grants
Key Generation: Ed25519 keypairs generated per identity
Storage: Keys encrypted and stored in identity vault
Usage: Keys used for receipt signing and verification
Isolation: Keys never leave the Rust backend
dApp code integrity is verified:
Process:
- Load dApp source code
- Compute SHA-256 hash of source
- Store hash in state root
State Root Entry:
{
installedDApps: [
{
id: "wallet",
manifest: {...},
codeHash: "abc123..." // SHA-256 of source
}
]
}Before Execution:
- Compute current dApp code hash
- Compare to hash in state root
- Return error if mismatch
Security:
- Detects code tampering
- Prevents execution of modified code
- Ensures deterministic replay
State integrity is verified via state root:
Components:
- Identity public key
- Installed dApps (with code hashes)
- Storage state (canonicalized)
- Receipt chain commitment
Computation:
var stateRoot = {
version: STATE_ROOT_VERSION,
capabilityVersion: CAPABILITY_VERSION,
identityPublicKey: publicKey,
installedDApps: dapps, // Sorted by id
storage: storage, // Sorted keys
receiptChainCommitment: commitment
};
var hash = sha256(JSON.stringify(canonicalize(stateRoot)));Boot-Time State Root:
- Computed at boot
- Stored as
_bootStateRoot - Used for integrity verification
Verification:
- Before each execution, verify current state root matches boot state root
- If mismatch, enter safe mode
- Prevents silent state corruption
FRAME detects tampering through multiple mechanisms:
Detection:
- Hash chain verification
- Signature verification
- Receipt structure validation
Response:
- Replay fails if chain broken
- State root mismatch detected
- Safe mode triggered
Detection:
- Code hash verification
- State root code hash comparison
Response:
- Execution blocked if hash mismatch
- Safe mode triggered
Detection:
- State root verification
- Integrity lock check
Response:
- Safe mode triggered
- Execution blocked
FRAME enters safe mode when integrity violations are detected:
- State root mismatch
- Code hash mismatch
- Receipt chain broken
- Runtime code tampering
- Execution blocked
- Only system intents allowed
- Recovery mode enabled
- User notified
- State reconstruction from receipt chain
- Code hash verification
- State root recomputation
- Integrity restoration
FRAME provides the following security guarantees:
- Capability Isolation: dApps only access granted capabilities
- Sandbox Execution: dApps cannot escape sandbox
- Cryptographic Integrity: All state transitions signed
- Identity Isolation: Identities cannot access each other's data
- Code Integrity: dApp code verified via hashes
- State Integrity: State verified via state root
- Tamper Detection: Tampering detected and prevented
These guarantees ensure that FRAME provides a secure execution environment for applications and agents.