Documentation / Plugin System

Plugin System

Aphotic base is the shell, rice/theming, Settings, and core Quickshell modules — everything else, AI capabilities included, is an independently installable and removable plugin. This page covers the manifest format, what each capability actually does, and the current plugin roster. For the day-to-day commands, see CLI Reference’s Plugins section.

The manifest (plugin.toml)

A plugin is a directory with a plugin.toml manifest declaring what it is and what it touches. Manifest versions are additive — a v1 plugin (just [plugin] + a theme hook) parses and behaves identically under v2 or v3, nothing needed a migration.

[plugin]
name = "example"
display_name = "Example Plugin"
description = "..."
version = "1.0.0"
author = "..."
category = "ai"                    # dev | security | mobile | ai | theming | productivity
capabilities = ["theme-hook"]      # see Capabilities below -- a plugin can declare more than one

[requires]
binaries = ["some-binary"]         # checked before enabling; warns and no-ops, never blocks

[hooks]
on_theme_change = "hooks/on_theme_change.sh"

[owns]
config_keys = ["someSettingKey"]           # Settings.qml keys this plugin is the sole consumer of
external_config = ["~/.some/foreign.conf"] # a foreign program's config file this plugin touches

[ui.dashboard_tab]
id = "example"
icon = "extension"
label = "Example"
component = "qml/ExampleTab.qml"

[harness]
wire = "hooks/wire.sh"     # see the harness-hook capability below
unwire = "hooks/unwire.sh"

ui-surface covers eight distinct surface kinds, one [ui.*] block each (a plugin can declare more than one):

Block Hosted where Notes
[ui.dashboard_tab] Command Center (SUPER+D) The original kind. id/icon/label/component, optionally requires_layer/requires_data.
[ui.notch_tile] The notch popout Same shape as a dashboard tab, docked into the notch instead, alongside the two core tiles (Processes, Commands) every install already has.
[ui.settings_pane] Settings, docked into an existing category Adds parent (the category id it docks into, e.g. "ai" or "appearance") instead of claiming its own rail entry — the rail is a fixed length by design.
[ui.overlay] A layer-shell window core owns and budgets Adds anchor, width, height — core sizes and positions a real PanelWindow from these once and never renegotiates it; the plugin ships a plain Item, never its own window. This is what a fully click-through, always-on-screen surface (a pet, an audio visualiser) needs, without letting third-party code own real window geometry.
[ui.fullscreen-overlay] A dedicated fullscreen, exclusive-input layer-shell window, built and torn down around a trigger For a surface that needs the whole screen and real keyboard focus while it’s open, not a fixed-budget always-on overlay.
[ui.background] The desktop background itself Draws inside the wallpaper window core already owns, between the still wallpaper and the desktop clock. Takes no anchor/width/height — that window is already full-screen, so there is no geometry to budget. Use this rather than [ui.overlay] for anything wallpaper-shaped: an overlay sits above the background window and would cover the desktop clock. Live Wallpapers is the first plugin built on it.
[ui.workspace] The SUPER+SHIFT+W workspace plane A full workspace-shaped tool surface — see Supported Features. The keybind itself only exists at runtime while at least one enabled plugin registers this surface; with none installed there’s nothing to open and no key is bound. Agent Audit is the first plugin built on it.
[ui.pet_action] (and [ui.pet_action_2], [ui.pet_action_3]) The pet plugin’s own action menu Numbered sections rather than an array — the shared TOML reader has no arrays-of-tables — capped at three entries per plugin. The first surface kind where one plugin can register more than one entry.

requires_layer/requires_data are both optional on every kind above. Omit both and the surface is available on every install — PluginRegistry treats an empty gate as “no gate,” not as “gated on nothing being true.”

category drives filtering in aphotic plugin list --remote and Settings → Plugins. Security-category plugins live in a separate index that stays untrusted (and unfetched) until aphotic plugin trust-security-index is run once — the same “opt into the riskier stuff explicitly” pattern as the exploit layer’s BlackArch confirmation.

Capabilities

A plugin declares one or more capability tags in [plugin].capabilities. Each unlocks a different mechanism. Aphotic 2.0.7 hosts ui-surface, theme-hook, project-hook, workspace-hook, harness-hook, profile, cli, chat-provider and action, and the surface kinds dashboard, notch, settings, workspace, overlay, fullscreen-overlay, background and pet_action. A plugin declaring something this build doesn’t host is checked at install: if nothing it declares is hosted, aphotic plugin install refuses it (“needs a newer Aphotic”); if only part is, it installs with a warning that the rest stays dark until you update, and aphotic plugin list keeps flagging it as partially hosted.

Capability What it does Example
theme-hook Fires [hooks].on_theme_change on every theme apply, with the resolved palette (background/foreground/cursor + 16 ANSI colors) as positional hex args or JSON on stdin. Fire-and-forget, 5-second timeout — a slow or broken hook can’t stall the theme switch it’s piggybacking on. OpenRGB Sync
project-hook Fires [hooks].on_project_open (one arg: the project’s absolute path) when the launcher’s @ mode opens a project. direnv Notice
workspace-hook Fires [hooks].on_workspace_launch (one arg: the profile name) when a Workspace Profile launches. Workspace Session Log
harness-hook For a plugin that wires itself into a different program’s own config instead of reacting to an Aphotic event. Install/enable runs [harness].wire; disable/remove runs [harness].unwire — both receive one argument, the shell’s own lib/aphotic directory, so a plugin’s translator script never has to guess a fixed clone path. This is the one capability where disable/enable actively rewrite external state rather than just gating a symlink, since the “enabled” flag lives in the harness’s own config file, not something Aphotic can check from its own side. claude-hooks, codex-hooks, opencode-hooks
ui-surface Contributes a real UI surface — dashboard tab, notch tile, Settings section, or a core-owned overlay window (see the table above) — loaded dynamically at runtime (Loader.source, not a compiled-in import) and gone the instant the plugin is disabled or removed — no shell rebuild, no leftover UI. Agent Graph, Desktop Pet, Spectrum
cli Contributes a real aphotic subcommand, or a subcommand of an existing one, resolved by declaration ([cli]’s command/subcommand/script) — no core file ever names the plugin providing it. Core’s own commands are tried first, so a plugin can’t shadow a built-in. Hardware Advisor (aphotic ai fit)
profile Registers a headless, install-profile-shaped component ([profile]) that participates in the Resource Engine’s negotiation (claims, yields, phase changes) the same way core’s own profile detectors do — for domain behavior that has no UI surface of its own, just system coordination. Gaming Profile
chat-provider Contributes a pill to the AI chat provider list ([chat_provider]: id, label, backend, state, optional requires_layer/requires_data). Core keeps the transport — backend must name one core already speaks, currently only ollama — and the plugin supplies which model and which system prompt, in a JSON file it writes under ~/.config/aphotic/plugins/<plugin>/ ({"model": ..., "systemPrompt": ...}) that the shell watches live. Plugin providers are listed before the core ones. —
action Contributes a named, invokable action ([action] through [action_5], up to five per plugin) rather than a place to draw — it turns up wherever the shell offers actions (the notch’s Commands tile, and qs -c aphotic ipc call aphotic action <id> for keybinds and scripts) instead of being registered once per surface that wants it. Namespaced by install name (pet.switchPet), so no core file or other plugin ever names it directly. Desktop Pet (Switch pet)
profile-hook Not hosted in 2.0.7. Meant to fire on a registered profile’s phase changes (activate/deactivate). A plugin can declare it, but nothing runs it on this build. OpenRGB Sync declares it
agent-event-hook Not hosted in 2.0.7. Meant to fire on live AI-agent session events (the agent-events.jsonl stream). A plugin can declare it, but nothing runs it on this build. OpenRGB Sync declares it

The shell runtime API ([api])

Surfaces and hooks say where a plugin mounts; [api] says what it may ask the running shell for once it’s there:

[api]
version = 1
uses = ["context.observe", "resource.observe"]

The shell hands each plugin a handle carrying only the calls uses names, checked again at call time, so disabling or removing the plugin revokes it. This build speaks API version 1, with context.observe, context.request, resource.observe, surface.declare and notifications.publish; context.request is rate-limited per plugin. A plugin written against a newer API version is refused at install. aphotic plugin api [--json] prints what the running build speaks.

[owns] is informational, not an auto-revert list: config_keys/external_config show up in aphotic plugin list --json and Settings → Plugins so a plugin’s footprint stays auditable, but removing a plugin doesn’t reset those keys to their built-in defaults — a stale agentGraphQuality value, for instance, causes no dead UI or drift on its own, and a reinstall just picks the same value back up. Files and UI surfaces do get removed symmetrically on aphotic plugin remove — that part is a hard guarantee, not just a convention.

Current plugin roster

All distributed from the separate aphotic-plugins repo — aphotic plugin install <name> clones it locally the first time, then just copies the one plugin out of it.

18 plugins as of this writing, aphotic plugin list --remote is always the current count:

Plugin Category Capabilities What it does
Agent Graph ai ui-surface The live tool-call graph + replay transport (SUPER+D’s Command Center tab), plus its own Settings section. Activates only once the ai layer is enabled and a harness is actually configured — a bare Ollama-only setup has nothing for it to render. See Agentic Workflows.
Agent Audit ai ui-surface A workspace-hosted ([ui.workspace], SUPER+SHIFT+W) run-evidence inspector: live activity plus replay timelines over the same local event history Agent Graph draws from.
Agent Notch Tile ai ui-surface Notch tile for AI harnesses: waiting-for-input badge, active harness/phase, live subagent count, today’s token usage, and the local provider’s GPU VRAM claim.
claude-hooks ai harness-hook Wires Claude Code’s own hook payload straight into ~/.claude/settings.json — no translator needed, its payload already matches the shared agent_hook.py sink’s expected shape — and claims the statusLine slot to read quota windows.
codex-hooks ai harness-hook Wires a translator (Codex’s tool-name aliases and event-field names differ slightly from Claude’s) into ~/.codex/hooks.json.
opencode-hooks ai harness-hook Symlinks a translator into OpenCode’s plugin auto-discovery directory (~/.config/opencode/plugins/) — no config-file edit needed, OpenCode loads any .js/.ts file dropped there.
llama-swap ai ui-surface Notch tile for a llama-swap host: reachability, loaded models, measured GPU memory, live generation stats, inference mode, and per-model unload. Needs the ai layer.
Hardware Advisor (llm-fit) ai ui-surface, cli Hardware-aware local model recommendations from llmfit, docked into the AI Settings pane, plus aphotic ai fit for the same from the CLI.
Dev Notch Tile dev ui-surface Notch tile for the Dev profile: open project, lifecycle phase, resource claims, a badge when a lockfile drifts from its manifest.
Dev Ports dev ui-surface A workspace tool ([ui.workspace], SUPER+SHIFT+W) that lists local HTTP dev servers by scanning listening loopback ports, with online/cached status and one click to open. Needs the dev layer.
direnv Notice dev project-hook Notifies you when a project opened from the launcher’s @ mode has an .envrc, so you know to review/allow it.
Gaming Profile gaming profile Gamemode detection, do-not-disturb while you play, and a foreground GPU VRAM claim so a local model yields the memory.
Live Wallpapers theming ui-surface Plays video wallpapers (.mp4, .webm, .mov, .mkv, .m4v) on the desktop through [ui.background]. The base shell already lists videos in the picker and applies them as an extracted still, which is what the palette comes from; this draws the looping video over that same still, so removing the plugin leaves your video wallpapers working as stills. Pauses on battery and whenever a window covers the workspace, both on by default. Ungated: works on every install.
OpenRGB Sync theming theme-hook, profile-hook, agent-event-hook Syncs RGB lighting to the active theme’s accent over OpenRGB’s SDK. It also declares reactions to the Gaming profile and live AI-agent sessions, but 2.0.7 doesn’t host those two capabilities, so it installs as partially hosted and only the theme sync runs.
Deep Signal Screensaver theming ui-surface, cli An idle screensaver ([ui.fullscreen-overlay], trigger = "idle"): the wordmark resolves out of noise like a signal surfacing from deep water, over a drift of bioluminescent particles, coloured from whatever theme is active. Adds aphotic screensaver to set the text or import an image as ASCII.
Desktop Pet theming ui-surface, action A creature that lives on the desktop — drag it anywhere (anchor = "free"), it wanders its own patch, wears the theme, and reacts when clicked. Three drawn pets ship with it — Lumen, Cipher and Kozumi, each repainted to the live palette — picked in Settings → Appearance → Desktop Pet, or drop your own sprite sheet into ~/.config/aphotic/pets/<name>/. Its own action menu ([ui.pet_action], [ui.pet_action_2]) and a switchPet action other surfaces can invoke. Ungated: works on every install.
Spectrum core ui-surface An audio spectrum along the bottom of the screen, painted in the live theme accent, asleep whenever nothing is playing — another [ui.overlay] surface, with its own intensity/smoothing sliders docked into Appearance. Ungated: works on every install.
Workspace Session Log productivity workspace-hook Keeps a local, timestamped log of when each Workspace Profile launches.

Installing all three harness-hook plugins for the harnesses you actually use, plus agent-graph/agent-audit/agent-notch-tile, reproduces the exact live-tracking behavior that used to be bundled automatically into the ai layer before the plugin architecture landed (v2.0.0) — see Agentic Workflows’s “Turning it on” section for the one-time setup.

Four UI plugins above — Desktop Pet, Spectrum, Live Wallpapers and Deep Signal Screensaver — are deliberately ungated: their surfaces declare no requires_layer and no requires_data, so they’re relevant to every install regardless of profile or layers. (theming is a category, not a layer: the four gateable layers are ai, dev, gaming and security, where security means any exploit/exploit-* layer, so a theming plugin has nothing to gate on and being installed is the opt-in.)

CLI

aphotic plugin list [--remote] [--refresh] [--json] [--category <name>]   # installed, or browse the (cached) remote index
aphotic plugin install <name> [--link]                        # clone (or symlink, for local dev) a plugin
aphotic plugin update <name>                                  # refresh a plugin's files from the repo
aphotic plugin enable|disable <name>
aphotic plugin remove <name>
aphotic plugin trust-security-index / untrust-security-index  # opt into (or back out of) the security-category index
aphotic plugin validate <dir>                                  # check a plugin folder before installing it
aphotic plugin api [--json]                                    # the runtime API version and calls this build offers

aphotic plugin list flags installed files that have drifted from the registry — if the on-disk version of a plugin doesn’t match what plugins.json records, it shows a drift indicator so you know an update is available.

Settings → Plugins covers the same ground with a GUI: an Installed list (enable/disable/remove, missing-dependency warnings, capability chips, a “Wires: …”/”Uses settings: …” disclosure for [owns]) and a Browse available list pulled live from the registry’s index, filterable by category.

See also

  • Build a Plugin — a worked tutorial: manifest, component, validate, test, publish
  • Agentic Workflows — the flagship ui-surface plugins (Agent Graph, Agent Audit, Agent Notch Tile) and the three harness-hook plugins they pair with
  • Supported Features — where the plugin system fits among everything else
  • CLI Reference — full command syntax