---
title: "Reference plugins"
description: "Choose and configure Adonia's supported production integration packages."
image: "https://adonia.pages.dev/og.png"
version: "next"
---

> Documentation Index
> Fetch the complete documentation index at: https://adonia.pages.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Reference plugins

The reference plugins are ordinary publishable packages built only on the
documented manifest and registry surfaces. They are usable integrations and
examples, not hidden framework features.

## Activity log

[Activity log](/reference/plugins/activity-log) provides a panel- and
tenant-scope-aware, read-only resource over core's action-event table. It
ships exact resource/action/actor/target filters, semantic change display,
safe resource links, a retention helper, and the
`./commands/activity_prune` Ace command.

The package exports two manifest entry points:

- `activityLogManifest` carries protocol, compatibility, client-entry, and
  migration metadata for installers.
- `activityLogPlugin({ panel, authorize, tenantScope?, ... })` returns the
  configured manifest whose server hook adds the resource to that panel.

Authorization is deliberately required; the package does not invent a policy
or expose the log by default.

```ts
import { Panel } from '@adonia/core'
import { activityLogPlugin } from 'adonia-activity-log'
import User from '#models/user'

Panel.make('admin').plugins([
  activityLogPlugin({
panel: 'admin',
authorize: (ctx) => ctx.auth.user instanceof User && ctx.auth.user.role === 'admin',
  }),
])
```

## Import and export

[Import and export](/reference/plugins/import-export) provides explicit
CSV/XLSX mapping, bounded and queued imports, streaming/compressed exports,
owned history and progress, Drive/Lucid/file persistence adapters, failure
artifacts, retries, and export presets.

`importExportManifest` (also returned by the source-compatible
`importExportPlugin()` function) declares:

- guarded panel routes under `transfers/:resource/*`;
- reserved export and import action-job handlers;
- the `import-export-worker` doctor check;
- its persistence migration directory; and
- the `adonia-import-export/client` browser entry.

The routes resolve the host's authenticated controller from
`adonia.importExportController`. Worker handlers resolve the host's transfer
runtime from `adonia.importExportWorker`. Attaching the manifest therefore
replaces hand-written route tables and action-name switches, but the host
still binds those two application-specific services, starts a queue consumer,
chooses storage credentials, and enforces resource/actor/tenant ownership.
The low-level services, stores, endpoint contracts, actions, and
`routeImportExportWorker()` remain exported for advanced composition.

## Roles and permissions

[Roles](/reference/api/roles) provides tenant-aware Lucid role, permission,
and assignment storage plus a panel-scoped role-management resource and a
Bouncer-compatible `PermissionPolicy` base.

```sh
pnpm add adonia-roles @adonisjs/lucid
node ace configure adonia-roles
node ace migration:run
```

Attach `rolesPlugin()` to every panel that manages roles. Configuration names
the panel, declares the permission catalogue shown in the resource's
`checkbox-list` field, and requires a server-side `authorize` callback:

```js
import { rolesPlugin } from 'adonia-roles'

rolesPlugin({
  panel: 'admin',
  permissions: [
{ slug: 'posts.view', label: 'View posts' },
{ slug: 'posts.*', label: 'Manage posts' },
  ],
  authorize: (ctx) => ctx.auth.user?.isSuperAdmin === true,
})
```

Queries and assignments are scoped to the configured panel and active tenant.
Use `tenantScope: false` only for a genuinely non-tenant panel. Permission
slugs support exact `<resource>.<ability>`, `<resource>.*`, and `*` grants;
extend `PermissionPolicy` on host resources rather than bypassing core's
normal authorization pipeline. A bare role slug must be unique within its
tenant, so pass a persisted `Role` or numeric id when multiple panels reuse a
slug. `rolesManifest` supplies migrations and the `roles-tables` doctor check;
this package has no separate browser entry because its management UI is a
normal Adonia resource.

## Media library

[Media library](/reference/api/media-library) adds a tenant-aware
`MediaAsset` model, browsable resource, protected picker/upload routes, named
collection rules, optional Sharp variants, and `F.media()` for storing an
asset id on another resource.

```sh
pnpm add adonia-media-library @adonisjs/drive @adonisjs/lucid
node ace configure adonia-media-library
node ace migration:run
```

Configure an `@adonisjs/drive` disk, then attach a manifest with explicit
collections, storage, and authorization:

```js
import { defineMediaCollections, mediaLibraryPlugin } from 'adonia-media-library'

mediaLibraryPlugin({
  collections: defineMediaCollections([
{ name: 'images', acceptedMime: ['image/*'], maxSizeMb: 12 },
  ]),
  disk: 'media',
  visibility: 'private',
  authorize: (ctx) => ctx.auth.user !== null,
})
```

Register `mediaLibraryUiPlugin` from `adonia-media-library/client` in the
application `ComponentRegistry`; it supplies the lazy picker used by
`F.media('coverAssetId', { collection: 'images' })`. The picker browses,
searches, uploads, and selects only through panel-guarded routes. Uploads are
sniffed from stored bytes rather than trusting MIME headers, and private disk
keys stay out of descriptors. Install `sharp` only when collections declare
image variants: without it the original succeeds but conversion status is
`sharp-unavailable`. Treat the stored numeric asset id as a host-model foreign
key, not as a Drive object key.

## Two-factor authentication

[Two factor](/reference/api/two-factor) provides RFC 6238 TOTP credentials,
single-use recovery codes, panel middleware, protected setup/challenge
routes, and lazy setup and challenge pages.

```sh
pnpm add adonia-two-factor
node ace configure adonia-two-factor
node ace migration:run
```

The host must configure `APP_KEY`. Attach `twoFactorPlugin({ issuer, window,
recoveryCodeCount, stepUpSeconds })`, and place
`requireTwoFactor({ stepUpSeconds })` after the host authentication middleware.
The middleware permits users without a confirmed credential and redirects an
enabled user without a recent panel-bound proof to the challenge route.

Register `twoFactorUiPlugin` from `adonia-two-factor/client`, then let the
Inertia resolver ask the registry for `adonia-two-factor/setup` and
`adonia-two-factor/challenge` before resolving application pages. Setup shows
the base32 secret and `otpauth://` URI; both pages are lazy chunks. Request
bodies never choose the user or panel. Secrets are encrypted with `APP_KEY`,
plaintext recovery codes are returned only once, regeneration invalidates all
old recovery codes, and disabling 2FA requires the current password plus a
current TOTP or recovery code.

## Backups

[Backups](/reference/api/backups) supplies PostgreSQL, MySQL, and
file-backed SQLite backup/restore engines, private gzip artifacts, checksum
verification, retention, signed downloads, a read-only panel resource, and
Ace commands.

```sh
pnpm add adonia-backups @adonisjs/drive @adonisjs/lucid
node ace configure adonia-backups
node ace migration:run
```

Create a private Drive disk, `LucidBackupRecordRepository`, `BackupEngine`,
`DefaultBackupsRuntime`, and `DefaultBackupsController`. Bind the runtime at
`ADONIA_BACKUPS_RUNTIME_BINDING`, bind the controller at
`ADONIA_BACKUPS_CONTROLLER_BINDING`, and attach
`backupsPlugin({ panel, disk, diskName, authorize })`. The separately booted
queue worker needs the runtime binding too: the manifest's reserved
`adonia-backups.run` handler dispatches server-created jobs to
`DefaultBackupsRuntime.work`.

The resource exposes server-authorized signed downloads without emitting
opaque Drive keys. Register the exported backup/run and backup/prune commands
in `adonisrc.ts` for operator use. PostgreSQL images need `pg_dump` and `psql`;
MySQL images need `mysqldump` and `mysql`; in-memory SQLite is unsupported.
Restore is destructive, requires the exact record-specific
`restoreConfirmationToken(record)`, verifies size and SHA-256 before changing
the target, and has no implicit rollback.

## Notifications

[Notifications](/reference/api/notifications) provides viewer-scoped
database notifications, guarded JSON routes, queued-action completion alerts,
an optional mail channel, and a lazy bell in `topbar.right`.

```sh
pnpm add adonia-notifications
node ace configure adonia-notifications
node ace migration:run
```

Attach `notificationsPlugin()` to each panel. `notify(recipient, payload)`
requires the panel and normalized tenant, uses the database channel by
default, and accepts root-relative or HTTP(S) `href` values. For mail, inject
an `AdonisMailChannelAdapter` into `NotificationService` and request
`channels: ['database', 'mail']`; recipients without an email are skipped.

Add `adonia-notifications` to the Tailwind v4 sources and register
`notificationsUiPlugin` from `adonia-notifications/client`. The bell polls its
guarded panel endpoint every 30 seconds, pauses while the document is hidden,
and supports mark-one and mark-all-read; use
`createNotificationsUiPlugin({ pollIntervalMs: 0 })` to disable polling.
Routes scope every query by viewer, panel, and tenant, and deliberately omit
notification `data` from responses. Queued completion alerts are emitted only
for actions declared with `isQueued: true`, and failure copy never includes
the thrown exception.

## Browser registration and installation

Activity log, import/export, media library, two-factor, and notifications
publish named UI plugins from `./client`. A manifest's `client` string is
metadata rather than an automatic import: register the matching UI plugin in
the application's `ComponentRegistry` and keep its server/client package
versions equal. Roles and backups render through core resource surfaces and do
not require a separate browser plugin.

Each package's `node ace configure <package>` hook uses
`configureAdoniaApp(..., { plugins })` from `@adonia/devtools/configure` to
publish manifest migrations and collect doctor checks. Run migrations after
configuration. Reference-plugin routes are never public escape hatches:
manifest routes inherit the panel's access and tenant middleware, and package
handlers add their documented actor/tenant/authorization scopes.

Complete exported surfaces for all reference packages are inventoried in the
[generated API reference](/reference/api).

Source: https://adonia.pages.dev/guide/reference-plugins/index.mdx
