> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tappify.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# What extensions can never do

> The guarantees Tappify holds for owners, and the mechanism behind each one.

This is the reference for the boundary an extension runs inside. Each line below is a guarantee
Tappify makes to the owner, enforced by the runtime rather than by convention, and together they
are the reason an extension can be installed without an audit. They also tell you which calls to
reach for, because every one of them is a door the host holds open on purpose.

## An extension never touches the host

Your code mounts inside its own shadow root and reaches the rest of Tappify only through `tap`.
It cannot read or write the host's DOM, its router, its cookies or its storage. Copying,
downloading and leaving Tappify are host-drawn calls for exactly that reason:

| You want                | You call                          | The host does                                                           |
| ----------------------- | --------------------------------- | ----------------------------------------------------------------------- |
| Copy text               | `tap.ui.copy(text)`               | Writes the clipboard and shows its own confirmation                     |
| Save a file             | `tap.ui.download(blob, filename)` | Hands the browser the file from the host's own document                 |
| Open a link             | `tap.nav.openExternal(url)`       | Asks the owner to confirm leaving Tappify, then opens a new tab         |
| Go somewhere in Tappify | `tap.nav.push(path)`              | Moves the host's router, and refuses a path outside the owner's project |

## An extension never sees a credential it was not given

Never a Tappify session credential, never a store credential, never another extension's data.
Credentials an owner supplies for your connector are held encrypted in Tappify's vault, are never
returned to the browser, and are read server-side only to forward to the server URL your manifest
declares.

The install token your server receives is RS256, five minutes long and audienced to your
extension alone. The data API accepts none of them: there is no vendor-callable token for
Tappify data, and every data call is made by the host on the owner's own session.

## Nothing is written on the owner's behalf without an approval card

`tap.actions.run` creates a pending run and renders the approval card: a card naming the scope, the
blast radius, whether the action can be undone and what it costs. Nothing reaches your server until
the owner approves it. There are no autonomous writes and no caller that bypasses the card — not the
assistant, which stops at the same pending run and lets the owner decide in chat, and not the
extension — and "always ask" is the default, which cannot be turned off for a scope that needs a
security review.

## Anything a vendor produced carries the vendor tile

Every surface an extension renders on — widget, tab, page, row action — carries the vendor's
mark, drawn by the host outside your mount, and so does every entry an extension adds to a menu
or to the project navigation. Owners always know which pixels and which numbers are not
Tappify's.

## The host draws every failure

Loading, failed, missing scope, paused, suspended and retired are Tappify's cards, with Tappify's
copy. An extension that throws, times out or fails to load shows one card, with Retry and Report
to vendor, and the rest of the dashboard is unaffected.

## Vendor text never carries authority over the assistant

Tool descriptions, skills, prompt templates, knowledge files and context blocks are size-capped,
scanned at publish for text that tries to override Tappify's own rules, and read by a reviewer
for a public extension. They are data about a vendor's product. An extension cannot render inside
the assistant's transcript; the assistant returns typed data and the host picks the card.

## Tappify never runs vendor code on its servers

An extension that acts runs its own server. Tappify calls it with a short-lived token audienced
to that one extension and a body carrying the project, the filters and the owner's saved
documents. There are no hosted functions.

## An extension is never framed

Tappify never puts a vendor's site in an iframe in the product. Expanding a widget or opening a
row action re-mounts the component you shipped, at a different size, with the row as
`tap.context`.

## And the switches behind them

* Bundles are content-addressed and immutable. A remote entry loads only from an origin on the
  allowlist, and only against the SHA-384 hash recorded for that release; an unverified
  extension-origin script is refused rather than loaded.
* At most three remote entries load at once per page, each with an eight-second ceiling, so a
  slow vendor never holds up the dashboard.
* Install tokens are RS256, five minutes, audienced to one extension, and the host mints a fresh
  one every four minutes for as long as a mount is open.
* Suspending an extension stops it loading everywhere inside one install refresh, about a minute.
* Uninstalling deletes the install and every document it held. Retiring revokes every install and
  keeps the documents exportable for thirty days before the same deletion runs.
* When a person leaves a workspace, their `user`-scoped documents go with them, across every
  install in it.

<Card title="Scopes" icon="key" href="/extensions/reference/scopes">
  What an owner can grant, and which grants need a review of your company.
</Card>
