This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/docs/EXTENSION-GUIDE.md
Jeffrey Smith da2c333e1a
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 22s
CI/CD / test-runners (pull_request) Has been skipped
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-frontend (pull_request) Successful in 7s
CI/CD / test-go-pg (pull_request) Successful in 3m11s
CI/CD / test-sqlite (pull_request) Successful in 3m30s
CI/CD / build-and-deploy (pull_request) Successful in 1m35s
v0.8.4: Documentation refresh + surface sizing fix
Docs pass covering v0.7.5–v0.8.3 module additions:
- STARLARK-REFERENCE: workspace module, permissions module, settings.has_capability()
- EXTENSION-GUIDE: capabilities manifest, vector(N) column, user_permissions,
  gate_permission, updated permissions list
- Tutorial reviewed for v0.7+ accuracy (no changes needed)

Surface sizing fix: .surface-inner converted to flex column layout so
the shell topbar and surface container share vertical space correctly.
All surface containers changed from height:100% to flex:1;min-height:0.
Eliminates 44px scroll cutoff on docs, admin, settings, and all
extension surfaces.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:35:40 +00:00

291 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Extension Guide
## Package Types
| Type | Description |
|------|-------------|
| `surface` | A routable UI page rendered in the shell viewport |
| `extension` | Starlark hooks, tools, API routes, DB tables |
| `full` | Both surface and extension combined |
| `library` | Shared code imported by other packages via `lib.require()` |
| `workflow` | Bundled workflow definition |
## Tiers
| Tier | Runs | Capabilities |
|------|------|-------------|
| `browser` | Client-side JS only | DOM access, SDK hooks, no server-side logic |
| `starlark` | Sandboxed server-side | DB, HTTP, notifications, secrets, API routes, realtime |
| `sidecar` | Separate container | Full runtime (future) |
## manifest.json
Every package has a `manifest.json` at its root. Example for a surface:
```json
{
"id": "my-surface",
"title": "My Surface",
"type": "full",
"tier": "starlark",
"version": "1.0.0",
"description": "A custom surface with server-side logic.",
"icon": "🔧",
"author": "you",
"route": "/s/my-surface",
"auth": "authenticated",
"permissions": ["db.write"],
"api_routes": [
{"method": "GET", "path": "/items"},
{"method": "POST", "path": "/items"}
],
"db_tables": {
"items": {
"columns": {
"title": "text",
"done": "int"
},
"indexes": [["title"]]
}
},
"settings": {
"page_size": {
"type": "string",
"label": "Items Per Page",
"description": "Number of items shown per page",
"default": "25"
}
}
}
```
### Field Reference
| Field | Required | Description |
|-------|----------|-------------|
| `id` | yes | Unique kebab-case identifier |
| `title` | yes | Display name |
| `type` | yes | `surface`, `extension`, `full`, `library`, `workflow` |
| `tier` | yes | `browser`, `starlark`, `sidecar` |
| `version` | yes | Semver string |
| `description` | no | Short description |
| `icon` | no | Emoji icon for sidebar/menu |
| `route` | surfaces | URL path (e.g., `/s/my-surface`) |
| `auth` | no | `authenticated` (default) or `public` |
| `permissions` | no | Sandbox capabilities: `db.write`, `db.read`, `api.http`, `notifications`, `secrets`, `realtime.publish`, `connections.read`, `workflow.access`, `batch.exec`, `files.read`, `files.write`, `workspace.manage` |
| `api_routes` | no | Array of `{method, path}` for extension HTTP endpoints |
| `api_schema` | no | OpenAPI documentation for extension API routes (see below) |
| `db_tables` | no | Table definitions (see below) |
| `settings` | no | User-configurable settings schema |
| `exports` | libraries | Functions exported for other packages |
| `hooks` | no | Event bus subscriptions |
| `config_section` | no | Settings/Admin panel injection (see below) |
| `capabilities` | no | Environment requirements (see below) |
| `user_permissions` | no | Permissions this extension registers for users (see below) |
| `gate_permission` | no | Permission checked before `on_request` executes |
| `schema_version` | no | Integer for additive schema migrations |
## db_tables Schema
Tables are automatically namespaced as `ext_{package_id}_{table_name}`.
Every table gets an auto-generated `id` primary key and `created_at` timestamp.
Column types: `text`, `int`, `vector(N)`.
The `vector(N)` type stores N-dimensional float vectors for similarity
search via `db.query_similar()`. Storage adapts to the backend:
Postgres + pgvector uses native `vector(N)` with HNSW indexes,
Postgres without pgvector uses `JSONB`, SQLite uses `TEXT`. N can be
14096. See the [Starlark Reference](STARLARK-REFERENCE) for query API.
```json
"db_tables": {
"notes": {
"columns": {
"title": "text",
"body": "text",
"creator_id": "text",
"pinned": "int"
},
"indexes": [
["creator_id"],
["pinned"]
]
}
}
```
## api_schema (OpenAPI Documentation)
Extensions can optionally declare an `api_schema` array in their manifest to provide rich API documentation. Declared routes appear in Swagger UI at `/api/docs` with parameters, request bodies, and response schemas. Routes without `api_schema` entries still appear as auto-generated stubs.
```json
"api_schema": [
{
"path": "/items",
"method": "GET",
"summary": "List items",
"description": "Returns paginated items for the current user",
"params": {
"limit": {"type": "integer", "default": 50, "description": "Max results"},
"offset": {"type": "integer", "default": 0}
},
"response": {
"type": "object",
"example": {"data": [{"id": "string", "title": "string"}]}
}
},
{
"path": "/items",
"method": "POST",
"summary": "Create item",
"body": {
"title": {"type": "string", "required": true},
"description": {"type": "string"}
}
}
]
```
Only `path` and `method` are required. All other fields are optional. Malformed entries are logged and skipped without blocking extension loading.
## Capabilities
Extensions can declare environment requirements via the `capabilities`
manifest field. The kernel validates these at install time.
```json
{
"capabilities": {
"required": ["postgres"],
"optional": ["pgvector", "workspace"]
}
}
```
- **required** — install is rejected (HTTP 422) if any capability is missing.
- **optional** — install succeeds with a logged warning. Query at runtime
with `settings.has_capability("pgvector")` to adapt behavior.
Detected capabilities: `pgvector`, `workspace`, `object_storage`, `s3`,
`postgres`. The admin can view detected capabilities at
**Admin > System > Capabilities** or via `GET /admin/capabilities`.
## User Permissions
Extensions can register custom permissions that the admin assigns to
user groups. This controls access to extension features beyond the
sandbox permission model.
```json
{
"user_permissions": ["image-gen.use", "image-gen.admin"],
"gate_permission": "image-gen.use"
}
```
- **user_permissions** — on install, these are merged into the kernel's
permission registry. On uninstall, they are removed. The admin assigns
them to groups in **Admin > Groups**.
- **gate_permission** — if set, the kernel checks this permission before
calling `on_request`. Unauthorized users get a 403 without the
extension code executing.
In Starlark, check permissions inline via `req["permissions"]` or call
`permissions.check(user_id, "image-gen.use")`.
## Starlark Sandbox API
Starlark scripts run server-side with a 1M operation budget and no
filesystem access. See the [Starlark Reference](STARLARK-REFERENCE) for
the complete module catalog, function signatures, and permission gates.
## config_section — Settings Panel Injection
Packages can inject configuration panels into the Settings, Admin, or
Team Admin surfaces. Declare `config_section` in `manifest.json`:
```json
{
"config_section": {
"label": "My Config",
"icon": "M12 2L2 7l10 5 10-5-10-5z",
"component": "js/config.js",
"surfaces": ["settings", "admin"],
"category": "system"
}
}
```
| Field | Required | Description |
|-------|----------|-------------|
| `label` | yes | Navigation label shown in the sidebar or tab list |
| `icon` | no | SVG path data for the nav icon |
| `component` | no | JS asset path (default: `js/config.js`). Must `export default` a Preact component. |
| `surfaces` | yes | Target surfaces: `"settings"`, `"admin"`, `"team-admin"` |
| `category` | no | Admin surface category tab (default: `"system"`). Ignored for settings/team-admin. |
**How it works:**
1. At page load, the backend scans all enabled packages for `config_section`
entries targeting the current surface.
2. Matching sections are injected into the page as `__CONFIG_SECTIONS__`.
3. The frontend dynamically imports the component module and renders it
as an additional tab/section.
4. The component receives `{ packageId, teamId }` as props.
**Example component** (`js/config.js`):
```javascript
const { html } = window;
const { useState, useEffect } = hooks;
export default function MyConfig({ packageId }) {
const [val, setVal] = useState('');
useEffect(() => {
sw.api.ext(packageId).get('/settings').then(r => setVal(r.value));
}, []);
return html`<div>
<label>API Key</label>
<input value=${val} onInput=${e => setVal(e.target.value)} />
<button onClick=${() => sw.api.ext(packageId).put('/settings', { value: val })}>Save</button>
</div>`;
}
```
## Permissions Model
Extensions declare required permissions in `manifest.json`. The admin
must grant each permission before the extension can use the corresponding
sandbox module. Permission status is visible in **Admin > Packages > Permissions**.
See [Permissions & Groups](PERMISSIONS-AND-GROUPS) for the full RBAC
model, user permission slugs, and settings cascade.
## File Structure
```
my-package/
manifest.json # required
js/ # browser-side JavaScript
index.js
css/ # stylesheets
styles.css
script.star # Starlark entry point
star/ # additional Starlark modules
assets/ # static assets
migrations/ # schema migration files
```
## Testing Extensions
1. Build the package: `cd packages && bash build.sh my-package`
2. Upload the `.pkg` file via Admin > Packages > Install.
3. Grant permissions in Admin > Packages > Permissions.
4. Enable the package.
5. If it is a surface, navigate to its route (e.g., `/s/my-package`).
Extension API routes are accessible at `/s/{slug}/api/{path}` and require an authenticated Bearer token.