4.7 KiB
Tribes Plugin Template
Template for creating Tribes plugins.
Getting Started
- Click "Use this template" on GitHub to create your own repo
- Clone and rename:
git clone https://github.com/you/your-plugin.git
cd your-plugin
./scripts/rename.sh your_plugin YourPlugin
- Edit
manifest.json— set description, capabilities, requirements - Implement your plugin in
lib/your_plugin/plugin.ex - Run tests:
mix deps.get
mix test
Development
For local development alongside a Tribes checkout:
# Symlink into the host plugins directory once
cd /path/to/tribes
ln -s /path/to/your-plugin plugins/your_plugin
# Start Tribes dev server
iex --sname dev -S mix phx.server
Then edit the plugin in its own repo. In development, the host now watches symlinked external plugins and automatically:
- runs
mix compilefor Elixir/HEEx or manifest changes - runs
npm run build --prefix assetswhenassets/package.jsonis present - reloads the plugin in the running Tribes VM
- triggers a Phoenix browser reload after the rebuild finishes
That means the normal loop is:
cd /path/to/your-plugin
mix deps.get
mix test
# in another terminal
cd /path/to/tribes
iex --sname dev -S mix phx.server
If your plugin does not need a custom frontend pipeline, you can skip
assets/package.json and write browser-ready files directly under assets/js
and assets/css; the host dev watcher will copy them into priv/static for
you in development.
Project Structure
your_plugin/
├── manifest.json # Plugin metadata (Nix build + runtime)
├── mix.exs # Dependencies
├── lib/
│ ├── your_plugin/
│ │ ├── plugin.ex # Tribes.Plugin entry point
│ │ └── application.ex # OTP supervision tree (optional)
│ └── your_plugin_web/
│ └── live/ # LiveView pages
├── assets/ # JS/CSS (one bundle per plugin)
│ ├── package.json # Optional build script used by dev + Guix packaging
│ └── package-lock.json # Optional, recommended for reproducible builds
├── priv/
│ ├── static/ # Built assets for release
│ └── repo/migrations/ # Ecto migrations
└── test/
Manifest
manifest.json declares your plugin's identity and capabilities:
{
"name": "your_plugin",
"entry_module": "Tribes.Plugins.YourPlugin.Plugin",
"host_api": "1",
"otp_app": "your_plugin",
"provides": ["some_capability@1"],
"requires": ["ecto@1"],
"enhances_with": ["inference@1"]
}
- entry_module — must be
Tribes.Plugins.*.Plugin - otp_app — required and must match
name - provides — capabilities this plugin makes available
- requires — hard dependencies (build fails without them)
- enhances_with — optional dependencies (plugin degrades gracefully)
See the Plugin System docs for the full specification.
Plugin Data
If your plugin adds Ash resources that should replicate cluster-wide, use
extensions: [AshNostrSync] on those resources and follow the host defaults:
- persisted attributes sync by default unless explicitly excluded
- synced resources should use one UUID primary key by default
AshNostrSyncrequires exactly one primary key; composite keys are not supported- synced resources get extension-managed
deleted_atsoft-delete support by default - destroy actions can use
soft_delete()when they should tombstone rows soft_delete?(false)is the opt-out if a synced resource should not use the default tombstone model
Keep resource-specific side effects explicit in actions. AshNostrSync owns the
default tombstone/filter/projection behavior, but your plugin still decides which
actions are semantic deletes and what else they should do.
Testing
Three test levels:
- Unit tests (
test/your_plugin/) — plugin logic in isolation - Manifest tests (
test/your_plugin/manifest_test.exs) — manifest schema validation - Contract tests (
test/contract_test.exs) — runtime spec matches manifest
Run all: mix test
For DB setup/migrations in local development, run:
mix tribes.migrate
This runs Tribes + Parrhesia + plugin migrations via Tribes.Release.
Building for Release
MIX_ENV=prod mix compile
npm run build --prefix assets
mkdir -p dist/your_plugin
cp -r _build/prod/lib/your_plugin/ebin dist/your_plugin/
cp -r priv dist/your_plugin/
cp manifest.json dist/your_plugin/
For Guix-based deployment, package your plugin in the guix-tribes channel and
enable it from the node config.
Licence
TODO: Choose a licence.