Plugin API reference
A plugin is a folder with manifest.json and a built ES module. The exact public TypeScript contract ships as types/plugin-api.d.ts in the readaware-plugins repository. This page explains how its pieces fit together.
Package shape
my-plugin/
manifest.json
main.js
src/main.ts # recommended and committed for review
assets/ # optional, explicitly listed for marketplace installsmain.js default-exports a lifecycle object. ReadAware runs it in a dedicated module Worker and hands activate an actor-scoped context.
export default {
activate(ctx) {
// Inspect and register. Side effects are blocked in this phase.
},
migrate(storageCtx, change) {
// Optional: transform plugin-private KV and documents.
},
deactivate() {
// Optional: release the plugin's own external resources.
},
};Manifest
{
"id": "theme-schedule",
"name": "Theme Schedule",
"version": "0.1.0",
"schemaVersion": 1,
"minAppVersion": "0.3.0",
"requires": {
"domains": { "settings": "^1.0.0" },
"contributions": {
"commands": "^1.0.0",
"settingsOptions": "^1.0.0"
},
"services": {
"storage": "^1.0.0",
"schedules": "^1.0.0",
"ui": "^1.0.0"
},
"schemas": { "settings": "^1.0.0" }
},
"settingsAccess": {
"discover": ["appearance.theme", "reading.theme"],
"write": ["appearance.theme", "reading.theme"]
},
"main": "main.js"
}| Field | Contract |
|---|---|
id | Lowercase letters, digits, and hyphens; maximum 64 characters. It is the permanent namespace and must equal the folder name. |
name, version | User-facing name and package version. |
schemaVersion | Required positive integer for plugin-private KV and document data. Independent of package version. |
requires | Required map of capability IDs to semver ranges, grouped by domains, contributions, services, and schemas. |
permissions | Optional semantic authority requested from the user. Unknown values fail validation. |
settingsAccess | Optional discover/read/write grants for exact setting paths or explicit section.* groups. |
minAppVersion | Optional app-version floor. Use it when the package depends on a newly shipped capability. |
settings | Optional host-rendered plugin setting fields. |
schedules | Optional recurring tasks, declared before their handlers are bound. |
themes, fonts | Optional declarative theme and font contributions; requires ui:themes. |
main | Entry module relative to the folder; defaults to main.js. |
Use the capability browser for the complete roster and permission vocabulary. A requirement is always a compatibility claim; it never grants authority.
Runtime context
| Namespace | Contains |
|---|---|
ctx.manifest | The validated manifest, read-only. |
ctx.appVersion, ctx.locale | Host version and current UI locale. |
ctx.lifecycle.phase | activating, migrating, or active. |
ctx.capabilities | Only the capability versions visible to this plugin actor. |
ctx.domains | Granted ReadAware-owned state and behavior. |
ctx.contributions | Registries into which the plugin may supply implementations. |
ctx.services | Bounded host operations and plugin-private infrastructure. |
Permission-gated namespaces are absent when not granted. Every Worker call is also authorized host-side; hiding a method is not the only check. Registrations return a disposable and are reclaimed in reverse order when activation fails or the plugin is disabled.
Domains
A Domain exposes queries, optional commands, and committed events.subscribe. Commands use the same event-sourced write path as ReadAware and are attributed to plugin:<id>. Write permission implies read.
| Domain | Queries and commands | Authority |
|---|---|---|
library | Books, metadata, source chapter text, TOC, collections; import, edit, star, remove, virtual books, and collection commands. | library:read / library:write |
reading | Per-book and aggregate reading stats; mark finished, open a book, and navigate to CFI or href. | reading:read / reading:write |
annotations | Filter highlights, notes, and passive ask traces; create, edit, recolor, and remove highlights or notes. | annotations:read / annotations:write |
conversations | Read book threads, list global threads, and read a thread. Writes stay with the chat runtime. | conversations:read |
settings | Discover permitted catalog entries, read resolved values, update supported targets, and subscribe to committed changes. | Exact settingsAccess grants |
There is no shelf or appearance domain. Library data and active reading behavior are separate. Appearance is a section inside Settings.
Settings access
discover, read, and write are independent. Grant exact paths whenever possible; use a section group such as appearance.* only when the feature genuinely needs the whole section. Updates go through the catalog's validation, target policy, persistence, and post-commit effects.
const entries = await ctx.domains.settings.queries.discover({
section: "appearance",
});
await ctx.domains.settings.commands.update([
{
path: "appearance.theme",
value: "dark",
target: { kind: "global" },
},
]);Contributions
| Registry | Plugin supplies | Permission |
|---|---|---|
selectionActions | Selection action and handler returning a toast or host-rendered view. | None |
headerActions | Reader or library action, placement metadata, and view callback. | None |
commands | Command metadata and handler. | None |
settingsOptions | Dynamic options for one declared plugin field. | None |
voiceProviders | Voice list and encoded-audio synthesis. | None |
contentProviders | Sections for a virtual book key. | None |
readerModes | Bounded reader segmentation mode; currently bundled-only. | reader:modes |
agentTools | Tool schema, human label, description, and executor. | agent:tools |
agentContextProviders | Bounded current-turn reference blocks. | agent:context |
agentRetrievalProviders | Search results from plugin-owned data. | agent:retrieval |
memoryCandidateProviders | Possible durable facts, preferences, insights, or summaries. | agent:memory |
themes, fonts | Manifest-declared semantic theme and font data. | ui:themes |
syncTransports | A sync-backend session: sealed event batches, sealed blobs, and meta objects on a remote of the plugin's choosing. | sync:transport |
Every contribution ID is namespaced by plugin, every registration is owned and inspectable, and stale disposables cannot remove a newer replacement. A new contribution kind still needs a deliberate host consumer; after that, any compatible plugin can register without being named by the app.
Agent extension boundaries
- Context providers run for one turn. The host adds provenance, caps size, and serializes output as untrusted reference data.
- Retrieval providers become namespaced tools with a host-owned
query/limitschema and clipped results. - Memory candidate providers propose bounded candidates after a turn; the host validates scope, deduplicates, and performs any durable write.
Plugins never receive the Memory port, cannot inject system rules, and cannot write long-term memory directly.
Sync transports
A syncTransports registration provides an alternative sync backend — a remote mailbox the app's sync engine pushes to and pulls from (the first-party WebDAV Sync plugin speaks WebDAV this way). The boundary is ciphertext: the engine seals every event and blob before the plugin sees it, so a transport carries opaque envelopes plus their routing fields (event id, HLC stamp) and never sees event types, book bytes, or keys. The contract is dumb storage — dense per-device event batches, blob objects in the engine's envelope formats, and create-only meta objects for first-writer-wins key material. Ordering, cursors, merge, the passphrase ritual, and scheduling stay host-side, and a transport connection is mutually exclusive with a ReadAware account. Classify failures by throwing errors that carry stable sync/* codes; anything uncoded is treated as transient and retried with backoff.
Host services
| Service | Contract | Permission |
|---|---|---|
storage | Namespaced KV, document collections, and external-change notifications. | None |
secrets | Namespaced encrypted credential slots. | None |
ui | Host toast and save/export flow. | None |
schedules | Bind a handler to a manifest-declared cadence. | None |
session | Subscribe to bounded reading-session facts. | None |
network | Host-mediated HTTP. | service:network |
llm | One-shot text or JSON-schema-constrained model calls using the user's configuration. | service:llm |
clipboard | Write text to the system clipboard. | service:clipboard |
Storage
Use KV for small settings and checkpoints. Use a named document collection for plugin-owned records with stable IDs and optional bookId/anchor provenance. Provenance is an index, not ownership; a document may survive deletion of the referenced book. Uninstall clears document collections but retains KV, secret slots, and committed schema metadata for reinstall and migration.
Schedules
The manifest declares { id, label, everyMinutes } and activation binds the handler through ctx.services.schedules.bind. The minimum cadence is 15 minutes. Runs happen at least at that cadence while the app is open, catch up after launch when overdue, and do not overlap. This is not a durable background job or an exact-time guarantee.
Declarative UI and settings
Plugins return versioned view data, not executable UI. The view grammar includes markdown, searchable lists, forms, detail layouts, dictionary results, and bounded block trees. Handlers may keep the surface, show a toast, open or replace a view, reset navigation, close the surface, or return field errors. The host owns loading and failure states for promises.
Manifest settings use host controls for text, textarea, number, time, select, choice, checkbox, toggle, and secret fields. Conditional fields use visibleWhen; dynamic selects use a registered settingsOptions provider. Secret fields write directly to encrypted secret slots and never enter the ordinary settings object or the agent-visible catalog.
Themes and fonts
Theme plugins declare semantic data in the manifest. An app theme overrides a fixed host token vocabulary; a reader theme supplies the required six-color page palette and optional typography defaults. The host validates values, generates CSS, loads approved local font files, and applies nothing until the user selects it.
Supplying choices needs ui:themes. Selecting one needs an exact Settings write grant such as appearance.theme or reading.theme. One does not imply the other.
Lifecycle phases
- Activating: queries and plugin-private reads are available; registrations are staged; side effects are blocked.
- Migrating: only plugin KV and document collections are available.
- Active: promoted handlers may use their granted domains, contributions, and services.
The host drains activation RPCs, health-checks the Worker, runs any data migration, then promotes the full staged set at one explicit point. Failed activation disposes staged work without replacing the current runtime.
Worker environment
There is no React, Jotai, DOM, WebView, Tauri, SQLite, filesystem, or process access. Ambient fetch, WebSocket, EventSource, XMLHttpRequest, BroadcastChannel, IndexedDB, and Cache Storage are disabled. Use the typed context for network, persistence, and every host interaction.
Compatibility and stability
Domains, contributions, services, and declarative schemas each carry an independent semantic version. Unknown IDs, invalid semver ranges, inaccessible required capabilities, and incompatible host versions prevent activation. Compatible additions bump the owning capability, not one global plugin API number.
The current ecosystem is first-party, so the present registry-backed contract is the baseline. Do not rely on earlier shelf, appearance, or pre-registry shapes.