Skip to content

Extension points

Cove extensions start with IExtension and opt into one or more capability interfaces. An extension can combine capabilities—for example, a scraper can also expose settings, an API, and a background worker.

This page catalogs the public extension contracts in Cove.Plugins. Use it to choose the smallest hooks that fit your feature.

Every compiled extension implements IExtension.

ContractUse it to
IExtension.ConfigureServicesRegister extension-owned services in the extension’s dependency-injection container.
IExtension.InitializeAsyncStart work that requires the built service provider.
IExtension.ShutdownAsyncStop cleanly when the extension is disabled, uninstalled, or Cove exits.
IExtension.OnInstallAsyncSeed data or create non-database resources during installation. Keep this hook idempotent because reinstall and hot-replacement paths may invoke it again.
IExtension.OnUninstallAsyncRemove non-database resources before uninstall completes.
IManifestAwareSource identity, compatibility, categories, and dependency metadata from extension.json. Extension base classes normally handle this for you.

ExtensionContext supplies Cove configuration, the extension data directory, the running Cove version, and the UI registry. Dependencies declared by extension id and semantic-version range control initialization order and enable/disable behavior.

Extensions may register their own services. They should treat host services as an explicit compatibility boundary, not assume every internal Cove service is stable. IExtensionServiceExchange is the supported opt-in exchange for publishing and resolving services across extension containers.

Cove.Sdk provides manifest-aware base classes and a UI builder. They reduce lifecycle and dispatch boilerplate; they do not add capabilities that their implemented interfaces do not list.

BaseImplementsChoose it when
CoveExtensionBaseIUIExtension, IManifestAwareThe extension needs normal lifecycle and service registration, an optional UI manifest, or a base for one manually implemented capability.
DataExtensionBaseCoveExtensionBase, IDataExtensionThe extension owns relational tables or forward-only SQL migrations and does not need the full combined base.
EventExtensionBaseCoveExtensionBase, IEventExtensionThe extension mainly routes entity lifecycle events to handlers.
JobExtensionBaseCoveExtensionBase, IJobExtensionThe extension mainly declares named, host-run jobs.
FullExtensionBaseData, API, UI, events, jobs, state, and scan participationOne extension genuinely needs several of those capabilities. All listed capabilities have safe no-op defaults, but the extension still advertises every listed interface to the host.

There are no capability-specific SDK bases for API endpoints, background workers, middleware, actions, scrapers, downloaders, or permission contributions. Derive from CoveExtensionBase and implement only the needed interface for those cases.

The host applies extension.json immediately after constructing a manifest-aware extension. Id, Name, Version, description, author, URL, categories, MinCoveVersion, and dependencies then come from that manifest. Manifest is available from ConfigureServices onward.

The main override and helper signatures are:

public virtual void ConfigureServices(
IServiceCollection services,
ExtensionContext context);
public virtual UIManifest GetUIManifest();
public virtual Task InitializeAsync(
IServiceProvider services,
CancellationToken ct = default);
public virtual Task ShutdownAsync(CancellationToken ct = default);
public virtual Task OnInstallAsync(
IServiceProvider services,
CancellationToken ct = default);
public virtual Task OnUninstallAsync(
IServiceProvider services,
CancellationToken ct = default);
protected UIManifestBuilder ManifestBuilder();
protected void PublishContributions<T>(IServiceProvider services)
where T : class;

Lifecycle methods and GetUIManifest() default to no work. Override ConfigureServices for extension-owned DI registrations. Call PublishContributions<T> during InitializeAsync only for a shared contract that sibling extensions should resolve through IExtensionServiceExchange; the host withdraws published instances when the extension stops.

DataExtensionBase requires ConfigureModel(ModelBuilder) and provides DefineMigrations() plus Migration(name, sql). Migrations are forward-only, ordered as added, and tracked by name.

EventExtensionBase calls RegisterHandlers() from its constructor. Register exact event strings with On, or use OnCreated, OnUpdated, and OnDeleted. Handlers receive the event and cancellation token.

JobExtensionBase calls DefineJobs() from its constructor. Job(...) binds an id and label to a handler and can declare parameters, description, and showInTaskList. Do not rely on derived-class fields that have not initialized yet from either constructor-time registration method.

FullExtensionBase combines the data helpers with MapEndpoints, DefineEventHandlers, DefineJobs, Store, and ScanAsync. It does not implement IBackgroundExtension, IMiddlewareExtension, IActionExtension, provider interfaces, or permission contributors. Add those interfaces explicitly when needed, or use a narrow base if most combined capabilities would remain empty.

UIManifestBuilder creates a UIManifest and automatically stamps supported contributions with the extension id. A manifest-backed extension normally starts with the protected ManifestBuilder() helper:

public sealed class ReportsExtension : CoveExtensionBase
{
public override UIManifest GetUIManifest() =>
ManifestBuilder()
.AddPage(
route: "example-reports",
label: "Reports",
componentName: "ReportsPage",
icon: "puzzle")
.AddSettingsTab(
key: "example-reports-settings",
label: "Example reports")
.Build();
}

Representative signatures are:

public UIManifestBuilder AddPage(
string route,
string label,
string componentName,
string? icon = null,
string? detailRoute = null,
bool showInNav = true,
int navOrder = 100);
public UIManifestBuilder AddSlot(
string slot,
string componentName,
string? id = null,
int order = 100);
public UIManifestBuilder AddTab(
string pageType,
string key,
string label,
string componentName,
int order = 100,
string? countEndpoint = null,
string? icon = null,
string[]? manualContexts = null);
public UIManifest Build();

The builder also has methods for actions, settings tabs and panels, tutorial topics, features, and list filters and sorts. AddPane and OverrideComponent build declarations whose frontend consumers are currently reserved; see UI extension points before choosing a method.

UIManifestBuilder does not replace extension.json. In particular, package frontend files with manifest jsBundle and cssBundle. Although the builder exposes WithJsBundle, WithCssBundle, and WithRuntimeVersion, the aggregated host manifest supplies asset URLs from the package manifest and supplies its own frontend runtime version. Do not use those methods as package configuration.

Implement only the interfaces your extension needs.

ContractExtension point
IApiExtensionMap minimal-API endpoints with MapEndpoints. Cove adds and removes the routes at runtime and enforces any attached Cove authorization conventions before endpoint execution.
IMiddlewareExtensionInspect, transform, or short-circuit every HTTP request and response. Call next to continue the pipeline.
IJobExtensionDeclare named jobs and run them with parameters, cancellation, and progress reporting. ShowInTaskList can expose a job in Cove’s task UI.
IBackgroundExtensionRun a host-managed long-lived worker until its cancellation token is signaled.
IEventExtensionReceive entity lifecycle events such as video.created, performer.updated, or tag.deleted.
IScanParticipantJoin a core library scan, receive resolved scan paths and exclusions, and report progress within the scan job.
IStatefulExtensionReceive an extension-scoped IExtensionStore for persistent string key/value data.
IDataExtensionAdd extension-owned EF Core model configuration and ordered, forward-only SQL migrations. Do not drop core tables or columns.
IActionExtensionContribute toolbar and bulk actions backed by an API endpoint or frontend handler. The contract declares context-menu actions, but the frontend does not currently render them.

Choose storage by shape:

  • use IExtensionStore for small durable key/value state
  • use IDataExtension for structured, queryable extension-owned tables
  • use manifest settings for administrator-editable configuration

Background workers should create a dependency-injection scope per unit of work and persist checkpoints rather than relying on in-memory state. Event handlers, jobs, middleware, and endpoints should honor cancellation and keep failure messages local to the operation.

These provider interfaces integrate with Cove’s built-in review, provenance, and intake workflows.

ContractExtension point
IScraperProviderDeclare scrapers and scrape or search videos, performers, galleries, images, groups, audio, and text.
IDownloaderProviderMatch source URLs, offer quality choices, and download videos, images, galleries, audio, or text into Cove’s intake flow.

Scrapers declare supported entities, URL patterns, invocation capabilities, preference sites, and a risk level. Cove passes a ScraperPermissions value with each request. The current host derives the allowed network host from a URL input and leaves JavaScript and browser debugging disabled; name-only searches receive no allowed host. Providers must honor that request value.

Downloaders declare supported entities, URL patterns, and capabilities such as resume, range requests, multiple qualities, or inline metadata. Cove supplies a temporary directory, HTTP clients, logging, and progress reporting through IDownloaderHost.

For implementation guidance, see Create a scraper and Create a downloader.

Some host-consumed contracts live in Cove.Core.Interfaces instead of deriving from IExtension. Publish their implementations through IExtensionServiceExchange when building these specialized integrations.

ContractExtension point
ITextEncoderEncode text into vectors for a named embedding family.
IFaceSuggesterSupply single-face and batch performer suggestions.
IFaceSuggestionDecisionHandlerHandle accept, reject, or merge decisions for provider-owned face suggestions.
IFaceLifecycleParticipantClean up provider state when faces are deleted or AI face data is purged.

These are shared service contracts rather than standalone extension lifecycles; the providing package still implements IExtension and publishes the service instance during initialization.

Implement IUIExtension.GetUIManifest to declare frontend contributions. Ship an ESM bundle when a contribution references a React component or frontend handler, and a CSS asset when the extension needs custom styles. A single UIManifest can contain any combination of the following contributions.

See UI extension points for the fields, supported targets, runtime behavior, and limitations of each contribution. Use the frontend runtime API for supported module imports, authenticated requests, and shared UI primitives.

ContributionWhere it appears
UIPageDefinitionA new route with an optional navigation item.
UISlotContributionReact component or HTML inserted into a host-defined named slot.
UITabContributionA tab on a supported entity detail page, optionally with a count badge.
UISettingsTabA dedicated tab in the Extensions settings group, with either panel or full-page layout.
UISettingsPanelA settings component in an extension tab or a targeted built-in tab and section.
ExtensionActionA toolbar or bulk action. Actions may also come from IActionExtension; the declared context-menu type is currently reserved.
UITutorialTopicAn in-app Cove Manual topic made from slides, Markdown, images, and links.
UIListFilterContributionA first-class filter criterion on a built-in entity list, including custom-field-backed filters.
UIListSortContributionA first-class sort option on a built-in entity list, including custom-field-backed sorts.
UIFeatureDefinitionNamed feature metadata and string options that the host UI can inspect.

Slots, tabs, and page overrides only render where Cove publishes the corresponding stable slot, page type, or page key. Use the host-defined identifiers rather than inventing a target name.

Pages and tabs can declare one permission or an All/Any permission set. Use the record-accepting UIManifestBuilder overloads for those richer immutable definitions; the builder stamps the real extension owner. These frontend checks do not replace endpoint authorization.

ContributionUse it to
UIThemeDefinitionAdd a theme with CSS variables, an optional stylesheet, component and layout layers, background animation, and color scheme.
UIComponentStyleDefAdd a named component-style preset.
UILayoutStyleDefAdd a named layout preset.

Page overrides are intentionally broader than additive contributions. Prefer a page, slot, or tab when that meets the need.

ContributionReplaces
UIPageOverrideA built-in page identified by its exact page key.

When multiple extensions target the same page, priority determines the winner. Page overrides carry greater compatibility risk because they replace host-owned UI rather than add to it.

UIManifest also defines panes, component overrides, selector overrides, dialog overrides, and a DetailRoute value on page definitions. Cove currently transports these declarations but does not consume them in the frontend, so they are not usable extension points yet. The * target documented on UIPageOverride is likewise not a working application-shell override; use an exact built-in page key.

Not every package needs a DLL. extension.json can describe a manifest-only bundle or scraper-pack, and can contribute:

  • generic settings surfaced in the Extensions settings UI
  • in-app Cove Manual topics
  • external tool, runtime, service, environment-variable, and configuration requirements
  • extension dependencies and Cove version compatibility
  • declarative network, scraper-runtime, and downloader-runtime requirements

A YAML scraper pack contributes scraper definitions without compiling a .NET provider. A bundle can group contributions and dependencies under one installable identity.

Frontend JavaScript and CSS require a loaded IUIExtension; declaring bundle paths on a manifest-only package does not load those assets.

The manifest permission lists are not a runtime authorization boundary. See Extension permissions for their exact fields and current behavior.

Start from the user-visible behavior:

GoalStart with
Add a screen or panelIUIExtension with a page, tab, slot, or settings contribution
Add an HTTP operationIApiExtension
Run work on demandIJobExtension
Run continuouslyIBackgroundExtension
React to library changesIEventExtension
Participate in scansIScanParticipant
Enrich existing itemsIScraperProvider
Bring media into CoveIDownloaderProvider
Store small durable stateIStatefulExtension
Own relational dataIDataExtension
Customize existing UIAn additive UI contribution first; an override only when replacement is required

Most full-featured extensions pair one workflow capability with IUIExtension, IApiExtension, and one storage option. Keep permissions narrow, preserve metadata provenance, and package pre-built backend and frontend artifacts as described in Packaging.