Out-of-process plugins
Apiary protocol 1 runs third-party extensions as child processes instead of loading Go binaries. A plugin crash, invalid response, or timeout terminates only that invocation; it does not crash the dispatcher. Plugins are never executed during discovery or configuration validation.
Install and enable
An installed plugin is a directory containing apiary-plugin.json and the
executable named by that manifest. The default search directory is
.apiary/plugins beside apiary.yaml; plugin_dirs overrides it.
plugin_dirs:
- .apiary/plugins
- ~/.local/share/apiary/plugins
plugins:
- id: dev.apiary.event-file
enabled: true # omitted means true
timeout: 5s # default: 10s, per invocation
config:
path: .apiary/events.jsonl
Use apiary plugins, apiary plugins inspect <id>, and
apiary plugins validate to inspect installations. apiary validate also
discovers every enabled plugin and validates its configuration against the
manifest's JSON Schema. Set enabled: false to keep an installed/configured
plugin available without starting or schema-validating it.
Relative plugin directories resolve beside apiary.yaml. Discovery is
deterministic and rejects duplicate IDs rather than choosing one by path order.
Manifest version 1
{
"schema_version": 1,
"id": "dev.apiary.event-file",
"version": "1.0.0",
"apiary": ">= 0.10.0-0",
"protocol": 1,
"executable": "apiary-plugin-event-file",
"capabilities": ["event_exporter"],
"config_schema": {
"type": "object",
"properties": {"path": {"type": "string", "minLength": 1}},
"required": ["path"],
"additionalProperties": false
},
"security": {
"network": false,
"read_paths": [],
"write_paths": ["configured event file"],
"secret_env": []
}
}
idis a lowercase reverse-DNS identifier and is stable across releases.versionfollows semantic versioning;apiaryis a semantic-version constraint.protocolmust be1. Unknown manifest/protocol versions fail closed.executableis relative to the plugin directory. Absolute paths, traversal, symlinks, non-regular files, and non-executable files are rejected.capabilitiesacceptssource,runner,workflow_action,approval_provider,secret_provider, andevent_exporter.config_schemasupportstype,properties,required,additionalProperties,enum,items,minLength,maxLength,minimum,maximum,minItems, andmaxItems. Unknown validation keywords fail closed.securityis an inspectable declaration, not an OS sandbox guarantee.
Protocol version 1
Apiary starts a fresh process for each invocation, writes one compact JSON object plus a newline to stdin, and expects one JSON response on stdout. Diagnostic output belongs on stderr. Stdout is limited to 4 MiB and captured stderr to 64 KiB. The per-instance deadline cancels and terminates the child process.
Request:
{
"protocol": 1,
"request_id": "1784736000000000000",
"capability": "event_exporter",
"method": "export",
"config": {"path": ".apiary/events.jsonl"},
"payload": {"schema_version": 1, "type": "runner.started"}
}
Success and failure responses echo the request ID:
{"protocol":1,"request_id":"1784736000000000000","result":{"written":true}}
{"protocol":1,"request_id":"1784736000000000000","error":{"code":"write_failed","message":"permission denied"}}
Capability method vocabulary:
| Capability | Protocol methods |
|---|---|
source |
connect, poll, acknowledge, write_result, plus optional source capabilities |
runner |
configure, run |
workflow_action |
run |
approval_provider |
notify, remind, escalate |
secret_provider |
resolve |
event_exporter |
export |
Protocol envelopes are stable; capability payloads use the corresponding
versioned Apiary internal contract. The first runtime integration is
event_exporter. The remaining capability names reserve the same manifest and
transport boundary for their proxies without requiring a new installation model.
Go plugin authors can import github.com/orlandoburli/apiary/sdk/plugin and call
plugin.Main(handler). The SDK decodes one request, rejects protocol mismatches,
echoes the request ID, and emits the structured response envelope. Plugins in
other languages can implement the JSON examples above directly.
Trust and secrets
Plugins execute with the Apiary service account's OS permissions. Before install:
- Obtain the binary from a trusted publisher.
- Verify its checksum and signature out of band.
- Inspect the manifest's network, path, and secret requirements.
- Install into a directory writable only by the Apiary operator/service account.
Apiary does not download or auto-upgrade plugins. Upgrade atomically by verifying
the new artifact in a staging directory, stopping Apiary, replacing the complete
plugin directory, running apiary plugins validate, and restarting. Keep the old
directory for rollback, but never leave two versions with the same ID in searched
directories.
Child processes receive a minimal environment (PATH, home/temp/locale/timezone)
plus only variables listed in security.secret_env. Secret values are never
included in protocol requests or error messages. Plugin configuration should
contain references or non-secret options, not credentials. Event exporters
receive the persisted event after Apiary's metadata redaction.
Process isolation is not a security sandbox: a malicious executable can still use the service account's filesystem and network access. Use OS-level sandboxing, containers, restricted service users, and egress controls where the trust level requires them.
Reference plugin
src/examples/plugins/event-file demonstrates an event_exporter that appends one
redacted event JSON object per line. Its README contains build and installation
commands.