chain-studio/assets/docs/security.md
flemming-it 7ff4fda2a3
Some checks failed
Security / Security check (push) Failing after 2s
refactor(brand): rename F∆I -> Ch∆In + hub binary fai -> chain
Studio follows the platform rename: product branding F∆I -> Ch∆In in UI
strings, command examples fai -> chain, and — critically — the spawned
hub binary path ~/.fai/bin/fai -> ~/.fai/bin/chain so Studio launches
the renamed binary. The fai_* Dart identifiers (FaiLog, widget files,
the generated SDK) stay = vendor/internal namespace. flutter analyze:
no issues.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-06-15 16:22:22 +02:00

2.3 KiB

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

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:

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.