Files
self 4658b76241
CI / Test (push) Failing after 22s
feat: prefix Kobold plugin slug
Rename Kobold and Trust plugin identity references to tribe-one-prefixed slugs across runtime API, e2e fixtures, and docs.
2026-06-17 22:33:25 +02:00

4.5 KiB

Plugin Contract

Manifest

manifest.json is the runtime contract consumed by Tribes:

  • id is the globally unique plugin identity; slug is the local URL/assets namespace.
  • otp_app should be vendor-prefixed and must match the Mix application name.
  • entry_module must be a loadable module ending in .Plugin.
  • provides declares capabilities exported by the plugin.
  • requires declares hard plugin/API contracts beyond the host_api foundation.
  • enhances_with declares optional host capabilities.
  • migrations should be true when priv/repo/migrations contains plugin migrations.
  • children should be true when the plugin starts its own supervision tree.

host_api is the versioned foundation contract. It provides the supported Phoenix, Ash, PubSub, data, and cluster-event APIs for plugins. Do not add separate framework capability requirements for APIs that belong to that foundation.

Declare org.tribe-one.caps.ui@1 in requires when rendering through Tribes.Plugin.Layouts.app, importing Tribes.UI.Components, or using use Tribes.UI.

Entry Modules

The plugin entry module is:

defmodule TribeOne.TribesPlugin.Kobold.Plugin do
  use Tribes.Plugin.Base, otp_app: :tribe_one_kobold
end

Keep plugin implementation code under your own namespace. The default generator uses TribeOne.TribesPlugin.*; third-party plugins should use their own organization or project namespace.

Host Dependencies

tribes_plugin_api is the release-facing host_api foundation. It carries the supported plugin behaviours, helpers, Phoenix/Ash data surface, and sync DSL. Do not add the full :tribes app as a production dependency.

Host services are exposed through public facade modules in Tribes.Plugin.Services. Use Tribes.Plugin.Services.Alliance for local tribe metadata and tribe users, Tribes.Plugin.Services.Metrics for compact metric rollups, and Tribes.Plugin.Services.Logs for operational plugin logging.

Localization

Keep Kobold-specific copy in Kobold's own Gettext backend and priv/gettext catalogs. For shared Tribes UI labels that are documented as public plugin API, import Tribes.Plugin.Gettext and call tgettext/2:

import Tribes.Plugin.Gettext

tgettext("Cancel")
tgettext("Save")

Do not call arbitrary internal Tribes Gettext domains from plugin code.

The generated plugin intentionally splits the :tribes path dependency by environment:

  • In :dev, :tribes is compile-only. This lets mix compile create the entry-module beam expected by the host plugin manager without starting a nested Tribes application.
  • In :test, :tribes is runtime-enabled. Host-backed tests need the real repository, endpoint pipeline, and plugin contract helpers.

Ash Resources

For cluster-synced plugin data, add Ash resources under the plugin namespace and use extensions: [AshNostrSync] only for data that should replicate across the cluster. Prefer one UUID primary key per synced resource. Document any local-only tables, retention policies, or side effects in plugin docs.

Kobold Dataset Resources

Kobold's MVP uses fixed plugin-owned Ash resources as its storage and sync contract. User-defined dataset resources are dynamic schema data, not runtime Elixir modules, runtime Ash resources, or per-resource database tables.

Initial fixed resources:

  • Dataset
  • ResourceDefinition
  • DatasetEvent
  • RecordProjection
  • MergeProposal

The first shipped foundation includes Dataset, ResourceDefinition, DatasetEvent, and local-only RecordProjection. Public dataset/resource/event mutations use AshNostrSync.PublishChange; private mutations deliberately do not publish and remain local. RecordProjection is rebuildable from DatasetEvent history. MergeProposal remains planned for the fork/merge milestone.

Do not generate Ash resources or migrations for each user-defined dataset resource in the first cut. If a future optimized dataset type needs dedicated Ash resources, document that as a specialized plugin compatibility layer rather than the generic Kobold storage model.

Local Supertest API

Kobold exposes a loopback-only JSON API under /plugins-api/tribe-one-kobold for smoke and supertest scenarios:

  • GET /health
  • GET /schema
  • POST /reset
  • POST /datasets
  • POST /datasets/:dataset_id/resources
  • POST /datasets/:dataset_id/records
  • GET /state?run_id=...
  • POST /projections/rebuild

This API is not the durable user-facing federation contract; it exists to prove plugin install, migrations, public dataset sync, and private local-only dataset behavior in live scenarios.