···2233Balance minimal install/development/distribution barriers with web-level safety at runtime.
4455-Key bits:
55+## Current Implementation
66+77+### Extension Structure
88+99+Extensions live in `./extensions/{name}/` with:
1010+1111+```
1212+extensions/
1313+├── groups/
1414+│ ├── manifest.json # Extension metadata
1515+│ ├── background.js # Main logic (ES module)
1616+│ ├── config.js # Configuration
1717+│ ├── home.html # UI pages
1818+│ └── ...
1919+├── peeks/
2020+│ └── ...
2121+└── slides/
2222+ └── ...
2323+```
2424+2525+### Manifest Format
2626+2727+```json
2828+{
2929+ "id": "groups",
3030+ "shortname": "groups",
3131+ "name": "Groups",
3232+ "description": "Tag-based grouping of addresses",
3333+ "version": "1.0.0",
3434+ "background": "background.js",
3535+ "builtin": true
3636+}
3737+```
3838+3939+**Required fields:**
4040+- `id` - Unique extension identifier
4141+- `shortname` - URL path segment (lowercase alphanumeric + hyphens)
4242+- `name` - Display name
4343+- `background` - Background script filename
4444+4545+**Optional fields:**
4646+- `description` - Extension description
4747+- `version` - Semver version string
4848+- `builtin` - If true, has full API access (default: false)
64977-- Extensions are a folder with a PWA manifest and web content files
88-- They're opened under the peek:// protocol, each in a web content process
99-- Their main window is hidden, like the Peek background content process
1010-- Extensions are run directly from their local folder (wherever the user selected)
1111-- Extensions are managed in the main settings app, eg add/remove, enable/disable
5050+### Extension URLs
12511313-Implementation
5252+Extensions are accessed via the `peek://ext/` protocol:
5353+5454+```
5555+peek://ext/{shortname}/{file}
5656+```
5757+5858+Examples:
5959+- `peek://ext/groups/home.html`
6060+- `peek://ext/peeks/settings.html`
6161+- `peek://ext/slides/background.js`
14621515-- New table in datastore for extensions
1616-- New section in settings app, where users can:
1717- - Add/remove
1818- - Enable/disable
1919- - Activate/suspend/reload
2020- - Click to access settings
2121-- Peeks, Slides and Groups as built-in but disable-able extensions
2222-- Command registration moves to API, so extensions can call it
2323-- Extension related commands like the groups ones are moved to the extension
2424-- Coarse permissions flag: built-in extensions get full access to api, others are restricted from using the extensions management api (to start)
2525-- Extensions need to register a shortname for use in the peek:// address, conflicts are rejected at install time
6363+**Reserved shortnames** (cannot be used):
6464+- `app`, `ext`, `extensions`, `settings`, `system`
26652727-Note: The implementation will also instigate another shift, moving as much logic into the background web app as possible, vs in node.js space, so we can eventually move to other back-ends than Electron.
6666+### Extension Loader
28672929-Capabilities via injected API:
6868+Located at `app/extensions/loader.js`, handles:
6969+7070+- **Loading**: Fetches manifest, validates, registers shortname, imports background script
7171+- **Unloading**: Calls uninit(), unregisters shortname, removes from registry
7272+- **Reloading**: Unload + load cycle
7373+- **Conflict detection**: Rejects extensions with duplicate shortnames
7474+7575+Built-in extensions are registered in the loader:
7676+```javascript
7777+export const builtinExtensions = [
7878+ { id: 'groups', path: 'peek://ext/groups', backgroundScript: 'background.js' },
7979+ { id: 'peeks', path: 'peek://ext/peeks', backgroundScript: 'background.js' },
8080+ { id: 'slides', path: 'peek://ext/slides', backgroundScript: 'background.js' }
8181+];
8282+```
8383+8484+### Permissions Model
8585+8686+**Coarse permission levels:**
8787+- `builtin: true` → Full API access including extension management
8888+- `builtin: false` → Restricted from `api.extensions` management APIs
8989+9090+**Permission check:** Only `peek://app/...` addresses can manage extensions.
9191+9292+### Extension Management API
9393+9494+```javascript
9595+// List running extensions (no permission required)
9696+const result = await api.extensions.list();
9797+// { success: true, data: [{ id, shortname, manifest, ... }] }
9898+9999+// Get extension manifest (no permission required)
100100+const result = await api.extensions.getManifest('groups');
101101+// { success: true, data: { id, shortname, name, ... } }
102102+103103+// Load/unload/reload (permission required - core app only)
104104+await api.extensions.load('groups');
105105+await api.extensions.unload('groups');
106106+await api.extensions.reload('groups');
107107+```
108108+109109+### Background Script Pattern
110110+111111+```javascript
112112+// extensions/myext/background.js
113113+import { openStore } from 'peek://app/utils.js';
114114+import appConfig from './config.js';
115115+116116+const api = window.app;
117117+const { id, defaults, storageKeys } = appConfig;
118118+const store = openStore(id, defaults);
119119+120120+const init = () => {
121121+ // Register shortcuts, commands, subscriptions
122122+ api.shortcuts.register('Option+x', handleShortcut, { global: true });
123123+ api.commands.register({ name: 'my command', execute: handler });
124124+};
125125+126126+const uninit = () => {
127127+ // Clean up
128128+ api.shortcuts.unregister('Option+x', { global: true });
129129+ api.commands.unregister('my command');
130130+};
131131+132132+export default { id, init, uninit };
133133+```
134134+135135+---
136136+137137+## Design Goals
138138+139139+- Extensions are a folder with a manifest and web content files
140140+- They're opened under the peek:// protocol, each in a web content process
141141+- Their main window is hidden, like the Peek background content process
142142+- Extensions are run directly from their local folder
143143+- Extensions are managed in the settings app (add/remove, enable/disable)
144144+145145+Note: The implementation shifts logic into the background web app vs node.js space, enabling future non-Electron backends.
146146+147147+## Capabilities via Injected API
3014831149- Window management
32150- Datastore access