flowchart TB
WB[Excel workbook]
Parent[Boardflare add-in host]
Child[Separate-origin notebook iframe]
Python[marimo / Pyodide]
XML[Workbook Custom XML]
AI[Boardflare AI endpoint\n+ configured gateway/provider]
API[User-chosen external APIs]
Packages[Package / CDN infrastructure]
WB -->|declared bf.inputs values| Parent
Parent -->|input capability| Child
Child --> Python
Python -->|bf.publish values/functions| Child
Child -->|outputs capability| Parent
Parent -->|BF.OUTPUT / BF.FUNCTION| WB
Parent <-->|saved source + opening mode| XML
Child -->|AI prompt + selected/supplied context| AI
Python -->|user-authored HTTP requests| API
Python -->|package loading/install| Packages
Security and Data Flow
This page documents the trust boundaries of the current Python for Excel implementation. It is intended for security reviewers, developers, and workbook authors handling sensitive data. For the runtime topology and lifecycle, see Architecture and Runtime.
The central rule is that Python for Excel has several distinct data paths. Avoid blanket claims such as “everything stays local” or “everything goes to the cloud.” The destination depends on which capability is used.
Data paths at a glance
| Activity | Data path | Main trust decision |
|---|---|---|
| Normal notebook calculation | Declared workbook values → Boardflare host → browser Python; published results return to Excel. | Trust the workbook and notebook source you execute. |
| Notebook AI authoring | User prompt + context selected/supplied for the AI action → Boardflare AI endpoint → configured AI gateway/provider. | Decide what notebook/workbook-derived context may leave the normal calculation path. |
| Notebook-authored API calls | Python/browser HTTP request → destination chosen by notebook code. | Trust the external service and its authentication/privacy boundary. |
| Package loading | Package/runtime metadata or wheels may be fetched into the browser runtime. | Trust the package source and dependency supply chain. |
| Workbook persistence/sharing | Executable notebook source is stored with the workbook. | Treat the file as a document that can contain code. |
Normal calculation, AI, arbitrary API calls, and package installation are different trust decisions even though they all originate from one notebook surface.
Normal workbook calculation
Workbook data reaches Python only through the declared workbook integration. bf.inputs() asks the host to resolve specific workbook references and materialize their values into the notebook runtime.
Notebook Python does not receive a general-purpose parent-window or Office.js workbook object from the public API. Published values and callable results return through the separate outputs capability and shared-runtime registry.
This architectural boundary reduces accidental access to unrelated workbook operations. It does not prove that authored Python or third-party packages are safe.
Separate-origin notebook boundary
The notebook executes inside an iframe served from a different network origin from the parent add-in in production.
The iframe currently uses:
sandbox="allow-scripts allow-same-origin"allow-same-origin is required by the stock marimo/Pyodide runtime, so the trust boundary does not come from sandbox tokens alone. Isolation depends on a distinct notebook origin plus explicit connection validation.
Production serves the child from https://notebook.boardflare.com. The child is configured with the exact parent origin and fails closed when that parent information is missing or malformed. Parent code is not supposed to read the child iframe document directly.
Capability handshake
Source, input, and outputs connections are explicit capabilities. Before transferring a MessagePort, the parent validates the connection identity.
sequenceDiagram
participant Child as Notebook child / Anywidget
participant Window as window.postMessage boundary
participant Parent as Boardflare parent
participant Port as Dedicated MessagePort
Child->>Window: capability connect request
Window->>Parent: event source + origin + transferred port
Parent->>Parent: Validate iframe window
Parent->>Parent: Validate exact child origin
Parent->>Parent: Validate protocol version
Parent->>Parent: Validate session ID + secure nonce
Parent->>Parent: Validate capability label
Parent->>Parent: Validate widget generation when required
Parent->>Parent: Require exactly one MessagePort
Parent-->>Child: Accept / transfer capability
Child<<->>Port: Ongoing capability traffic
Port<<->>Parent: Active session/generation only
The current checks include:
- the active iframe
Windowas the message source; - the exact configured child origin;
- protocol version
1; - the active secure session ID;
- the active secure nonce;
- exactly one transferred
MessagePort; - an allowed
source, input, or outputs capability label; - a non-empty generation identifier for input/output models.
Stale sessions and generations cannot claim current input snapshots, publication registries, or function results. These are implementation-level defense-in-depth controls, not notebook APIs.
Workbook persistence integrity
Excel stores notebook source and the saved Edit/App opening preference in Boardflare Custom XML. The current record includes a schema version, source, SHA-256 source digest, internal opening mode, and save timestamp.
A save writes the record, reads it back, and verifies the stored source/metadata. If verification fails, the host attempts to restore the prior stored value.
This is a persistence integrity check. It is not a digital signature and does not establish who authored or approved the notebook.
Current storage limits are:
| Item | Limit |
|---|---|
| Notebook source | 200,000 UTF-8 bytes |
| Complete notebook XML | 1,000,000 bytes |
See Getting Started: saving and reopening for the user workflow.
Uploaded source is staged before it is trusted as durable
Loading a Marimo .py file does not immediately replace the workbook copy.
stateDiagram-v2
Saved: Last verified workbook source
Staged: Uploaded source in memory
Saved --> Staged: Upload .py
Staged --> Saved: Save succeeds / becomes new verified source
Staged --> Saved: Startup fails + Restore saved notebook
The staged notebook starts in a fresh Edit session. Until Marimo Save reaches verified workbook persistence, Restore saved notebook can discard the staged replacement and return to the last workbook copy. This design reduces the chance that simply opening a bad .py file destroys the durable notebook source.
It does not make uploaded Python trustworthy: the staged source is still executable code once the notebook session starts it.
Executable workbook trust
Notebook source can execute when a workbook starts the notebook runtime, including cold starts driven by worksheet formulas and workbooks saved to open in App mode.
Treat a notebook-enabled workbook like any file that can contain executable logic:
- open it only from a source you trust;
- review package imports and external HTTP calls before using sensitive data;
- do not treat App mode as source protection;
- do not treat iframe isolation as proof that arbitrary Python is harmless;
- revalidate important workbooks after material source or dependency changes.
App mode changes presentation. It is not a sandbox or an authorization boundary.
Notebook AI eligibility
Notebook AI is separate from the normal calculation path. The parent enables the Marimo AI UI only when Office SSO provides both:
- an Office auth token; and
- a tenant ID that is not Microsoft’s personal-account tenant.
Personal Microsoft accounts, anonymous sessions, and unresolved identities receive the notebook with AI disabled. Normal bf.inputs(), bf.publish(), BF.OUTPUT(), and BF.FUNCTION() operation does not depend on this eligibility branch.
flowchart LR
SSO[Office SSO]
Check{Token + tenant ID?}
Personal{Personal tenant?}
Off[Notebook AI UI disabled]
On[Notebook AI UI enabled]
Marimo[Stock Marimo AI request]
Endpoint[Boardflare /api/ai/*]
Tenant[Server-side tenant check]
Gateway[Configured AI gateway/provider]
SSO --> Check
Check -->|No| Off
Check -->|Yes| Personal
Personal -->|Yes| Off
Personal -->|No| On
On --> Marimo
Marimo -->|Office SSO bearer token| Endpoint
Endpoint --> Tenant
Tenant -->|eligible| Gateway
Tenant -->|missing/personal| Off
The existing Office SSO bearer token is passed in memory into Marimo’s runtime auth configuration. The Boardflare AI endpoint independently requires the bearer token and checks the token payload’s tid before forwarding an eligible request.
Current server-side validation scope
The endpoint’s tenant check is intentionally a lightweight incremental control. It decodes the bearer token payload to inspect tid, but does not currently validate the token signature, issuer, audience, or expiry.
That limitation should be included in technical security reviews. The current control is an account-eligibility gate, not a complete server-side validation of Microsoft identity tokens.
Package loading and supply chain
The browser runtime can load packages from the Pyodide distribution and install compatible wheels using micropip. Package code or metadata may therefore be downloaded even when normal workbook calculation otherwise stays inside the browser runtime.
Package use adds two separate questions:
- Compatibility — does the package and its dependencies work in WebAssembly/browser Python?
- Trust — do you trust the package source, version, and transitive dependencies with the data available to the notebook?
Version important dependencies where practical and review package behavior for sensitive workflows. See Building Notebooks: packages and environment.
Excel versus the public browser demo
The public demo uses Univer as its spreadsheet host. It shares the Boardflare notebook and capability concepts, but its persistence and custom-function lifecycle differ from Excel.
Do not use the browser demo as evidence that Excel-specific controls such as Custom XML persistence, Office SSO AI eligibility, or shared-runtime cold start behave correctly. Validate those paths in the actual Excel add-in.
Threat-model boundary
The current design aims to keep spreadsheet capabilities explicit, scoped, and validated across the notebook iframe boundary. It does not promise that arbitrary Python, arbitrary third-party packages, arbitrary workbooks, or arbitrary external services are safe.
A security review should separately evaluate:
- runtime boundary controls — origins, session/nonce identity, capability labels, generations, and message ports;
- workbook/source trust — what Python code will execute and who supplied it;
- persistence integrity — whether the intended source was durably saved and verified;
- external data paths — AI, authored APIs, packages, and credentials;
- business-logic correctness — whether the analysis itself is appropriate for its intended decisions.
For the detailed lifecycle behind these controls, see Architecture and Runtime.