Adapters and Plugins
The adapters that build, sign, store, and hold updates, the plugins that extend the server and the app, and how to write your own.
Adapters and plugins
Hot Updater talks to bundlers, cloud services, and databases through two kinds of objects:
- An adapter fills one slot of a configuration: the
build,storage,database, orsigningofhot-updater.config.ts, and thedatabaseandstorageof the server'screateHotUpdater. A configuration has one adapter per slot; the server'sstoragetakes a list so it can read Bundles that different adapters stored. - A plugin is one entry of a list: the
pluginsofcreateHotUpdater, ofhot-updater.config.ts, or of the app'sHotUpdater.init, or an integration that wraps a build adapter. A configuration can have any number.
Both are plain objects that a factory function returns, such as
s3Storage(options) or insights(). Official adapters and plugins use the
same contracts as the ones you write, and there is no registry: install the
package and pass the object in.
Plugins are configured in three places:
- The server's
createHotUpdater({ plugins }), for server plugins, beside the server's database and storage adapters. pluginsinhot-updater.config.ts, for the same server plugins. The config mirrors the server: itsstorageanddatabasereach the server's bucket and database, or the server's admin API throughstandaloneRepository, and itspluginstell the CLI and the Console which plugins the server runs.- The app's
HotUpdater.init({ plugins }), for client plugins.
A managed project lists plugins in two places: hot-updater.config.ts, where
init imports apiKeys, insights, and remoteConfig from
hot-updater/plugins and lists [apiKeys(), insights(), remoteConfig()], and
the app. Its prebuilt server runs the same server plugins.
Keep the config's list the same as the server's, and a deployed Console's
console.config.ts, which mirrors the server too. When the server adds or
drops a plugin, change each list with it, or import one list from a module
every project can reach.
| Kind | What it does | Runs in | Official | Write one |
|---|---|---|---|---|
| Build adapter | Bundles the app's JavaScript for hot-updater deploy | CLI | Bare, Expo, Rock | BuildAdapter from @hot-updater/plugin-core |
| Storage adapter | Stores bundle artifacts and resolves their download URLs | CLI, Console, server | Supabase, Cloudflare R2, AWS S3, Firebase, Standalone | Create a storage adapter |
| Database adapter | Stores Bundles, Releases, Release Catalogs, Channels, and server plugins' tables | CLI, Console, server | Supabase, Cloudflare D1, DynamoDB, Firestore, Kysely, Drizzle, Prisma, MongoDB | Create a database adapter |
| Signing adapter | Signs artifact hashes during deploy | CLI | Local PEM, AWS KMS, Google Cloud KMS, remote | BundleSigningAdapter from @hot-updater/plugin-core |
| Server plugin | Adds tables, APIs, routes, and client authentication to the update server | Server, and the CLI and Console over a direct database; its tables come from managed init or hot-updater db in the server project | insights(), apiKeys(), remoteConfig() | Create a server plugin |
| Client plugin | Observes launches, update checks, downloads, and failures in the app, and calls the server | React Native app | Insights, Remote Config | Create a client plugin |
| Integration plugin | Uploads source maps for the bundles a build adapter makes | CLI | Sentry, Datadog, BugSnag | A function that wraps a build adapter |
The managed Cloudflare, Supabase, AWS, and Firebase servers are prebuilt with their provider's storage and database adapters and the official server plugins, Insights, API keys, and Remote Config. Other adapters and server plugins run on a custom server. Client plugins run in any app.
A feature can ship both halves, as Insights does: a server plugin that stores
data and serves routes, and a client plugin that calls those routes with the
app's base URL and headers. The server plugin names its client plugin in
cli.clientPlugin,
which managed init and the agent scaffold use to add it to the app's
HotUpdater.init, and which hot-updater doctor checks the app for.
Write your own
Pick the contract for the slot or the list, and write a factory that returns it. Each guide builds one and tests it:
| Kind | Contract | Guide |
|---|---|---|
| Storage adapter | createStorageAdapter from @hot-updater/plugin-core | Create a storage adapter |
| Database adapter | createSqlAdapter, createKvAdapter, or a DatabaseAdapter, wrapped by createEngineDatabase from @hot-updater/plugin-core | Create a database adapter |
| Server plugin | definePlugin from @hot-updater/plugin-core | Create a server plugin |
| Client plugin | defineClientPlugin from @hot-updater/protocol | Create a client plugin |
| Signing adapter | BundleSigningAdapter from @hot-updater/plugin-core: getPublicKey and sign | Remote Signing describes the service protocol |
| Build adapter | A function from { cwd } to a BuildAdapter from @hot-updater/plugin-core | The official build adapters are the reference |
Hot Updater's packages divide by where their code runs:
| Package | What it holds | Imported by |
|---|---|---|
@hot-updater/protocol | The device-safe, zero-dependency package for what crosses a boundary: the data formats and pure computations the app and the server share, and the contracts between a host and its extensions on the device, such as the client plugin contract. | The app, the server, the CLI, and client plugins |
@hot-updater/plugin-core | The kit that extends the server: the adapter and server plugin contracts and their helpers, and the storage engine they run on. It runs wherever the server runs, and depends on @hot-updater/protocol. | Adapters and server plugins, including Hot Updater's own |
@hot-updater/server | The runtime host: createHotUpdater and its handlers, with the built-in adapters and plugins it re-exports. | The server and deployed Console |
hot-updater/plugins | The official server plugin factories for CLI configuration: apiKeys, insights, and remoteConfig. | hot-updater.config.ts |
@hot-updater/react-native | The app's SDK. It imports @hot-updater/protocol and the built-in Insights and Remote Config clients, never @hot-updater/plugin-core or the server. | The app |
An adapter or a server plugin you write imports @hot-updater/plugin-core, and
a client plugin imports @hot-updater/protocol, as Hot Updater's own plugin
packages do. A database adapter builds on the engine kit there too.
@hot-updater/react-native re-exports the client plugin names for app code.
Each package's API is its root. A subpath only sets apart a part some projects
leave out: @hot-updater/server/adapters/* holds the Kysely, Drizzle, Prisma
and MongoDB adapters, @hot-updater/server/plugins the built-in plugins, and
an official plugin package has one entry per side. No package has an internal
entry, and a package uses another only through that package's API. Test
helpers live in @hot-updater/test-utils alone.
Then package it for npm with Hot Updater as a peer dependency; see Test and publish.
Community adapters and plugins
Community adapters and plugins are npm packages that use the contracts above.
Search npm for the hot-updater-adapter and hot-updater-plugin keywords to
find them. Hot Updater does not review or maintain them: read a package's
source and its tested Hot Updater versions before you install it. To list
yours here, open a pull request in the
Hot Updater repository.
Build adapter
Build adapters generate JavaScript bundles when you run hot-updater deploy.
@hot-updater/bare: Runs the project's configured bundler through the React Native CLIbundlecommand.@hot-updater/rock: Runs the project's configured bundler through the Rock CLIbundlecommand.@hot-updater/expo: Executes the bundle command through Expo.
Storage adapter
Storage adapters upload generated bundles to your chosen provider during
hot-updater deploy, and the server resolves the URLs devices download them
from.
Storage adapters expose one flat, runtime-independent byte contract. CLI and
server consumers validate only the operations they need. Provider packages can
export the same adapter name from runtime-specific subpaths; for example,
@hot-updater/cloudflare uses S3-compatible credentials while
@hot-updater/cloudflare/worker captures an R2 binding.
@hot-updater/supabase: Stores bundles in Supabase withsupabaseStorage.@hot-updater/cloudflare: Provides deploy-timer2Storageand Worker runtime storage for Cloudflare R2.@hot-updater/aws: Stores bundles in AWS S3 withs3Storage.@hot-updater/firebase: Stores bundles in Firebase Storage withfirebaseStorage.@hot-updater/standalone: Sends storage operations to your own HTTP service withstandaloneStorage.
To write one, see Create a storage adapter.
Database adapter
Database adapters persist immutable artifact metadata, Release policy, compiled Catalogs and Channels, and the tables of the server plugins a server runs, such as Insights, API keys, and Remote Config. Deploy and administrative operations commit changes atomically. The server uses the same database for Catalog reads, artifact resolution and its plugins; the device selects an update from the returned Catalog.
Core and server plugins declare their tables and run their reads and transactions on one storage engine, so a database adapter only stores rows and reads index ranges on its backend.
@hot-updater/supabase: Stores metadata in Supabase withsupabaseDatabase.@hot-updater/aws: ProvidesdynamoDBfor metadata.@hot-updater/cloudflare: Stores metadata in Cloudflare D1 withd1Database.@hot-updater/firebase: Stores update metadata in Firebase Firestore withfirebaseDatabase.@hot-updater/standalone:standaloneRepositoryreaches a self-hosted server's database through its admin API, as thedatabaseofhot-updater.config.tsor of the Console's configuration.- Kysely, Drizzle, Prisma, and MongoDB connect an existing database to a custom server.
To run on another backend, see Create a database adapter.
Signing adapter
Signing adapters authenticate immutable OTA artifact content during
hot-updater deploy. They resolve the provider public key and sign the 32 raw
bytes decoded from each SHA-256 artifact hash. The deploy command verifies
every returned RSA-SHA256 signature before upload and checks that the signer
identity matches the native app trust anchor.
Signing is independent of the storage and database adapters. Omit signing or
set local enabled: false for unsigned deployments; Hot Updater never falls
back from an enabled signer to an unsigned deployment.
- Built-in local signing: Use
enabled: trueandprivateKeyPath. Signing is never enabled automatically. hot-updater/signing: Use a compatible signing endpoint withremoteSigning, or hardened managed-key custody withawsKmsSigningandgoogleCloudKmsSigning.
Expo projects configure the native trust anchor independently through the
@hot-updater/expo app plugin's publicKeyPath. This keeps EAS builds outside
the private-key and KMS credential boundary.
The cloud KMS SDKs are optional peer dependencies and load only when their signer is used for deployment.
See Bundle Signing for custody differences, provider setup, native trust-anchor rollout, and rotation guidance.
Integration plugins
Integration plugins connect bundle builds to error-monitoring services. They wrap your selected build adapter to upload source maps and associate them with the deployed update. The app's monitoring SDK must use matching identifiers for readable stack traces.
Each guide covers service credentials, build configuration and symbolication verification. These integrations can be used with any hosting, storage and database choice supported by the selected build workflow.
Server plugins
Server plugins extend the update server rather than the CLI. They run inside
createHotUpdater, own tables and aggregates on the server's database, and add
APIs, routes and client authentication. Core has none of their tables or routes:
a server has Insights only when it runs the insights() plugin, which records
Insights reports, apiKeys() protects client routes, and remoteConfig()
serves Remote Config. Managed providers run all
three. To write one for a custom server, see
Create a server plugin.
Hot Updater's own plugins are packages built the same way:
@hot-updater/plugin-insights, @hot-updater/plugin-api-keys, and
@hot-updater/plugin-remote-config. @hot-updater/server/plugins re-exports them for servers and
deployed Consoles. CLI configurations import these factories from
hot-updater/plugins, which ships with the CLI. Each package has an entry
per side, the layout a plugin you publish follows; see
Ship both sides from one package.
The Console has built-in screens for these official features, and shows each one only when its configuration lists the plugin, as the server runs it. It has no UI extension API: a plugin extends the server and the app, and its data reaches people through its API and its routes.
Client plugins
Client plugins run in the React Native app. They observe launches, update
checks, downloads, and update failures, and can send requests to the update
server with the app's base URL and headers. Pass them to plugins in
HotUpdater.init, which returns the app's HotUpdater instance with each
plugin's API under the plugin's id, as the server's hotUpdater.api holds
server plugins' APIs. The
Insights plugin reports to the server's
insights() plugin; it is @hot-updater/plugin-insights/client, which
@hot-updater/react-native exports from its root. The
Remote Config plugin, from
@hot-updater/plugin-remote-config/client, fetches the values the server's
remoteConfig() plugin picks and reads them synchronously. To write one, see
Create a client plugin.