> For the complete documentation index, see [llms.txt](https://basedapp.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://basedapp.gitbook.io/docs/integrations/mini-apps-platform/08-security-and-sandbox.md).

# Security & sandbox

This document is the threat model and the set of guarantees the platform makes. Every other doc references this one for security-affecting decisions.

## Threat model

Adversaries we design against, in roughly increasing order of capability:

| #  | Adversary                       | Typical motivation                                       | Capability                                                                       |
| -- | ------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------- |
| T1 | Buggy first-party mini-app      | n/a                                                      | Crashes, leaks.                                                                  |
| T2 | Sloppy third-party mini-app     | n/a                                                      | Mishandles user data, leaks via analytics.                                       |
| T3 | Adversarial partner             | Profit, account takeover, drain wallets                  | Full control of the JS bundle they ship; access to whatever the manifest grants. |
| T4 | Compromised CDN / MITM          | Inject malicious updates                                 | Can replace bundles in transit if signing is weak.                               |
| T5 | Compromised partner credentials | Push a malicious update to a previously-trusted mini-app | Can sign a bundle as a known good partner.                                       |
| T6 | Cross-mini-app attacker         | Read another mini-app's storage, impersonate user        | Has a foothold via T3 or T5.                                                     |
| T7 | Sandbox escape                  | Native code execution, key extraction                    | Most-capable.                                                                    |

Our defenses must hold against T1–T6 by design. T7 is mitigated through defense-in-depth and a kill switch.

## Guarantees the platform makes

The platform provides these guarantees to users (and to ourselves, for review):

| #   | Guarantee                                                                         | Mechanism                                                                                                                   |
| --- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| G1  | A mini-app cannot read or write a private key, mnemonic, or session token.        | The bridge does not expose them. Signing operations return signed payloads only.                                            |
| G2  | A mini-app cannot make a network request to a domain not in its manifest.         | Bridge validates URL host against `permissions.network`; unmatched → reject.                                                |
| G3  | A mini-app cannot read another mini-app's storage.                                | Per-mini-app MMKV namespace; storage operations carry mini-app id.                                                          |
| G4  | A mini-app cannot launch arbitrary deep links or other mini-apps.                 | Both `nav.openDeepLink` and `nav.openMiniApp` are allowlisted in manifest, with target consent for inter-mini-app launches. |
| G5  | A mini-app cannot impersonate the user without explicit, per-action confirmation. | Every signing/payment shows a host-rendered confirmation sheet outside mini-app control.                                    |
| G6  | A mini-app cannot crash or hang the host UI.                                      | Runs in a separate Dart isolate with CPU/memory limits and a watchdog.                                                      |
| G7  | A mini-app cannot ship code that wasn't validated and signed at install time.     | Bundles are signature-verified at install + boot; `eval()` and dynamic `import()` are banned.                               |
| G8  | A mini-app cannot persist beyond its declared lifetime/quota.                     | Storage quotas; eviction policy; explicit user-revocable permissions.                                                       |
| G9  | A mini-app cannot identify the same user across mini-apps.                        | `auth.userId` returns `HMAC(globalUserId, miniAppId)`.                                                                      |
| G10 | A revoked or kill-listed mini-app cannot run.                                     | Kill list checked at boot; cached bundles invalidated; isolate refuses to spawn.                                            |

These guarantees are normative. A change that would weaken one requires explicit platform-level review.

## Sandbox boundaries

The sandbox has four boundaries, arranged outward from the JS heap:

```mermaid
flowchart TB
    subgraph B1["B1 — Language-level: QuickJS heap (mini-app code)"]
        B2["B2 — API surface: globalThis allowlist<br/>(based, console, setTimeout, …)"]
    end
    B3["B3 — Capability gating: Bridge dispatcher (Dart)<br/>• schema validation<br/>• permission check<br/>• rate limiting"]
    B4["B4 — Implementation-level: BasedMiniAppHost implementations<br/>(still defensive — never trusts caller)"]
    B1 -- "JSON over SendPort" --> B3
    B3 --> B4
```

### B1 — Language-level

* QuickJS interpreter, no JIT, no native code generation.
* No `eval`, no `new Function`, no dynamic `import` (banned at bundle build time and re-checked at install).
* Strict mode enforced on all bundles.

### B2 — API surface

* The JS context starts with a minimal `globalThis`. We delete `globalThis.process`, `globalThis.require`, and unbind any QuickJS-specific globals.
* The only added globals are: `based`, `console`, `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, plus standard ECMAScript globals (`Promise`, `JSON`, `Math`, `Date`, `Map`, `Set`, etc.).
* `console.*` is rerouted to a buffered, telemetry-bounded channel; partners see logs in their dashboards.

### B3 — Capability gating

The bridge is the most-tested boundary. Every host method:

* Has its required permission encoded in metadata, checked before dispatch.
* Has its argument schema validated.
* Has rate limits applied.
* Logs any rejection with sufficient detail for forensic review (mini-app id, version, method, reason).

### B4 — Implementation-level

Even though calls reaching this layer are validated, the implementations remain defensive:

* Never trust the caller's data shape — re-parse.
* Never assume the caller has the permission claimed — re-check at the implementation when the cost is meaningful.
* Use existing host services (`ActiveSignerService`, `BasedHttp` interceptors) so that mini-app calls reuse the same hardened paths as host calls.

## Signing — the highest-stakes flow

The signing flow is the most security-critical part of the platform. It earns its own subsection because the threat is direct: a malicious mini-app whose only goal is to drain the wallet.

### Invariants

1. **Mini-app code never sees a private key, ever.** The signer object is held only by `ActiveSignerService` in the host process.
2. **Every signing operation shows a host-rendered confirmation sheet.** The sheet is composed entirely by the host using `based_ui` widgets — the mini-app cannot inject content into it.
3. **The sheet always shows three things, prominently:**
   * The mini-app `name` and `icon` from the manifest (so the user knows who is asking).
   * The `reason` string passed to the call (truncated to 200 chars; HTML-stripped).
   * A decoded summary of the transaction (recipient, amount, function name when known, gas).
4. **Unknown contracts get a warning banner.** The sheet shows "This mini-app is requesting a raw transaction we can't decode. Approve only if you trust the developer." with the destination address and a hex-data preview.
5. **No trust escalation.** A user cannot "always allow signing for this mini-app" — every signing call requires confirmation. (This is non-negotiable.)
6. **No double-spend on confirmation.** The throttle from `TradingAgentService.canApproveAgent` (10 s) applies. A second sheet for the same mini-app within the throttle window is queued, not auto-approved.

### Network broadcasting

After signing, the mini-app receives the signed payload and is expected to broadcast it via `based.http.post(...)`. The broadcast endpoint must be in `permissions.network`. The host does *not* broadcast on the mini-app's behalf; we want the partner backend in the loop (it usually wants to log + relay).

There is a future host-broadcast option for chains where MEV protection or private-relay broadcast matters. Out of scope for v1.

## Bundle integrity

Bundles must be **signed by the platform** to install. This is non-negotiable for production.

* Algorithm: Ed25519.
* Signing key: managed by the platform team in HSM-backed storage; rotated annually.
* The signature covers a content hash of the entire bundle (manifest + JS + assets), built deterministically.
* Public keys are pinned in the host binary; rotation requires a host release.
* Boot-time re-verification: the cached bundle's signature is re-checked on every boot, not just at install. This catches local-disk tampering on rooted devices.

Partner-side workflow:

1. Partner uploads built bundle to platform's CI.
2. Platform CI runs static checks (eval-free, schema-valid, etc.).
3. On approval, platform signs and publishes the signed bundle.

For dev mode, signature verification is bypassed but a dev-mode banner is shown across the mini-app continuously, and no signing-class permissions can be used against mainnet.

## Kill list

The platform maintains a centrally-published kill list:

* Endpoint: `GET https://miniapps.based.app/v1/killlist`
* Polled by host on app foreground (with a backoff and cache).
* Kill entries: `{ "id": "com.partner.x", "reason": "abuse", "fromVersion": "1.0.0" }`.
* Match: a mini-app whose `id` matches and whose `version >= fromVersion` will refuse to run.
* Boot path always re-checks the cached kill list, then opportunistically refreshes.

A killed mini-app shows a host-rendered explanatory screen instead of mounting.

## Cross-mini-app interactions

When mini-app A launches mini-app B:

* Both must declare the relationship: A in `permissions.openMiniApps`, B in `permissions.acceptsLaunchFrom`.
* Params crossing the boundary go through JSON serialization (no shared object refs).
* B has *no* implicit access to A's storage, A's user id (B sees its own pseudonymous id), or A's signing permissions.
* Returned results follow the same path back: serialized, validated.

This makes inter-mini-app calls effectively the same trust boundary as a network call — no implicit transitive trust.

## App Store / Play Store compliance

We are running interpreted code that is downloaded after install. The relevant rules:

* **Apple guideline 4.7** ("HTML5, JavaScript, CSS apps"): allowed, provided the code only enables features and functionality consistent with the developer-account-listed app, and does not provide a store, change the app's primary purpose, or create platforms within platforms.
  * We satisfy this because mini-apps are scoped to the host app's domain (crypto/finance/commerce as listed by Based), and do not constitute a general app store.
  * We must still review every mini-app for appropriateness; this is a platform team responsibility.
* **Apple guideline 2.5.2** ("Apps must be self-contained… should not download, install, or execute code that introduces or changes features or functionality of the app, including other apps"): we comply because mini-apps execute *interpreted* code in a contained sandbox and cannot change host features.
* **Apple guideline 4.7 (sub-rule)**: mini-apps may not contain links to alternative payment systems or attempt to bypass IAP for digital goods. The platform review enforces this.

For Play Store (Google), the equivalent rules are looser; our compliance posture is well within their requirements.

## Privacy

* **Per-mini-app pseudonymous user id.** No mini-app sees the global user id; cross-mini-app correlation requires partner cooperation.
* **No silent device fingerprinting.** Mini-apps do not see device identifiers, advertising id, user agent, screen dimensions beyond layout-relevant abstractions, or IP (the host's HTTP client masks the source).
* **Analytics events are opt-in per-event-name.** A mini-app's `analytics.track` events go through a host filter that drops events whose names contain forbidden tokens (configured patterns).

## Defense-in-depth checklist (for review)

Each item is a tripwire we maintain:

* Static analyzer scans bundles for `eval`, `new Function`, `import(`, `Function(` (false-positive whitelist exists).
* Bundle hash + signature pinned at install; re-verified at boot.
* Kill list polled at app foreground, every cold boot, and on failed verification.
* Permission grants displayed at first launch and revocable from settings.
* Per-action confirmation sheets for signing and external nav.
* Bridge rejection telemetry alerted on anomaly (a mini-app suddenly producing 1000× more `permission_denied` errors).
* Storage quota enforced at write; bundle size enforced at install.
* Network deny-by-default; explicit allowlist required.
* Inter-mini-app launches require both-sides consent.
* Cookie jar isolated per mini-app; no shared session.
* HTTP `Authorization` headers stripped unless explicitly granted.

## Unresolved security questions

These are tracked in `12-open-questions.md` but flagged here:

* Should a mini-app's per-mini-app pseudonymous id be revealed to its own backend, or only to the local mini-app code? (Trade-off: usability vs. cross-app correlation via partner backend.)
* How do we handle wallet-rotation: a user changes wallet, what happens to a mini-app currently running with that wallet's permissions?
* Should we have a "trusted partner" tier with relaxed limits (higher heap, larger storage, additional permissions like WS), and how is that earned?

Read [`09-distribution-and-updates.md`](/docs/integrations/mini-apps-platform/09-distribution-and-updates.md) next.
