# Plugin deployment Uploading a bundle plugin, enabling it, and managing permissions. ## Upload — `POST /api/plugin/:pluginId/upload-bundle` Upload the built bundle to the host. Uses `multipart/form-data`. The `pluginId` in the URL must match the `id` in `plugin.json`. ```bash PLUGIN_ID=$(python3 -c "import json; print(json.load(open('plugin.json'))['id'])") curl -s -b "auth_session=$SESSION" \ -F "js=@dist/main.js" \ -F "css=@dist/styles.css" \ -F "manifest=@plugin.json" \ "http://localhost:3003/api/plugin/$PLUGIN_ID/upload-bundle" ``` **Auth note**: Supports both session cookies (`auth_session`) and Personal Access Tokens (`Authorization: Bearer `). **Fields:** | Field | Required | Max size | Description | |---|---|---|---| | `js` | Yes | 5 MB | The IIFE bundle (`dist/main.js`) | | `css` | No | 1 MB | Optional stylesheet (`dist/styles.css`) | | `manifest` | Yes | 10 KB | `plugin.json` manifest | **Returns** `201` with `{ id, name, version, ... }` — the plugin ID to use for enable and install. **Security** (7-layer validation): 1. Authentication — valid session cookie required (401 if missing) 2. Developer whitelist — `plugin_developer` table lookup (403 if not whitelisted) 3. Rate limit — 10 uploads/min per user (429 if exceeded) 4. Size limits — JS ≤ 5 MB, CSS ≤ 1 MB, manifest ≤ 10 KB 5. Manifest validation — required fields, `id` regex `[a-z0-9-]+` 6. Content validation — JS must be IIFE format (prefix `!function`/`(()=>`), rejects `