I ship eight small Chrome extensions under Legacy Tools. They are all
Manifest V3, and they all need some form of access to the pages you
visit — that is what tools that check things before you send them do.
I have written before about the rules I give AI agents so they never broaden permissions on their own. This post is about the
other side of that: the shapes the declarations themselves take.
Recently I put all eight manifests side by side (the versions shipped to
the Chrome Web Store as of 2026-08-22) and realized they ended up using
four different shapes to declare that access. None of this was planned
up front; each shape fell out of what the tool is. Here is the survey.
1. Everywhere, all the time — five extensions.
Safe Privacy Gate, Safe Attachment Check, Safe Mail Link Check, Site
Operator Check, Find My Age. These step in right before a send or a
click, and there is no way to know in advance which site that will
happen on. Interesting detail: only two of the five actually write
<all_urls>
into host_permissions
. The other three get the same
breadth from content_scripts.matches
alone.
2. One fixed site — one extension.
Safe Night Check is a Gmail-only tool, so the whole declaration is a
single pattern: https://mail.google.com/*
.
3. A fixed list plus optional grants — one extension.
Safe Privacy Mask enumerates thirty matches
patterns for the AI chat
sites it supports, and puts <all_urls>
into
optional_host_permissions
so that any other site is granted only when
the user adds it, one prompt at a time.
4. activeTab only — one extension.
While checking what these declarations look like from the user's side
(on `chrome://extensions`
, with the extensions side-loaded into
Chromium), two things stood out:
storage
,
scripting
, contextMenus
and activeTab
— none of those produces
a line in that box. "The permissions list is blank, so it must be
harmless" is not a read you can make.matches
counts as a host permission.host_permissions
at all, and its site-access wording
was character-for-character identical to Safe Privacy Gate, which
declares <all_urls>
explicitly. You cannot shrink the warning by
moving breadth out of host_permissions
and into content_scripts
.Permission declarations are not configuration. They are UI — shown to
the user, in their words, at install time and on the details page. The
shape you choose is a product decision: <all_urls>
is honest for a
tool that must work everywhere, a fixed list is honest for a
single-site tool, and activeTab
is worth the extra design work when
"only when I ask" is the actual contract.
I wrote up the longer versions of both halves of this: