-
Notifications
You must be signed in to change notification settings - Fork 0
Writing Modules
This guide covers everything you need to build a MuxCore module from scratch.
A module is a Go package that:
- Implements
contracts.Module(lifecycle) - Calls
contracts.Register()ininit()(auto-discovery) - Implements one or more capability contracts (the actual functionality)
- Has a
muxcore.jsonat its repo root (marketplace metadata)
my-module/
go.mod <- depends on github.com/Muxcore-Media/core
module.go <- your implementation
muxcore.json <- marketplace metadata
package mymodule
import (
"context"
"github.com/Muxcore-Media/core/pkg/contracts"
)
func init() {
contracts.Register(func(deps contracts.ModuleDeps) contracts.Module {
return NewModule(deps.EventBus)
})
}
type Module struct {
bus contracts.EventBus
}
func NewModule(bus contracts.EventBus) *Module {
return &Module{bus: bus}
}
func (m *Module) Info() contracts.ModuleInfo {
return contracts.ModuleInfo{
ID: "my-module",
Name: "My Module",
Version: "1.0.0",
Kinds: []contracts.ModuleKind{contracts.ModuleKindProvider},
}
}
func (m *Module) Init(ctx context.Context) error { return nil }
func (m *Module) Start(ctx context.Context) error { return nil }
func (m *Module) Stop(ctx context.Context) error { return nil }
func (m *Module) Health(ctx context.Context) error { return nil }Every module receives a contracts.ModuleDeps struct with seven services:
| Field | Interface | Use |
|---|---|---|
EventBus |
contracts.EventBus |
Publish and subscribe to events |
Registry |
contracts.ServiceRegistry |
Discover other modules at runtime |
Routes |
contracts.RouteRegistrar |
Register HTTP handlers |
Cluster |
contracts.Cluster |
Cluster membership (nil if standalone) |
Storage |
contracts.StorageOrchestrator |
Object storage via IDs, not paths |
WorkerPool |
contracts.WorkerPool |
Distributed task scheduling |
Audit |
contracts.AuditLogger |
System-wide audit logging |
Implement contracts.Downloader. Discovered via FindByKind(ModuleKindDownloader).
Example: downloader-qbittorrent
Implement contracts.Indexer. Discovered via FindByKind(ModuleKindIndexer).
Example: indexer-jackett
Implement contracts.AuthProvider. Core wires the first auth module into the API middleware automatically.
Example: auth-local
Implement contracts.Playback. Discovered via FindByKind(ModuleKindPlayback).
Example: jellyfin
Implement contracts.MediaLibrary + MediaTypeSchemaProvider. Discovered via FindByKind(ModuleKindMediaManager).
Example: media-manager-movies
Implement contracts.WorkflowEngine. Discovered via FindByKind(ModuleKindWorkflow).
Example: workflow-engine
Implement contracts.StorageProvider. Optionally implement Streamable, Seekable, Watchable, AtomicMovable, Hardlinkable — the orchestrator detects these at runtime.
Implement contracts.Scheduler. Discovered via FindByKind(ModuleKindScheduler).
Example: scheduler-cron
Register HTTP handlers via RouteRegistrar. Uses ModuleKindUI.
Example: admin-ui
Register API routes via RouteRegistrar. Uses ModuleKindAPI.
Example: api-rest
Implement contracts.RateLimiterProvider. Core discovers and wires into API middleware.
Example: ratelimit-tokenbucket
Implement contracts.CacheLayer. Storage orchestrator discovers automatically.
Example: cache-memory
Implement contracts.HealthMonitor. Core discovers and starts automatically.
Example: health-monitor
For modules without a specific kind interface, use ModuleKindProvider and advertise capabilities:
-
Supplementary Content:
SupplementaryContentProvider, capcontent.subtitle -
Notification:
NotificationProvider, capnotification.discord -
Import Lists:
ImportListProvider, capimportlist.trakt -
Media Info:
MediaInfoProvider, capmediainfo.ffprobe -
File Watcher:
FileWatcher, capfilewatcher.inotify -
Audit Logger:
AuditLogger, capaudit.log -
Worker Pool:
WorkerPool, capworker.distributed
// Broad: find all downloaders
entries := deps.Registry.FindByKind(contracts.ModuleKindDownloader)
// Fine-grained: find by capability
entries := deps.Registry.FindByCapability("playback.jellyfin")
// Direct: resolve by ID
entry, err := deps.Registry.Resolve("downloader-qbittorrent")
// Type-assert to the interface
for _, entry := range entries {
if dl, ok := entry.Module.(contracts.Downloader); ok {
dl.Add(ctx, task)
}
}deps.EventBus.Publish(ctx, contracts.Event{
Type: contracts.EventDownloadCompleted,
Source: "module:my-downloader",
Payload: payload,
})deps.EventBus.Subscribe(ctx, contracts.EventMediaRequested,
func(ctx context.Context, event contracts.Event) error {
var payload contracts.MediaRequestedPayload
json.Unmarshal(event.Payload, &payload)
return nil
}){
"name": "My Module",
"description": "What it does",
"version": "1.0.0",
"kind": "downloader",
"capabilities": ["downloader.torrent"],
"dependencies": [],
"homepage": "https://github.com/you/my-module"
}| Field | Required | Notes |
|---|---|---|
name |
Yes | Display name |
description |
Yes | Short description |
version |
Yes | Semantic version |
kind |
Yes | One of: auth, provider, downloader, indexer, media_manager, processor, playback, workflow, storage, ui, api, eventbus, scheduler |
capabilities |
Yes | Array of capability strings |
dependencies |
No | Module IDs this depends on |
homepage |
No | URL to repo |
- Push module to a public GitHub repo
- Add repo URL to a marketplace
catalog.json - For the official marketplace, submit a PR to
github.com/Muxcore-Media/marketplace-catalog
-
go.moddepends ongithub.com/Muxcore-Media/core -
contracts.Register()called ininit() - Implements
contracts.Module(Info, Init, Start, Stop, Health) - Implements the capability contract for your module kind
-
muxcore.jsonat repo root - Module kind matches the contract interface
- Builds with core:
go build -tags default ./cmd/muxcored