This document covers the current system model and mechanics. It answers how Plugboard works today. For intent and project boundaries, see Design.
Plugboard has three layers:
The exchange stores messages and claims in SQLite and exposes the core operations:
- publish a message
- read by topic or conversation
- claim the next message for a topic
- transition claims to completed, failed, or timed out
Messages are immutable records. Claims are separate operational state.
plugboard run is the worker host. It:
- waits for work on one topic
- claims one message
- invokes a plugin
- publishes a follow-up on success, failure, or timeout
Workers record both:
worker_group: the stable logical worker classworker_instance_id: the concrete running process
The baseline plugin contract is passive and Unix-like:
- read the claimed message body from
stdin - write a result to
stdout - exit
The worker maps the outcome into follow-up topics and claim status.
Plugboard uses SQLite at .plugboard/plugboard.db by default.
SQLite is the source of truth for:
- messages
- claims
- conversation history
The schema has two durable entities:
Fields in use today:
idtopicbodycreated_atparent_idconversation_idproducermetadata_json
If a message does not specify conversation_id, the exchange uses the
message id. Follow-up messages inherit the parent conversation.
Fields in use today:
idmessage_idworker_groupworker_instance_idclaimed_atlease_untilstatuscompleted_at
Claim status is one of:
activecompletedfailedtimed_out
A claim is live only while it is active and lease_until is still in
the future.
plugboard publish appends one message to a topic. If the topic ends in
.request, the CLI also records local tracking metadata for
notifications.
Workers claim the oldest claimable message for their topic in a SQLite transaction. Before claiming, the exchange deletes expired active claims so stale ownership does not block recovery.
The worker runs the plugin once for that message. There is no persistent backend session in the core worker model.
The worker transitions the claim, then publishes a follow-up message:
- success: configured success topic
- failure: configured failure topic
- timeout:
<request-topic>.timed_out
Follow-ups keep parent_id and conversation_id, so later reads can
inspect the whole thread.
The default operating model is asynchronous:
- publish work
- keep going
- read replies later
Plugboard also has a thin blocking helper:
This command publishes a request, emits message_id and
conversation_id, then waits for the first correlated reply on the
configured success or failure topic.
It does not introduce a new request entity. It still uses the normal message log and conversation correlation.
This command reads one conversation and reports whether a terminal success or failure reply exists yet.
This is the normal consumption command. It reads messages by topic or by conversation.
This is the forensic command. It is for raw history and claim-state debugging rather than routine use.
Workers and request waiters use advisory wakeups plus bounded SQLite re-checks.
Current defaults:
- worker wait timeout: 250 ms
- worker fallback re-check: 250 ms
- request wait timeout: 250 ms
- request fallback re-check: 250 ms
The wakeup path improves responsiveness, but correctness still depends on re-checking SQLite.
plugboard run waits for work, handles a message, drains any immediately
claimable backlog, then waits again.
plugboard run --once blocks until one matching message exists, handles
it, publishes the follow-up, and exits.
plugboard notify is a local helper on top of the exchange. It reads
tracked conversations from .plugboard/tracked-conversations.json,
checks them for terminal replies, emits one advisory notification, and
marks them as notified.
This does not affect message correctness. The exchange remains the source of truth.
plugboard publishplugboard readplugboard checkplugboard notifyplugboard requestplugboard inspectplugboard run
The current architecture supports several plugin styles without changing the core:
- command-style transforms
- local model adapters such as
ollama-plugin - API adapters such as
gemini-plugin - wrappers around warm or session-backed tools
Those variants all fit the same message, claim, execute, follow-up cycle.