Plugin Data Model

The current schema contains exactly 23 plugin tables. They separate code identity, user authority, device execution, and durable delivery so that changing one concern cannot silently change another.

Catalog and Releases

TableOne row representsImportant fields and statesPrimary users
pluginA stable plugin identity, not a versionplugin_key, publisher, presentation, visibility, revision; a null owner_user_id is catalog-owned, otherwise privateCatalog sync, private plugin creation, catalog API
plugin_package_blobA content-addressed uploaded package objectDigest, size, object key; staging / available / gc_candidate / deleted; maximum size 64 MiBPackage ingest, release creation, garbage collection
plugin_releaseAn immutable version with provenance and trust evidenceManifest and digest; compiled_builtin / package_blob; bundled / catalog / git / local; trusted_first_party / approved_catalog / private_untrusted; available / quarantined / revokedCatalog import, install, lifecycle, authority resolution
plugin_release_capabilityOne capability declared by a releaseCapability type/key, strict config JSON and digest, declared permissions, ordinalConnection setup, permissions, tool catalog, runtime selection, health

plugin_package_blob is server-side publication storage. It is not the Runner's channel-scoped runtime cache. An installation pins a release and never changes merely because a newer version appears in the catalog.

Installations and Authority

TableOne row representsImportant fields and statesPrimary users
plugin_installationA user's selection of one plugin releaseUser, plugin key, release, revision; installing / disabled / enabling / enabled / disabling / repair_required / uninstalling / uninstalledInstallation API, lifecycle Worker, all authority readers
plugin_installation_operationAn idempotent asynchronous lifecycle commandinstall / update / enable / disable / repair / uninstall; pending / running / succeeded / failed / cancelled; attempt, error code, diagnosticAPI reservation, lifecycle queue/Worker, UI polling
plugin_connectionA provider identity or local profile for an installationProvider/mode/region, principal, resource scope, device, credential version, default selection; pending / authorizing / ready / reauthentication_required / unavailable_device / failed / revoked / deletingOAuth or device profile setup, readiness, tool selection, Channel
plugin_secretA server-owned encrypted credential envelopeSecret kind/version, key id, AAD, nonce, ciphertext; active / replaced / revokedServer-side OAuth or credential connectors only
plugin_permissionOne current or superseded permission-policy revisionOptional capability scope, policy v1, revision, digest, supersedes_idPermission UI, readiness, tools, effects, Runner authority
plugin_oauth_attemptOne short-lived OAuth state and PKCE exchangeHashed state, encrypted verifier; pending / consuming / succeeded / failed / expiredOAuth begin/callback; terminal rows clear verifier encryption fields

Device-local Feishu and GitHub credentials must not use plugin_secret. Their plugin_connection.device_id is set, credential_id remains null, and the operational secret stays in the selected Desktop's OS-backed storage.

Permission documents are append-only revisions. A document may cover the entire installation (capability_id is null) or one capability. The policy fields are deliberately closed:

  • data_reads, device_access, and inbound_events: allow or deny
  • local_reversible: prompt, allow, or deny
  • external_writes: prompt or deny; unconditional allow is not valid

Devices and Runtimes

TableOne row representsImportant fields and statesPrimary users
plugin_device_enrollmentA Desktop's durable cryptographic identity and current leaseEd25519 public key/fingerprint, channel, platform/version, catalog digest, lease_generation, expiry; pending / active / offline / fenced / revokedDesktop enrollment/renewal, device bridge, lifecycle, authority checks
plugin_device_actionA short-lived interactive task for one deviceauthorize_local_profile / configure_local_secret / select_local_plugin_directory; secret-free canonical request; pending / claimed / awaiting_local_confirmation / completed / cancelled / expired / failedWeb/API creates; Desktop claims, confirms, signs, and completes
plugin_runtime_instanceA persistent configured Agent Runtime instanceInstallation/release/capability/device/profile, config, protocol/provider version; pending / ready / incompatible / unavailable / revokedCodex, Claude, OpenClaw, and Hermes profile setup and selection
plugin_runtime_sessionA chat Session bound to one runtime executionDirect plugin_authority_digest, workspace authority, runtime/device generation, provider session, epoch and event heads; starting / running / waiting_input / waiting_approval / cancelling / recovering / cancelled / completed / failed / lost / closedRuntime dispatch, recovery, reconnect, event ingest
plugin_runtime_eventOne immutable, hash-chained runtime eventEpoch/sequence, previous/event/payload/projection digests, provider event identity, projection timeRunner ingest and canonical projection; ACK advances after persistence

Enrollment is durable identity; a lease is short-lived liveness. Normal reconnects reuse an enrollment. Revocation advances the generation and fences old connections and requests.

plugin_runtime_instance applies to agent_runtime capabilities. A local_provider such as Feishu or GitHub normally uses a ready connection and the Runner's provider activator without inventing a runtime instance.

Effects and Inbound Delivery

TableOne row representsImportant fields and statesPrimary users
plugin_effect_intentAn immutable authorization envelope for a potentially external writePins installation/release/capability/tool/connection/permission/device/runtime/workspace, arguments, destination, idempotency and digests; mutable state/evidence/receipt/revision onlyEffect preparation, approval, dispatch, reconciliation
plugin_inbound_eventAn authenticated, deduplicated, normalized provider eventProvider/connection/delivery key, bounded raw body and digest, canonical event and digest, route/session; received / normalizing / normalized / routed / unrouted / failedFeishu Channel and declared inbound pipelines; normalization/routing/cleanup
plugin_inbound_outboxTransactional publication of a normalized inbound event into a durable product factTarget, canonical payload/digest, retry timing, durable record; pending / publishing / published / failedInbound admission and publication; never an offline-provider queue
plugin_channel_routeAn external conversation identity mapped to a SciLaxy SessionConnection, canonical external identity/digest, Session; active / disabled / deletedChannel binding and inbound routing
plugin_channel_outboxOne outbound Channel effect for a TurnPins authority revisions/device generation/effect/target/payload/idempotency; pending / dispatching / delivered / failed_before_dispatch / uncertainTurn settlement and local provider dispatch
plugin_webhook_endpointA GitHub public ingress bound to exact local authorityInstallation/connection revisions, device generation, release/authority digest; token stored as digest, verifier encrypted; active / revokedWebhook configuration and public handler
plugin_github_webhook_deliveryGitHub delivery deduplication, claim, and local-persistence ACK evidenceDelivery id, payload digest, claim lease, local receipt digest; dispatching / acknowledgedGitHub handler and online device relay; no raw provider body
plugin_audit_eventOne append-only, redacted plugin audit factActor/entity/action/outcome, revisions, request/trace/effect/provider, bounded metadataLifecycle, device, connection, effect, webhook, activity UI

An effect moves through a closed state machine:

prepared -> awaiting_approval -> approved -> dispatching
                                  |             +-> succeeded
                                  |             +-> failed_before_dispatch
                                  |             +-> uncertain -> reconciled_succeeded
                                  |                           -> reconciled_failed
                                  +-> denied

Before dispatch, it may also become expired, stale, or cancelled_before_dispatch. failed_before_dispatch is safe to retry; uncertain may have crossed the provider boundary and must be reconciled first.

Relationships

flowchart LR
  Plugin[plugin] --> Release[plugin_release]
  Release --> Capability[plugin_release_capability]
  Release --> Install[plugin_installation]
  Install --> Operation[plugin_installation_operation]
  Install --> Connection[plugin_connection]
  Install --> Permission[plugin_permission]
  Device[plugin_device_enrollment] --> Action[plugin_device_action]
  Action --> Connection
  Connection --> Runtime[plugin_runtime_instance]
  Runtime --> RuntimeSession[plugin_runtime_session]
  RuntimeSession --> RuntimeEvent[plugin_runtime_event]
  Install --> Effect[plugin_effect_intent]
  Connection --> Route[plugin_channel_route]
  Route --> Inbound[plugin_inbound_event]
  Inbound --> InboundOutbox[plugin_inbound_outbox]
  Effect --> ChannelOutbox[plugin_channel_outbox]
  Connection --> Webhook[plugin_webhook_endpoint]
  Webhook --> Delivery[plugin_github_webhook_delivery]

The diagram omits evidence references. Effects and Channel outbox rows also pin permission and connection revisions, release/config/tool digests, and device generation so an approval for one authority cannot execute as another.

Removed Session Tables

Migration 00093_remove_session_plugin_authority.sql removed:

  • plugin_session_snapshot
  • plugin_session_capability
  • agent_plugin_binding
  • the Session plugin_snapshot_id column

Current tool visibility resolves from enabled account installations, default connections, and current permissions. Runtime sessions, effects, and Channel dispatches freeze their own direct authority. Do not add compatibility reads or recreate the removed snapshot model.