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):
functioncapabilityGuard(capability,fn){returnfunction(...args){logCapabilityUse(capability);if(!hasPermission(identity,dappId,capability)){thrownewError(`Capability ${capability} not granted`);}returnfn(...args);};}3. Scoped API (only granted capabilities):
functionbuildScopedApi(dappId,grantedCaps){varapi={};if(grantedCaps.indexOf('storage.read')!==-1){api.storage={read: capabilityGuard('storage.read',storageRead)};}returnapi;}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// Blockedwindow.fetch// BlockedDate.now()// FrozenMath.random()// SeededdApps are loaded via dynamic import:
vardAppModule=awaitimport(`./dapps/${dappId}/index.js`);varresult=awaitdAppModule.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:
varsignable=receiptSignablePayload(receipt);varcomputedHash=awaitsha256(JSON.stringify(signable));varisValid=awaitverifySignature(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:
varstateRoot={version: STATE_ROOT_VERSION,capabilityVersion: CAPABILITY_VERSION,identityPublicKey: publicKey,installedDApps: dapps,// Sorted by idstorage: storage,// Sorted keysreceiptChainCommitment: commitment};varhash=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.