# @bsb/base

> Better Service Base (BSB) is an event-driven microservices framework for Node.js and TypeScript with AnyVali-based config validation, BSB type-safe event schemas, and a pluggable architecture for config, observability (logging, metrics, tracing), and even

Latest version **9.7.3** (published 2026-09-12) · (AGPL-3.0-only OR Commercial) license · 7.2K weekly downloads

## Install

```sh
npm install @bsb/base
pnpm add @bsb/base
yarn add @bsb/base
bun add @bsb/base
```

Provides the commands `bsb`, `bsb-client-cli`, `bsb-plugin-cli`.

## Health

**Score 90/100 (A)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score; growing popularity.

## Facts

| | |
|---|---|
| Version | 9.7.3 |
| Published | 2026-09-12 |
| First published | 2026-02-20 |
| Weekly downloads | 7.2K |
| License | (AGPL-3.0-only OR Commercial) |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=24.0.0 |
| Dependencies | 5 |
| Unpacked size | 1.3 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 3 |
| Author | BetterCorp |
| Maintainers | mrinc, betterninja |
| Keywords | bsb, base, service, event-driven, microservice, framework |

## Links

- npm: https://www.npmjs.com/package/@bsb/base
- Repository: https://github.com/BetterCorp/better-service-base
- Homepage: https://bsbcode.dev/
- Issues: https://github.com/BetterCorp/better-service-base/issues
- npm.io page: https://npm.io/package/@bsb/base

## Dependencies (5)

- [uuid](https://npm.io/package/uuid.md) ^14.0.1
- [yaml](https://npm.io/package/yaml.md) ^2.8.2
- [anyvali](https://npm.io/package/anyvali.md) ^1.1.5
- [chokidar](https://npm.io/package/chokidar.md) ^5.0.0
- [safe-regex2](https://npm.io/package/safe-regex2.md) ^5.1.0

## Alternatives

- [@sveltejs/kit](https://npm.io/package/@sveltejs/kit.md) — 2.2M weekly downloads
- [@atlaskit/theme](https://npm.io/package/@atlaskit/theme.md) — 402.0K weekly downloads
- [@tangle-network/brand](https://npm.io/package/@tangle-network/brand.md) — 10.0K weekly downloads
- [seneca](https://npm.io/package/seneca.md) — 7.4K weekly downloads
- [@modern-js/plugin-swc](https://npm.io/package/@modern-js/plugin-swc.md) — 3.4K weekly downloads

## Recent versions

- 9.7.3 (latest) — 2026-09-12
- 9.7.2 — 2026-09-10
- 9.7.1 — 2026-09-10
- 9.7.0 — 2026-09-09
- 9.6.99 — 2026-09-05
- 9.6.98 — 2026-09-05
- 9.6.97 — 2026-09-03
- 9.6.96 — 2026-09-03
- 9.6.95 — 2026-09-02
- 9.6.94 — 2026-09-02
- 9.6.93 — 2026-09-02
- 9.6.92 — 2026-09-02
- 9.6.91 — 2026-09-02
- 9.6.90 — 2026-09-02
- 9.6.89 — 2026-09-02
- … 124 more at https://npm.io/package/@bsb/base/versions

## README

# @bsb/base (Node.js Service Base)

Better Service Base (BSB) is an event-driven microservices framework for Node.js and TypeScript with AnyVali-based config validation, BSB type-safe event schemas, and a pluggable architecture for config, observability (logging, metrics, tracing), and events. It is designed for production-ready, secure-by-default backends with validated APIs and a type-safe event bus.

**Version 9.0** introduces breaking changes with improved type safety, cross-language support, and automated code generation. See the Plugin Development Guide for v9 patterns:
`https://github.com/BetterCorp/better-service-base/blob/master/nodejs/PLUGIN_DEVELOPMENT.md`

## Links

- GitHub: `https://github.com/BetterCorp/better-service-base/tree/master/nodejs`
- BSB Registry (package): `https://io.bsbcode.dev/packages/nodejs/@bsb/base`

### Intended Usage (Container-first)
- This project is designed to run standalone inside a Docker container and execute plugins authored and published separately.
- It is not intended to be embedded or imported as a library into another application package.
- Deploy the container and supply plugins via `BSB_PLUGIN_DIRS` (recommended, comma-separated) or `BSB_PLUGINS` installation at container startup.

#### Requirements
- Node.js >= 24.0.0, npm >= 11.0.0
- TypeScript 5.x for development

### Project Structure
- `src/`
  - `index.ts`: Public exports for the package (base classes, interfaces, controllers).
  - `cli.ts`: Production CLI entry (also exposed as `bin` -> `bsb`).
  - `dev.ts`: Development runner with hot-reload and restart controls.
  - `client.ts`: Legacy helper for embedding a client; avoid in new code.
  - `base/`: Core building blocks used by plugins and services
    - `BSBService`, `BSBServiceClient`: Base classes for service plugins and their clients
    - `PluginObservable`, `PluginEvents`: Per-plugin facades into observability and events
    - `BSBConfig`, `BSBObservable`, `BSBEvents`: Base plugin contracts
    - `factory.ts`: Option resolution and presets for `ServiceBase`
  - `interfaces/`: Strong TypeScript contracts for options, observability, events, results, tools
  - `serviceBase/`: Runtime controllers that orchestrate the system
    - `serviceBase.ts`: Main runtime (`ServiceBase`) - boot/init/run/dispose pipeline
    - `config.ts`: Loads and initializes the configuration plugin
    - `observable.ts`: Unified logging, metrics, and tracing via observable plugins
    - `events.ts`: Manages event plugins and exposes event APIs (broadcast, emit, return, streams)
    - `plugins.ts`: Resolves and loads plugins from local build or external locations
    - `services.ts`: Loads, orders, and runs service plugins + their clients
  - `plugins/`: Built-in plugins
    - `config-default/`: Default configuration plugin
    - `events-default/`: Default event bus
    - `observable-default/`: Console-based logging, metrics, and tracing
    - `service-default{0..4}/`, `service-benchmarkify/`: Example/demo service plugins
  - `tests/`: Mocha + ts-node test suite
- `lib/`: Compiled JavaScript output (generated by `tsc`)
- `templates/`: Scaffolding for new plugins (`plugin.ts`, `pluginClient.ts`, `events.ts`, `logger.ts`)
- `Dockerfile`, `entrypoint.sh`, `entrypoint.js`: Container build/runtime assets
- `typedoc.json`, `docs.json`, `typedoc-theme/`: API docs generation configuration/theme

### What's New in v9

v9 introduces breaking changes focused on type safety, developer experience, and cross-language support:

**Type Safety Improvements:**
- `createEventSchemas()` - No more `as const` required, automatic type inference
- Type branding - Compile-time validation that event types match categories
- Duplicate name validation - Builds fail when an EventSchemas key is reused across categories

**Simplified Configuration:**
- `createConfigSchema()` - Single function replaces class pattern
- Plugin metadata - Define once, auto-generates PLUGIN_CLIENT and bsb-plugin.json
- Centralized schemas - All generated JSON in lib/schemas/ with JSON $ref references

**Cross-Language Support:**
- Type helpers - int32, int64, uuid, datetime for precise type mapping
- Schema export - Auto-generates JSON schemas for client code generation
- Multi-language clients - Generate type-safe clients in TypeScript, C#, Go, Java
- Cross-plugin events - Type-safe communication between plugins (no `any` types)

See [Plugin Development Guide](https://github.com/BetterCorp/better-service-base/blob/master/nodejs/PLUGIN_DEVELOPMENT.md) for migration details and examples.

### Runtime Architecture
The `ServiceBase` class is the primary entry point. It coordinates the framework subsystems and plugin lifecycle.

Boot flow (high level):
1) Construct `ServiceBase` (select mode, cwd, and controller implementations)
2) `init()` sequence
   - `SBConfig.init()` -> choose and init configuration plugin
   - `SBObservable.init()` -> load observable plugins (logging, metrics, tracing)
   - `SBEvents.init()` -> load events plugins (+ always adds `events-default` first)
   - `SBServices.setup()` -> discover service plugins from config, create instances, and map dependencies; then `SBServices.init()` respecting declared ordering
3) `run()` sequence
   - Start observable, events, then `SBServices.run()` (ordered)
   - Dispose config for safety, start heartbeat metric
4) `dispose()`
   - Disposes services, events, observable, and config; exits the process

Timekeeping metrics are recorded for each step and logged as timers. A heartbeat counter runs hourly.

### Subsystems
- `SBConfig` (configuration)
  - Defaults to `config-default` plugin; can be replaced via environment variables
  - Provides resolved plugin lists: services, events, observable
  - Exposes `getPluginConfig()` for per-plugin configuration
- `SBObservable` (observability)
  - Manages observable plugins for logging, metrics, and tracing
  - Routes log, metric, and trace operations via an internal bus with filtering
- `SBEvents` (events)
  - Loads events plugins and always includes `events-default` as a fallback
  - Offers APIs for broadcast, fire-and-forget, request/response, and streaming
- `SBServices` (services)
  - Loads service plugins from config, re-maps declared `init/run` before/after dependencies, and initializes/runs them in order

### Plugin Resolution & Layout
`SBPlugins` looks for plugins in the following order (container usage prefers the first external option):
- Local project (dev): `src/plugins/<type>-<name>/index.ts`
- Local build: `lib/plugins/<type>-<name>/index.js`
- External plugin directories (`BSB_PLUGIN_DIRS`, comma-separated) [preferred in container]: `<dir>/<npmPackage>/<major>/<minor>/<micro>/lib/plugins/<type>-<name>/index.js`
- Node modules: `node_modules/<npmPackage>/lib/plugins/<type>-<name>/index.js`

Each plugin folder must export at least a `Plugin` class. Optionally export a `Config` class that extends `BSBPluginConfig` to provide validation and structured config.

Built-in plugin types include: `config-*`, `observable-*`, `events-*`, `service-*`.

### Development vs Production
- Container runtime (production): The container runs `lib/cli.js` (bin: `bsb`) and is the supported production path.
  - Runs `new ServiceBase(false, true, CWD)` (legacy signature -> optimized for production) inside the container entrypoint.
- Development runner: `src/dev.ts`
  - Runs `new ServiceBase(true, false, CWD)` with file watching
  - Creates `.bsbdevwatch` on first run; supports include/exclude patterns
  - Interactive controls:
    - `Ctrl+R` or typing `rs` to restart
    - `Ctrl+C`/`Ctrl+D` to dispose and exit
- Plugin CLI dev runner: `bsb-plugin-cli dev`
  - Watches `package.json`, `sec-config.yaml`, and `src`
  - Ignores dot-directories, `.git`, `lib`, `node_modules`, and `src/.bsb`
  - Add `bsb.dev.ignore` in `package.json` to append project-specific ignored paths/globs

### NPM Scripts
- `npm run dev`: Start development runner with hot-reload
- `npm start`: Run production CLI (`lib/cli.js` or `bsb`)
- `npm run tsc`: Clean and compile TypeScript to `lib/`
- `npm run build`: Clean -> tsc -> tests -> generate docs -> export schemas -> generate plugin metadata
- `npm run build-release`: Compile using `tsconfig-release.json`
- `npm run lint`: ESLint over `src/`
- `npm test`: Mocha in TS mode with JSON reporter output
- `npm run testDev`: Run tests with the default Mocha reporter
- `npm run generate-docs`: Generate TypeDoc JSON to `docs.json`
- API Reference: Hosted at `https://types.bsbcode.dev/nodejs/`
- `npm run export-schemas`: Export event schemas to `lib/schemas/{plugin-name}.json`
- `npm run generate-plugin-json`: Generate plugin metadata in `lib/schemas/`
- `npm run list-plugin-search-paths`: Print the packages/plugins visible to BSB, including package versions, plugin types, paths, and config-reference snippets; runs automatically during `build` and `build-release`

### Docker
Multi-stage build produces a minimal runtime image:
- Primary published image: `code.bettercorp.dev/bettercorp/service-base:node`
- Docker Hub mirror: `betterweb/service-base:node`
- `ENV NODE_ENV=production`, `ENV BSB_LIVE=true`, `ENV BSB_CONTAINER=true`, `ENV BSB_PLUGIN_DIRS=/mnt/plugins`
- Dockerfiles that extend the BSB Node image do not need to repeat those defaults unless intentionally overriding them.
- Volumes: `/mnt/plugins` (external plugins), `/mnt/temp`
- Entrypoint runs `node /home/bsb/node_modules/@bsb/base/lib/cli.js` as an unprivileged `node` user
- Built-in core plugins resolve from the installed `@bsb/base` package, not from a copied local `/home/bsb/lib/plugins` tree.
- The runtime package includes `npm run start`, `npm run debug`, and `npm run plugins` for shell/debug use inside the container.
- Optional plugin install/update at startup:
  - `BSB_PLUGINS="@scope/plugin-a:1.2.3,@scope/plugin-b@10"` -> installs listed packages
  - `BSB_PLUGIN_UPDATE=yes` -> refreshes packages listed in `BSB_PLUGINS`
- `BSB_SHOW_PACKAGES=true` -> prints the package/plugin discovery report before BSB starts
- `BSB_SYNC_PERMISSIONS=false` is the default: startup does not recursively change ownership or permissions. Set it to `true` when writable mounts or startup-installed plugins need permission repair. Watcher mode uses the same flag after each sync.
- The base image prepares permissions at build time. Derived Dockerfiles should run `RUN /home/bsb/sync-permissions.sh` as root after their final copy/install step. There is no automatic end-of-build hook.
- The helper processes `/home/bsb`, `/mnt/temp`, `BSB_PLUGIN_DIRS` (including legacy aliases), and `/home/bsb/.bsb` plus `BSB_WRITABLE_PATHS`. Read-only plugin mounts are skipped. For application files outside these paths, include their root for the build call, for example `RUN BSB_PLUGIN_DIRS=/app /home/bsb/sync-permissions.sh`.
- Permission sync sets ownership to `node:node`, code/plugin directories to `550` and files to `440`, writable directories to `770` and files to `660`. The entrypoint/helper remain executable and `sec-config.yaml` is `400`. Run the helper after commands that require writable build files or executable package binaries.
- Entrypoint progress logs are always enabled, including the permission-sync skip/completion marker and `BSB startup` immediately before launching Node.
- `BSB_PLUGIN_WATCHER=true` runs plugin sync mode instead of BSB. It checks `BSB_PLUGINS` on an interval, installs missing/new matching versions into the shared plugin directory, and skips complete versions that already exist.
- Derived Dockerfiles can log visible plugins after copying/installing them with `RUN node /home/bsb/node_modules/@bsb/base/lib/scripts/list-plugin-search-paths.js`

Example run (with mounted plugins directory):
```bash
docker run --rm \
  -e BSB_PLUGINS="@bettercorp/your-plugin@1.2.3" \
  -e BSB_SYNC_PERMISSIONS=true \
  -v $(pwd)/plugins:/mnt/plugins \
  code.bettercorp.dev/bettercorp/service-base:node
```

Recommended plugin directory layout (when using `BSB_PLUGIN_DIRS`):
```
/mnt/plugins/
  @org/plugin-a/
    1/2/3/
      package.json
      lib/plugins/service-plugin-a/index.js
      lib/plugins/observable-xyz/index.js
  @org/plugin-b/
    2/4/1/
      package.json
      lib/plugins/events-abc/index.js
```

Notes
- In container deployments, prefer placing prebuilt plugins under `BSB_PLUGIN_DIRS` as above. This avoids network installs on boot and ensures deterministic versions via immutable versioned folders.
- `BSB_PLUGINS` is available for dynamic `npm install` at startup, but mounting a curated plugin repository via `BSB_PLUGIN_DIRS` is recommended for production.
- `bsb client publish --target https://vault.example.com --plugin <id> --token <bv_p_...>` publishes a generated private-plugin schema directly to Vault; the npm package must still be available to the runtime.
- For shared plugin storage, run `code.bettercorp.dev/bettercorp/service-base:node` with `BSB_PLUGIN_WATCHER=true` and write access to `/mnt/plugins`; mount that volume read-only into runtime BSB containers. Runtime containers skip plugin-dir ownership and permission fixes when the mount is read-only.
- Avoid unversioned `BSB_PLUGINS` entries in production unless you intentionally want npm latest; `@latest` is rejected, and major/minor/exact selectors are supported.
- After changing BSB image versions that affect plugin install layout, run once with `BSB_PLUGIN_UPDATE=true` or clear the plugin cache volume so stale installed plugin folders are rebuilt.

### Environment Variables
- `APP_DIR`: Override working directory (mainly used in local development/testing)
- `BSB_DEBUG`: Enable debug logging in production mode (`true|1|yes|y`). Produces `production-debug` mode.
- `BSB_PLUGIN_DIRS`: Comma-separated list of external plugin directories (searched in order; first is install target)
- `BSB_PLUGIN_DIR`: Single external plugin directory (legacy, still supported). Accepts comma-separated paths.
- `BSB_PLUGINS`: Comma-separated list of npm packages to install at container start (entrypoint.js). Supports no selector, major, minor, and exact selectors. If installation fails, the container exits before BSB starts. Shared plugin storage uses package-scoped install locks so unrelated packages do not block each other.
- `BSB_PLUGIN_UPDATE`: `yes|true` to refresh packages explicitly listed in `BSB_PLUGINS`
- `BSB_SYNC_PERMISSIONS`: opt into recursive permission sync before BSB starts or after a watcher sync; default `false`, accepts `1|true|yes|y` (case-insensitive).
- `BSB_WRITABLE_PATHS`: comma-separated directories to create and make writable when permission sync runs. For runtime mounts, also enable `BSB_SYNC_PERMISSIONS=true`; this variable alone does not trigger a sync.
- `BSB_PLUGIN_WATCHER`: `yes|y|true` to run plugin watcher mode instead of BSB
- `BSB_PLUGIN_WATCH_INTERVAL_SECONDS`: Seconds between `node-watcher` sync runs. Default: 3600.
- `BSB_PLUGIN_WATCH_ONCE`: `yes|y|true` to run one watcher sync and exit.
- `BSB_SHOW_PACKAGES`: `yes|y|true` to print package/plugin discovery before startup
- Config plugin override (advanced):
  - `BSB_CONFIG_PLUGIN`: Name of config plugin (must start with `config-`)
  - `BSB_CONFIG_PLUGIN_PACKAGE`: npm package name hosting the config plugin

### Documentation

#### Plugin Development (v9)
- [Plugin Development Guide](https://github.com/BetterCorp/better-service-base/blob/master/nodejs/PLUGIN_DEVELOPMENT.md) - Complete guide for creating BSB plugins
- [Type System Guide](https://github.com/BetterCorp/better-service-base/blob/master/nodejs/TYPE_SYSTEM.md) - Cross-language type system reference

#### API Documentation
- API docs are generated with TypeDoc (`typedoc.json`).
  - `npm run generate-docs` -> emits `docs.json`
  - API docs are served at `https://types.bsbcode.dev/nodejs/`

### Testing
- Tests: Mocha + tsx
  - `npm test` -> CI-style JSON output (`junit.json`)
  - `npm run testDev` -> dev-friendly TS execution

### Creating Plugins

**For v9 plugin development, see the [Plugin Development Guide](https://github.com/BetterCorp/better-service-base/blob/master/nodejs/PLUGIN_DEVELOPMENT.md) for complete examples and best practices.**

Quick reference:
- Use `createEventSchemas()` to define typed events with compile-time validation
- Keep each EventSchemas key unique across emit/on and event type categories
- Use `createConfigSchema()` to define plugin configuration with metadata
- Use cross-language type helpers (`uuid`, `int32`, `datetime`, etc.) for better code generation
- Plugin metadata auto-generates `PLUGIN_CLIENT` and schema files during build

At minimum, export a `Plugin` class in `lib/plugins/<type>-<name>/index.js` (or `src/plugins/.../index.ts` in dev). For configurable plugins, export a `Config` created with `createConfigSchema()`. Publish your plugin as an npm package or ship its prebuilt folder structure under `BSB_PLUGIN_DIRS`.

### Quick Start (Container)
```bash
# Provide prebuilt plugins under ./plugins, matching the recommended layout
docker run --rm \
  -v $(pwd)/plugins:/mnt/plugins:ro \
  -e BSB_PLUGIN_DIR=/mnt/plugins \
  code.bettercorp.dev/bettercorp/service-base:node
```

Local development (for contributors only):
```bash
npm install
npm run dev
```

## Hosted clients

`bsb client install https://service.example.com` discovers public contracts at `/.well-known/bsb` and generates a client in this language. Use `--plugin org/name` when multiple contracts are hosted; `--source-language` and `--version` select an implementation. Saved schemas support offline regeneration. See the [discovery format and hosting instructions](../docs/hosted-client-discovery.md). Calls still use the configured BSB events transport.

---
_Source: https://npm.io/package/@bsb/base · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
