Embedding OpenDoc

The same editor, embedded twice on one page: once in a plain <iframe> a host wires itself, and once through the <opendoc-editor> custom element. Both resolve their capabilities from one authority before the frame's first navigation, so neither can be talked out of the mode it was given — and each panel prints the code a host writes to get it, which is the only real difference between them.

A plain iframe <iframe>

The host builds the URL and the sandbox by hand. Every parameter below is a decision this page had to make for itself.


          

  • —
  • — of — capabilities
    What the frame is given
    Frame sandbox
    —

    The editor mounts here

    Nothing boots until you ask. Each editor holds a WebAssembly instance of roughly 100 MB, and a page that starts two of them on load costs a visitor 200 MB for a demo they may not have wanted — the same reason the home page's live demo is click-to-activate.

    The custom element <opendoc-editor>

    The same configuration, declared. The element resolves the role and both withhold lists, derives the iframe sandbox from them, and reports all of it back on opendoc-capabilities.

    
              

    • —
    • — of — capabilities
      What the frame is given
      Frame sandbox
      —

      The editor mounts here

      Nothing boots until you ask. Each editor holds a WebAssembly instance of roughly 100 MB, and a page that starts two of them on load costs a visitor 200 MB for a demo they may not have wanted — the same reason the home page's live demo is click-to-activate.

      What each layer actually enforces

      The browser refuses a download the sandbox did not allow. The engine refuses a mutation in Viewing mode at a single fail-closed choke point, so a chrome defeated from devtools still meets it. And the chrome disables a withheld capability with a reason rather than hiding it — a control that vanishes teaches nothing, and a product that silently lacks a feature is indistinguishable from one that is broken. Open the command palette inside a readonly embed and look at Open…: it is there, it is disabled, and it says why.

      And the fourth layer: the API

      A host that was not granted edit must not be able to run an editing command through the SDK either, or the API is simply the unlocked door beside three locked ones. So the capability gate runs before dispatch: pick preview above, mount, and run format.bold on either transport — the result is {"ok": false, "refusal": {"code": "capability-withheld"}} and the command never reaches the engine. Withhold download and run file.export.docx for the same answer. A refusal is a value, not just a toast.

      To configure a container the other way round — every capability, every chrome region and the brand palette, with the configuration written out for you — use the SDK playground. The reference is Embed the editor.