Skip to main content
A plugin is a directory holding a plugin.json and, usually, one TypeScript module. Cordial runs the module as a sandboxed Deno process and talks to it in newline-delimited JSON over stdin and stdout. A plugin cannot open a file, a socket or a subprocess, so asking Cordial to do things through capabilities is the only surface there is. Why: ADR-003, ADR-007. A manifest with no entry is a plugin made of data: a texture pack, a set of flags, a preferences page. Cordial logs data only, nothing to start and carries on (ADR-021). The rest of this page is about the kind that runs.

The smallest plugin that works

1

Create the files

plugin.json:
main.ts:
2

Grant it log

Until something is granted the plugin is not refused, it is never started: plugin hello: no capabilities granted, not started. Flip the log switch on the plugin’s row in Settings, or write ~/.local/share/cordial/profiles/default/plugin-grants.json:
Grants are per profile (ADR-013), so approving a plugin in a throwaway profile does not approve it in the one you play on.
3

Run it

Start Cordial and look for the plugin’s line in the client output:
Never write anything but protocol lines to stdout. console.log in Deno writes to stdout, which is the wire. The first line Cordial cannot parse as a request ends the session (plugin hello: sent something unreadable (…)) and the process is killed. Use console.error, which goes to Cordial’s own output, or log.write.
Keep the directory name and the manifest id identical. Cordial does not check. Grants, settings, events and flags.set key off the manifest id, but the flag layer read back at startup and the enablement check key off the directory name. A plugin in hello-dev/ calling itself hello writes flags into a hello/ directory that nothing installed.

plugin.json

Only id is required. Discovery is quiet in two cases:
  • A directory with no plugin.json is passed over without comment. One whose plugin.json will not parse is announced as plugin: /path/plugin.json is not loadable (…).
  • A directory whose name starts with . is skipped, because the installer stages half-unpacked plugins there.

Capability names are not method names

flags.write is a capability: what a user grants, and the string a refusal names. flags.set is the method it gates. Calling flags.write gets unknown method "flags.write", an error rather than a permission error. settings.read is a capability and settings.get is the method. The full list and the method each one gates is on Capabilities and grants.

Test without packaging

You do not need an archive, an index or an install step. Cordial discovers plugins from a directory and follows symlinks to directories. Give the run its own data root so you stay off the profile you play on:
Use a path on real disk rather than /tmp, which is tmpfs and comes out of RAM, and delete it afterwards. just dev and just client take the same variable. Two development switches exist. Neither is supported configuration:
Set --run 60 generously. Plugins start late in bring-up, after the engine reports itself up, so a short run can exit before your plugin has said anything. How short is too short has not been measured (INFERRED from where start_all sits in bring-up).

Seeing a plugin’s output

If a plugin subscribes to events and receives none, rule out that there are none to receive. A plugin event is written straight into your stdin with no queue, so nothing arrives if the publisher is not running or was never granted events.publish. A core event is addressed by capability, so check lifecycle.read was granted in this profile, and which of the events you are waiting for are actually published (events).

Next

Protocol and sandbox

messages, pushes, what Deno permits, how a plugin starts.

Capabilities and grants

the list, default deny, refusals, per-profile grants.

Events

core events and plugin-declared events.

FastFlags

read the resolved set, contribute a layer.

The rest of the surface

notifications, presence, URLs, session state, settings, preferences, asset overlays.

What you cannot do

the walls.
plugins/README.md covers versions, dependencies and publishing. Decisions: ADR-008 (Deno), ADR-018 (sub-sandbox), ADR-020 (preferences).