Overview
The manifest file is your extension’s configuration center - it defines permissions, capabilities, and metadata. Understanding manifest configuration is critical for building secure, trustworthy extensions that users feel comfortable installing.
This lesson covers extension permissions, host permissions, and the principle of least privilege for store approval and user trust.
What You’ll Learn
- Interpret WXT’s auto-generated manifest.json
- Understand common extension permissions and their purposes
- Configure host permissions for web page access
- Distinguish between API permissions and host permissions
- Apply the principle of least privilege
- Understand how Chrome translates permissions for users
The Manifest’s Role
Every extension requires a manifest.json file that tells the browser:
- Metadata: Name, version, description
- Permissions: What browser APIs the extension can access
- Host Permissions: Which web pages the extension can interact with
- Entry Points: Popup, background script, content scripts
- Icons and Branding: Visual assets
With WXT, you rarely edit manifest.json directly. WXT generates it from your project structure and wxt.config.ts.
Viewing the Generated Manifest
Check .output/chrome-mv3-dev/manifest.json to see what WXT creates:
{
"manifest_version": 3,
"name": "tabnotes",
"version": "1.0.0",
"permissions": ["tabs", "storage"],
"action": {
"default_popup": "popup/index.html"
},
"background": {
"service_worker": "background.js"
}
}
WXT inferred this from:
- Project structure (
entrypoints/popup/,entrypoints/background.ts) - Config file (
wxt.config.tspermissions array)
Extension Permissions
Permissions grant access to specific browser APIs. Request only what you need - excessive permissions harm approval chances and user trust.
Common Permissions Reference
storage - Save and retrieve user data
- Use case: Persist extension settings, user preferences
- Includes:
chrome.storage.local,chrome.storage.sync - Security: Data isolated per-extension, not accessible by websites
tabs - Access tab information and control tabs
- Use case: Detect tab changes, get tab URL/title
- Includes:
chrome.tabs.query(),chrome.tabs.onActivated - Security: Can read browsing history via tab URLs
activeTab - Access current tab when user invokes extension
- Use case: Act on current page only when user clicks extension
- Includes: Temporary access to active tab
- Security: Safer than
tabs- requires user interaction, no background access
scripting - Inject JavaScript/CSS into web pages
- Use case: Modify page content, inject UI elements
- Includes:
chrome.scripting.executeScript() - Security: Requires host permissions (see below)
contextMenus - Add items to right-click menu
- Use case: Custom menu options on pages or selections
- Includes:
chrome.contextMenus.create()
bookmarks - Read and modify browser bookmarks
- Use case: Bookmark management tools
- Security: Full access to bookmark tree
notifications - Display system notifications
- Use case: Alert users outside browser
- Includes:
chrome.notifications.create()
cookies - Read and write cookies
- Use case: Session management, authentication helpers
- Security: Requires host permissions for specific domains
history - Access browsing history
- Use case: History analysis, recently visited pages
- Security: Highly sensitive - users see “Read your browsing history”
webRequest - Intercept and modify network requests
- Use case: Ad blockers, request monitoring
- Security: Can see and alter all network traffic
Complete Reference: Chrome Permissions List
Host Permissions
Host permissions define which web pages your extension can access. Some APIs (like scripting) require both API permission AND host permission.
Why Two Permission Types?
API Permissions: “Can I use this browser feature?” Host Permissions: “Can I access this website?”
Example: The tabs API lets you query tabs, but accessing tab.url of all tabs requires broader permissions.
Host Permission Patterns
Specific Domains:
// wxt.config.ts
export default defineConfig({
manifest: {
host_permissions: [
'https://github.com/*',
'https://stackoverflow.com/*'
]
}
});
Extension can only interact with GitHub and Stack Overflow pages.
All HTTPS Sites:
host_permissions: ['https://*/*']
Most common pattern - access any HTTPS website.
All URLs (Including chrome:// and file://):
host_permissions: ['<all_urls>']
Includes chrome:// pages and local files. Rarely needed.
Pattern Matching Syntax
*- Matches any stringhttps://*/*- All HTTPS siteshttps://*.google.com/*- All Google subdomainshttps://example.com/path/*- Specific path on domain
Best Practice: Use the most restrictive pattern that satisfies your extension’s needs.
WXT’s Automatic Host Permissions
Good news: WXT automatically adds host permissions when you create content scripts:
// entrypoints/content.ts
export default defineContentScript({
matches: ['https://github.com/*'],
main() {
console.log('Running on GitHub');
}
});
WXT automatically adds https://github.com/* to host_permissions in the generated manifest. You don’t manually configure it.
TabNotes Permission Strategy
For our note-taking extension, we need:
// wxt.config.ts
export default defineConfig({
manifest: {
permissions: ['storage', 'tabs', 'scripting'],
host_permissions: ['https://*/*']
}
});
Why Each Permission:
storage- Save notes persistentlytabs- Detect tab changes to load the correct notescripting- Inject visual indicator (ribbon) into web pageshttps://*/*- Allow content script on any HTTPS site
The Principle of Least Privilege
Request the minimum permissions needed. Every unnecessary permission:
- Slows store approval - Chrome/Firefox reviewers scrutinize permissions
- Reduces user trust - Users see warnings like “Read your browsing history”
- Increases security risk - More permissions = larger attack surface
How Users See Permissions
Chrome translates technical permissions into plain English:
| Permission | User Sees |
|---|---|
tabs | ”Read your browsing history” |
storage | ”Save data on your device” |
scripting + <all_urls> | ”Read and change all your data on all websites” |
history | ”Read your browsing history” |
bookmarks | ”Read and change your bookmarks” |
Important: Even if you only use tabs to get the active tab’s URL, users see “Read your browsing history” - this is why alternatives like activeTab exist.
activeTab vs tabs Permission
Use activeTab when:
- User explicitly clicks your extension
- You only need the current tab
- You don’t need background tab monitoring
Use tabs when:
- Background script monitors tab changes
- Extension needs tab information without user clicking icon
- TabNotes uses
tabsbecause we detect tab changes automatically
Configuring Permissions in WXT
All permission configuration happens in wxt.config.ts:
// wxt.config.ts
import { defineConfig } from 'wxt';
export default defineConfig({
manifest: {
// Extension metadata
name: 'TabNotes',
version: '1.0.0',
description: 'Save notes for every website',
// API permissions
permissions: [
'storage', // Save notes
'tabs', // Detect tab changes
'scripting' // Inject ribbon
],
// Host permissions (which sites to access)
host_permissions: [
'https://*/*' // All HTTPS sites
]
}
});
After changing permissions, WXT regenerates manifest.json automatically (may require dev server restart).
Permission Warnings During Development
Chrome shows permission warnings when you modify permissions:
Scenario: You add history permission to wxt.config.ts
Result: Chrome dev tools shows “New permissions required. Click to review.”
This simulates the user experience when your extension updates. Test that permission changes don’t break existing functionality.
Store Submission Requirements
Both Chrome Web Store and Firefox Add-ons require permission justification:
Example Justification for TabNotes:
- Storage: “Save user notes across browser sessions”
- Tabs: “Detect which website is active to load the relevant note”
- Scripting + https://: “Display visual indicator (ribbon) on pages with saved notes”
Reviewers reject extensions with:
- Permissions not used in the code
- Vague justifications (“Needed for functionality”)
- Overly broad permissions without clear need
Common Permission Mistakes
❌ Requesting tabs when activeTab suffices
// If you only need current tab on icon click
permissions: ['activeTab'] // Better than 'tabs'
❌ Using <all_urls> unnecessarily
host_permissions: ['<all_urls>'] // Includes chrome:// pages
// vs
host_permissions: ['https://*/*'] // HTTPS only (usually sufficient)
❌ Including unused permissions “just in case”
// Don't request permissions for potential future features
permissions: ['storage', 'tabs', 'history', 'bookmarks'] // Too broad!
Debugging Permission Issues
Permission Denied Errors
If you see errors like "storage" permission denied:
- Check
.output/chrome-mv3-dev/manifest.jsonto verify permission exists - Restart dev server (config changes require restart)
- Reload extension in
chrome://extensions
Missing Host Permissions
If content scripts don’t run:
- Verify
matchespattern in content script definition - Check WXT added pattern to
host_permissionsin manifest - Ensure pattern syntax is correct (
https://*/*nothttp://*/*)
What’s Next
You now understand extension permissions and the manifest file’s role in security. Requesting minimal permissions builds user trust and speeds store approval.
Lesson 5 builds the actual note-taking interface - a styled popup where users write and save notes. We’ll create the UI foundation before adding storage in Lesson 6.