JSON Plugin API
Send DynamicLake Live Activity updates from a local plugin process.
Overview
Use the JSON Plugin API when predefined DynamicLake components are enough and you do not need a SwiftUI ExtensionKit extension. Plugins can be written in any language that can connect to a Unix domain socket and write framed JSON.
For a full starter walkthrough, see CreateYourFirstPlugin. For package limits and DynamicLake Market review, see PluginPackagingAndMarket.
Connection
DynamicLake launches installed plugins and passes the socket path in the process environment:
DYNAMICLAKE_JSON_SOCKET=/path/to/dynamiclake-json-plugin.sock
DYNAMICLAKE_PLUGIN_SETTINGS_PATH=/path/to/plugin-settings.json
Each message is one frame:
- 4-byte unsigned big-endian payload length.
- UTF-8 JSON payload.
The JSON payload must be 64 KB or smaller. DynamicLake validates the full message before rendering.
Command Object
Every command must include schemaVersion and type.
{
"schemaVersion": 1,
"requestID": "req-1",
"type": "create",
"activityID": "build-42",
"title": "Build",
"priority": "normal",
"size": "large",
"surfaces": {
"compactLiveActivity": {
"leftSlot": { "type": "status", "status": "inProgress", "tint": "blue" },
"rightSlot": { "type": "progress", "value": 0.42, "tint": "blue" }
},
"sneakPeek": {
"leftSlot": { "type": "status", "status": "inProgress", "tint": "blue" },
"center": { "type": "text", "text": "Building DynamicLakeKit", "style": "marquee" }
}
}
}
| Field | Value | | --- | --- | | schemaVersion | Required. Must be 1. | | requestID | Optional opaque identifier returned in responses. | | type | Required. create, update, or dismiss. | | activityID | Required for create and update. Optional for dismiss. | | title | Optional display title, up to 80 characters. | | priority | Optional. low, normal, or high. Defaults to normal. | | size | Optional. small, normal, or large. Defaults to normal. | | surfaces | Required for create. Optional for update. | | presentSneakPeek | Optional on create and update. Opens the sneak peek for 1-10 seconds without hovering. See JSONPluginAPI — Show The Sneak Peek Automatically. |
Identifier-like fields may contain letters, numbers, ., _, :, and -.
Feature Detection
DynamicLake lists newer protocol features in the plugin's environment:
DYNAMICLAKE_PLUGIN_FEATURES=numericText,presentSneakPeek
Older DynamicLake versions don't set this variable and reject any message that uses an unknown field or value. Check for a feature before using it and fall back when it's missing.
features = set(os.environ.get("DYNAMICLAKE_PLUGIN_FEATURES", "").split(","))
supports_numeric_text = "numericText" in features
supports_present_sneak_peek = "presentSneakPeek" in features
| Feature | Enables | | --- | --- | | numericText | text components with style: "numeric". | | presentSneakPeek | The presentSneakPeek command field. |
Commands
create opens or replaces a plugin activity. It requires surfaces.compactLiveActivity.
update changes an existing activity by activityID. Send only the surfaces that changed.
dismiss closes a plugin activity. If activityID is omitted, DynamicLake may dismiss plugin-owned activity state for that client.
Size And Priority
Use size to choose the compact live activity width:
| Value | Meaning | | --- | --- | | small | Minimal compact activity for icon-only, status, progress, or timer layouts that must stay tight. | | normal | Compact visual, status, progress, or duration layouts that need slightly more room than small. | | large | Widest compact activity for visual, status, progress, or duration layouts that need the most room. |
Do not put descriptive text in compactLiveActivity slots. Use text in sneakPeek; in the live activity, text should be duration-style content such as a timer or countdown.
Use priority to choose how the plugin competes with other active DynamicLake content:
| Value | Meaning | | --- | --- | | low | Long-running, ambient state that can wait behind more immediate content. Use it for calendar events, ongoing status, and other activities that may remain active for a long time. | | normal | The default for active work the user may want to glance at, such as music playback, file uploads, builds, or downloads. | | high | Important, time-sensitive content that should win over normal activity because the user should not miss it, such as calls, messages, and urgent notifications. |
When size or priority is omitted from create, DynamicLake uses normal. When omitted from update, DynamicLake keeps the previous value.
Surfaces
Plugins can send two surfaces:
| Surface | Slots | | --- | --- | | compactLiveActivity | leftSlot, rightSlot | | sneakPeek | leftSlot, center, rightSlot |
The compact live activity should stay visual and minimal. Put labels, descriptive text, and controls in sneakPeek.
Plugin Settings
Plugins can declare settings in plugin.json. DynamicLake renders those settings with native settings cells and exposes the current values to the plugin at launch.
DynamicLake shows plugin settings in a fixed 520 x 620 window. The header uses the plugin icon and bold plugin name. Settings content is inside a vertical scroll view.
!Plugin settings components showing switch, slider, select, and button rows.
Supported setting types:
| Type | Required fields | Optional fields | | --- | --- | --- | | switch | id, type, title | description, systemImage, tint, default | | slider | id, type, title | description, systemImage, tint, default, min, max, step, suffix | | select | id, type, title, options | description, systemImage, tint, default; options may include systemImage | | button | id, type, title, url | description, systemImage, tint, buttonTitle, destructive |
{
"settings": [
{
"id": "showCodexIcon",
"type": "switch",
"title": "Show Codex Icon",
"systemImage": "terminal.fill",
"tint": "cyan",
"default": true
},
{
"id": "staleSeconds",
"type": "slider",
"title": "Stale Timeout",
"min": 15,
"max": 600,
"step": 15,
"suffix": "sec",
"default": 90
},
{
"id": "agentStyle",
"type": "select",
"title": "Agent Style",
"systemImage": "sparkles",
"options": [
{
"title": "Compact",
"systemImage": "rectangle.compress.vertical"
},
{
"title": "Detailed",
"systemImage": "list.bullet.rectangle"
}
],
"default": "Compact"
},
{
"id": "help",
"type": "button",
"title": "Plugin Help",
"buttonTitle": "Open",
"url": "https://example.com/help"
}
]
}
Setting validation rules:
- Use
24settings or fewer. idmust be1-64characters, start with a letter, and may contain letters, numbers,_, and-.titlecan be up to80characters.descriptioncan be up to180characters.default, when present, must match the setting type.sliderrequiresminlower thanmax;stepmust be positive and fit inside the range.suffixcan be up to16characters.selectrequires1-24unique options. Options can be strings or objects withtitleand optionalsystemImage. Each option title andsystemImagecan be up to80characters.buttonopens anhttporhttpsURL.buttonTitlecan be up to40characters.
At runtime, DynamicLake provides:
| Variable | Value | | --- | --- | | DYNAMICLAKE_PLUGIN_SETTINGS_PATH | JSON file with schemaVersion, identifier, and values. DynamicLake rewrites it when the user changes settings. | | DYNAMICLAKE_PLUGIN_SETTINGS_JSON | The same setting values as compact JSON. | | DYNAMICLAKE_SETTING_<SETTING_ID> | One variable per setting, such as DYNAMICLAKE_SETTING_SHOW_CODEX_ICON=1. |
Long-running plugins should reread or watch DYNAMICLAKE_PLUGIN_SETTINGS_PATH when they need live setting changes.
Components
Supported component types:
| Type | Required fields | Notes | | --- | --- | --- | | text | text | Use for sneak peek labels and marquee text. In compactLiveActivity, use only for duration-style values. Optional style: plain, marquee, compact, timer, or numeric. See JSONPluginAPI — Animated Numbers for numeric. | | image | depends on source | source: sfSymbol, appIcon, or inlineData. | | button | actionID | Optional title, systemImage, role, and shape. | | progress | none | Optional value from 0 to 1. Omit value for indeterminate progress. | | timer | startDate, endDate | ISO-8601 dates. startDate must be before endDate. | | status | none | Optional status: success, failed, inProgress, paused, or warning. |
All components may include:
| Field | Value | | --- | --- | | id | Optional component identifier. | | tint | Optional color: blue, cyan, teal, indigo, purple, pink, green, orange, yellow, red, gray, or white. |
Images
Prefer SF Symbols for small live activity UI.
{
"type": "image",
"source": "sfSymbol",
"systemImage": "bolt.fill",
"tint": "yellow"
}
Use appIcon to show the plugin icon:
{
"type": "image",
"source": "appIcon"
}
Use inlineData only for small PNG or JPEG data. Decoded inline image data must be 48 KB or smaller.
{
"type": "image",
"source": "inlineData",
"mimeType": "image/png",
"base64Data": "..."
}
File paths, URLs, shell commands, scripts, and arbitrary render code are not part of the component schema.
Buttons And Actions
Buttons never execute commands directly. A button sends an action callback to the plugin over the same framed socket connection.
Buttons use shape: "circle" by default. In sneakPeek, use shape: "roundedRect" when the button needs visible text. Keep compact live activity buttons icon-only.
{
"type": "button",
"id": "cancel",
"title": "Cancel",
"systemImage": "xmark",
"actionID": "cancel-build",
"shape": "roundedRect",
"role": "destructive"
}
{
"schemaVersion": 1,
"type": "action",
"activityID": "build-42",
"actionID": "cancel-build",
"componentID": "cancel",
"surface": "sneakPeek"
}
The plugin decides what to do with the action and may send an update or dismiss command afterward.
Animated Numbers
Use style: "numeric" for short values that change often, such as a percentage or a count. DynamicLake rolls each digit that changes, similar to SwiftUI's .contentTransition(.numericText()). In the sneak peek, numeric text is bold and slightly smaller than other sneak peek text. Numeric text works in sneakPeek.leftSlot, sneakPeek.center, sneakPeek.rightSlot, and duration-style compactLiveActivity text.
"rightSlot": { "type": "text", "id": "percent", "text": "45%", "style": "numeric" }
Sneak peek side slots widen to fit short numeric text such as 100%. Other text styles in side slots show only their first two characters, so use center for longer text.
Numeric text requires the numericText feature. Without it, show the value in center with style: "compact", or use a progress component in the side slot.
Show The Sneak Peek Automatically
By default a sneak peek opens only while the user hovers over the live activity. Add presentSneakPeek to a create or update command to open it for a number of seconds without hovering:
{
"schemaVersion": 1,
"type": "update",
"activityID": "download",
"presentSneakPeek": 2,
"surfaces": {
"compactLiveActivity": {
"rightSlot": { "type": "status", "status": "success", "tint": "green" }
},
"sneakPeek": {
"leftSlot": { "type": "button", "systemImage": "folder.fill", "actionID": "show-in-finder" },
"center": { "type": "text", "text": "Report.pdf", "style": "compact" }
}
}
}
- The value is the duration in seconds, from
1to10. - The activity must have
sneakPeekcontent and be the activity currently shown in DynamicLake; otherwise the request is ignored and the command still succeeds. - Use it for moments the user should notice, such as a finished download or a completed build. Don't send it with every progress update.
presentSneakPeek requires the presentSneakPeek feature. Without it, send the same command without the field; the sneak peek content still updates and appears on hover.
Responses And Errors
DynamicLake can send a response frame after handling a request.
{
"schemaVersion": 1,
"type": "response",
"requestID": "req-1",
"activityID": "build-42",
"ok": true
}
If validation fails, DynamicLake sends an error response and keeps the socket open when possible.
{
"schemaVersion": 1,
"type": "error",
"ok": false,
"error": "Missing field 'compactLiveActivity' at $.surfaces."
}
Topics
Protocol Types
DynamicLakeJSONPluginProtocolDynamicLakeJSONPluginCommandDynamicLakeJSONPluginSurfacesDynamicLakeJSONPluginCompactSurfaceDynamicLakeJSONPluginSneakPeekSurfaceDynamicLakeJSONPluginComponentDynamicLakeJSONPluginCommandTypeDynamicLakeJSONPluginComponentTypeDynamicLakeJSONPluginTextStyleDynamicLakeJSONPluginFeatureDynamicLakeJSONPluginTintDynamicLakeJSONPluginImageSourceDynamicLakeJSONPluginButtonRoleDynamicLakeJSONPluginButtonShapeDynamicLakeJSONPluginStatusDynamicLakeJSONPluginActionCallbackDynamicLakeJSONPluginResponse