# Sandbox model

Every Ch∆In module runs inside a WebAssembly sandbox with no
ambient access to the host. The module can do exactly what it
declares in its `module.yaml` — and nothing else.

## What a module declares

```yaml
permissions:
  - net: api.openai.com         # outbound HTTPS only
  - net: 127.0.0.1:11434         # local Ollama
  - fs.read: /var/lib/fai/in     # read-only directory
  - fs.write: /var/lib/fai/out   # writable directory
  - env: OPENAI_API_KEY          # one specific env var
  - hub: invoke                  # call other modules
```

Anything not on this list is impossible at runtime. A module
that forgets to ask for `net:` cannot reach the network at all.
The hub's `WasiCtxBuilder` wires only the declared preopens
and env vars — there is no escape hatch.

## What the operator controls

Operators add their own ceiling on top in `~/.fai/config.yaml`:

```yaml
security:
  module_allowlist:               # only these modules may load
    - "text.*"
  module_denylist:                # never load these
    - "experimental.*"
  max_permissions:                # cap requests per scope
    net: ["api.openai.com"]
    fs.read: ["/var/lib/fai"]
  require_signatures: true        # reject unsigned bundles
  require_sbom: true              # reject bundles without CycloneDX
```

Modules whose declared permissions exceed the operator's
ceiling fail to load with an explicit error. The operator
ceiling is the second gate; the module's own declaration is
the first.

## What you'll see in this app

- The **Store** detail sheet for an installed module shows
  the declared permissions list with icons (`net:` globe,
  `fs.read:` folder, `fs.write:` edit, `env:` terminal).
- The **Doctor** page summary tile shows total module count
  and capability count.
- A **flow run** that violates a permission fails with a
  named error (`PermissionDenied: net:example.com`) in the
  audit log — visible on the Audit page.

## What we don't have

- No process-level isolation between modules. They all run
  inside one hub process. A WASM trap escalates to a flow-step
  failure, not a hub crash, but heap exhaustion in one module
  can pressure the others. Pooled instance mode is on the
  roadmap.
- No live policy backend (OPA-style runtime decisions).
  Permission lists are static at module-load time. Customer
  -driven; deferred until a real deployment needs it.
