# Package Format & Manifest Reference

> v0.30.2

A `.pkg` file is a ZIP archive containing a `manifest.json` and optional
assets. Packages extend Chat Switchboard with surfaces (routable UIs),
extensions (server-side hooks/tools), or workflows.

## Archive Structure

```
manifest.json          required — package metadata and configuration
js/                    optional — JavaScript assets (served at /surfaces/:id/js/)
  main.js              entry point (loaded by surface template)
css/                   optional — stylesheets
assets/                optional — images, icons, etc.
script.star            optional — Starlark script (extension/sidecar tiers)
migrations/            optional — Starlark migration scripts (keyed by version)
  1.star
  2.star
```

## Building & Installing

```bash
# Build from packages/ directory
bash packages/build.sh my-package          # one package
bash packages/build.sh                     # all packages

# Install via API
curl -X POST http://localhost:3000/api/v1/admin/packages/install \
  -H "Authorization: Bearer $TOKEN" \
  -F file=@dist/my-package.pkg

# Install via admin UI
# Admin → System → Packages → Upload
```

## Manifest Schema

### Required Fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | `string` | Unique package identifier (kebab-case). Used as directory name and DB key. |
| `title` | `string` | Human-readable display name. |

### Optional Fields

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `type` | `string` | `"surface"` | Package type (see below). |
| `version` | `string` | `""` | Semver version string. |
| `description` | `string` | `""` | Short description shown in admin UI. |
| `author` | `string` | `""` | Author name or org. |
| `tier` | `string` | `"browser"` | Execution tier: `browser`, `starlark`, `sidecar`. |
| `route` | `string` | — | URL path for surface routing (e.g. `/s/my-surface`). |
| `auth` | `string` | `"authenticated"` | Auth requirement: `authenticated` or `public`. |
| `layout` | `string` | `"single"` | Page layout template. |
| `permissions` | `string[]` | `[]` | Required permissions (see Permissions below). |
| `tools` | `object[]` | — | Server-side tool declarations for AI tool-use. |
| `hooks` | `string[]` | — | Lifecycle hooks: `surface`, `on_install`, `on_uninstall`. |
| `settings` | `object` | — | Settings schema for admin-configurable options. |
| `schema_version` | `integer` | `0` | Current data schema version (for migrations). |
| `migrations` | `object` | — | Starlark migration scripts keyed by target version. |
| `network_access` | `string[]` | — | Allowed external hostnames (sidecar tier). |
| `api_routes` | `object[]` | — | Custom HTTP routes handled by Starlark `on_request`. |
| `db_tables` | `object[]` | — | Extension-owned database tables. |
| `pipes` | `object` | — | Filter pipeline registrations. |
| `components` | `string[]` | — | UI component identifiers. |
| `workflow_definition` | `object` | — | Embedded workflow definition (type `"workflow"` only). |

## Package Types

### `surface` (default)

A routable page served at the `route` path. The browser loads
`js/main.js` and renders into `#extension-mount`.

```json
{
  "id": "my-dashboard",
  "title": "My Dashboard",
  "type": "surface",
  "route": "/s/my-dashboard",
  "auth": "authenticated",
  "layout": "single",
  "version": "1.0.0"
}
```

### `extension`

Server-side logic without a routable UI. Runs Starlark code with
granted permissions.

```json
{
  "id": "auto-tagger",
  "title": "Auto Tagger",
  "type": "extension",
  "tier": "starlark",
  "version": "1.0.0",
  "permissions": ["filters.pre_completion"],
  "tools": [
    {
      "name": "tag_message",
      "description": "Auto-tag messages based on content"
    }
  ]
}
```

### `full`

Both a surface and an extension. Has a routable page and server-side
hooks/tools.

```json
{
  "id": "analytics",
  "title": "Analytics Suite",
  "type": "full",
  "tier": "starlark",
  "route": "/s/analytics",
  "version": "1.0.0",
  "permissions": ["db.read", "db.write"],
  "db_tables": [
    { "name": "events", "columns": { "ts": "text", "event": "text", "data": "text" } }
  ]
}
```

### `workflow`

A packaged workflow definition with optional surfaces and handlers.
See [WORKFLOW-PACKAGES.md](WORKFLOW-PACKAGES.md) for details.

```json
{
  "id": "onboarding-flow",
  "title": "Employee Onboarding",
  "type": "workflow",
  "version": "1.0.0",
  "workflow_definition": {
    "name": "Employee Onboarding",
    "slug": "employee-onboarding",
    "entry_mode": "public_link",
    "stages": [...]
  }
}
```

## Permissions

Declared in `manifest.permissions`. Admins grant/revoke per-package in
the admin UI.

| Permission | Description |
|-----------|-------------|
| `secrets.read` | Read secrets from the vault |
| `notifications.send` | Send push notifications |
| `filters.pre_completion` | Intercept messages before AI completion |
| `db.read` | Read from extension-owned tables |
| `db.write` | Write to extension-owned tables |
| `api.http` | Make outbound HTTP requests (sidecar tier) |
| `provider.complete` | Call LLM completion APIs |
| `forms.validate` | Validate workflow form submissions |
| `workflow.access` | Access workflow definitions and stage data |

## Settings Schema

Packages can declare admin-configurable settings. The schema drives a
form in the admin UI under System → Packages → Settings.

```json
{
  "settings": {
    "api_key": {
      "type": "string",
      "label": "API Key",
      "description": "External service API key",
      "required": true
    },
    "max_results": {
      "type": "number",
      "label": "Max Results",
      "default": 10
    },
    "enabled": {
      "type": "boolean",
      "label": "Enable Feature",
      "default": true
    }
  }
}
```

Settings are stored in the `package_settings` column and accessible from
Starlark via `settings.get("key")`.

## Data Migrations

Packages with `db_tables` can version their schema using Starlark
migration scripts. The engine runs migrations sequentially on
install/upgrade and rejects downgrades.

```json
{
  "schema_version": 2,
  "migrations": {
    "1": "def migrate(db):\n    db.query('CREATE TABLE ...')\n",
    "2": "def migrate(db):\n    db.query('ALTER TABLE ...')\n"
  }
}
```

Or reference files in the archive:

```
migrations/
  1.star    # def migrate(db): ...
  2.star    # def migrate(db): ...
```

## Extension Database Tables

Tables are namespaced to `ext_{pkg_id}_{table}` to prevent collisions.
Platform views provide read-only access to core data:

| View | Columns |
|------|---------|
| `ext_view_users` | `id`, `display_name`, `email` |
| `ext_view_channels` | `id`, `title`, `type`, `team_id` |

The `ext_data_tables` catalog tracks all extension-owned tables for
install/uninstall lifecycle management.

## Server-Side Tools

Extensions can declare tools for AI tool-use. The `on_tool_call` entry
point in the Starlark script handles dispatching.

```json
{
  "tools": [
    {
      "name": "lookup_customer",
      "description": "Look up customer by email",
      "parameters": {
        "email": { "type": "string", "required": true }
      }
    }
  ]
}
```

```python
# script.star
def on_tool_call(tool_name, params):
    if tool_name == "lookup_customer":
        result = http.get("https://api.example.com/customers?email=" + params["email"])
        return {"name": result["name"], "plan": result["plan"]}
```

## User-Installable Packages

Packages can be scoped to individual users or teams (v0.30.0). Only
`browser`-tier packages support user installation. Scope options:

| Scope | Visibility |
|-------|-----------|
| `global` | All users (admin-installed only) |
| `team` | Team members only |
| `personal` | Installing user only |
