---
title: Security & Trust Model
description: How the OS contains code via capability-gated apps and mods plus package trust verdicts that combine to limit untrusted code.
updated: 2026-06-05
---

# Security & Trust Model

How the OS contains code it runs. Apps and mods are **capability-gated** and packages carry a **trust verdict**; the two combine so that untrusted code is contained even if it declares broad permissions. This page consolidates `webgpu-os/docs/PERMISSIONS_MODEL.md` and `PACKAGING.md`.

## Two enforcement layers

1. **Capabilities** — a package may only call a syscall if it *declared* the matching permission **and** the user/policy *granted* it.
2. **Trust verdict** — the result of verifying the package's integrity, signature, provenance, and scan risk. The verdict can override grants (e.g. block network egress regardless of declared permissions).

## Capability enforcement

```mermaid
flowchart LR
  launch[Desktop._launchPanel] --> guard[guardSyscalls\nkernel/Syscalls.js]
  guard --> req[kernel.permissions.require appId, cap]
  req -->|granted| run[run syscall]
  req -->|missing/denied| deny[throw or prompt]
  req --- store[(PermissionStore\npersisted grants)]
```

- A package declares `permissions` in its manifest.
- At launch, `Desktop._launchPanel` wraps the panel's syscalls with `guardSyscalls(kernel, appId, syscalls)`.
- Each guarded method calls `kernel.permissions.require(appId, cap)` before running; missing/denied capabilities throw or prompt per policy.
- `kernel/Permissions.js` resolves decisions; `PermissionStore` persists grants.

### Permission vocabulary

Capabilities are dotted strings. Declare only what you use — the consent prompt lists requested permissions.

| Namespace | Examples | Gated action |
| --- | --- | --- |
| `fs.*` | `fs.read`, `fs.write`, `fs.delete`, `fs.list` | virtual filesystem |
| `storage.*` | `storage.read`, `storage.write`, `cache.get`, `mount-pick` | OPFS / cache / mounts |
| `ipc.*` | `ipc.emit`, `ipc.on` | inter-app messaging |
| `gpu.*` | `gpu.getDevice` | raw GPU device |
| `ai.*` | `ai.infer` | model inference |
| `net.*` | `net.send`, `net.on` | network egress |
| `ui.*` | `ui.notify`, `ui.modal` | shell UI |
| `package-install`, `package-remove`, `patch-apply` | — | package/patch management |

## Trust verdicts → capability defaults

Installed packages carry a verdict (`PackageManager.getTrustProfile(appId)`):

| Verdict | Network egress | Notes |
| --- | --- | --- |
| `trusted` (root-chained) | as granted | full capability set available to grant |
| `pinned` (TOFU) | as granted | accepted by the user on first install |
| `untrusted` (self-signed, unpinned) | **no auto-grant** | restricted; must be explicitly granted |
| `quarantined` (integrity fail / high scan risk / key change) | **hard-blocked** | `guardSyscalls` throws on any `net.*` regardless of grants |

This is **default-deny egress**: it blunts credential exfiltration and worm C2 even if a malicious package declares `net.send`.

## The verification choke point

All installs route through `PackageManager.verifyAndAuthorize()` (Source: `webgpu-os/docs/ARCHITECTURE.md`):

```mermaid
flowchart LR
  integrity[Integrity\nhash check] --> trust[Trust\nroots + pinning]
  trust --> provenance[Provenance\nsigning lineage]
  provenance --> scan[Scan\nPackageScanner]
  scan --> policy[Policy]
  policy --> verdict[Verdict]
```

- **Ring-0 roots** live in `kernel/trust/roots.json`; `kernel/TrustStore.js` handles roots + publisher pinning.
- **Provenance** is checked by `kernel/ProvenanceChecker.js` and `kernel/SigningLineage.js`.
- **Scanning** is performed by `packages/PackageScanner.js`.

## Consent & trust-on-first-use (TOFU)

- First install of a self-signed package prompts with: **publisher fingerprint**, **trust verdict**, **scan risk**, and **requested permissions**.
- Accepting **pins** the publisher fingerprint (`TrustStore.pin`).
- **Anti-takeover:** if the same publisher later presents a *different* fingerprint, the install is flagged (`publisherChanged`) and requires explicit re-consent — defending against `event-stream`-style account takeover.
- The shell can register a rich modal via `packageManager.setConsentHandler(fn)`; otherwise a `confirm()` fallback is used.

## Install-time execution policy

Packages run **no install scripts**. Code executes only when the app is opened (its `mount()`), inside the per-app sandbox (`storage/AppSandbox.js`) with guarded syscalls. There is no `postinstall`/`.pth`-style hook — the top real-world infection vector is removed by design.

## Boot-time self-audit

Boot with `?securityAudit` (or `window.__DEV__ = true`) to run:

- `auditSyscallGuards(kernel)` — reports guarded / open / unguarded syscalls.
- `auditCapabilityMap()` — detects capability-map drift (unclassified or stale entries).
- `runSecurityDoctor(kernel)` — full posture report; also exposed as `window.securityDoctor()`.

## Audit logs

- Package lifecycle → `/os/logs/packages.log`; updates → `/os/logs/updates.log`.
- Per-package trust metadata (`trustVerdict`, `scanRisk`, `provenanceLevel`, `publisherFingerprint`) is stored in the package registry.

## See also

- WebGPU OS **API Reference** — `kernel/Permissions`, `kernel/TrustStore`, `kernel/Syscalls`, `packages/PackageManager`, `packages/PackageVerifier`.
- App manifest fields — [WebGPU OS Architecture](../webgpu-os/architecture.md).
