Repository navigation
Bundles
How nself bundle install reaches an app-ready stack, and how nself records which bundles are installed and why each plugin is present.
nself bundle install <bundle> is done when the bundle is app-ready: every member is installed, the generated files are reconciled, the stack is started, every member that declares boot migrations has applied them, every table in the members' manifests is tracked in Hasura, and the services answer healthy. The command prints one line per step and ends with APP-READY, or with NOT APP-READY: step <step> failed for plugin <plugin>.
| # | Step | What it checks |
|---|---|---|
| 1 | licence |
Entitlement for a paid bundle. A free bundle (the Task Bundle) makes no bundle entitlement call: the step reads ok with free bundle: no bundle entitlement call; plugin licences checked. |
| 2 | fetch |
The registry resolves every member version. |
| 3 | install |
Each member is installed in order; a failure rolls back what this run installed. |
| 4 | ledger |
The bundle and its installed members are recorded in the ledger. |
| 5 | reconcile |
Generated project files are regenerated for the new membership (skipped when nothing changed). |
| 6 | start |
nself start brings the stack up. |
| 7 | readiness |
Each member with migrations.apply: boot reports every migration applied in /health (waits up to NSELF_PLUGIN_READY_TIMEOUT). The plugin applies its own SQL; nself never does. |
| 8 | tracking |
Tracks every table named in a member's manifest that Hasura does not track yet, with one targeted bulk request of pg_track_table operations (never replace_metadata, never an untrack; already tracked tables send no request), then verifies them with export_metadata. x means public.x; s.x means schema s. A manifest table missing from Postgres fails the step naming the plugin and the table. Tracking is NSELF_V15=1 behaviour; nself plugin install does the same for the plugins it installs, including newly installed dependencies, and fails with E140 (exit 2). Only members that declare migrations.apply: boot are tracked (nself plugin install names a member with tables but no boot migrations in a note; nself bundle install does not track it either, and the tracking check then reports its untracked tables as E135, as before). Tables count as tracked only on the default postgres source. A metadata write by someone else between the export and the bulk request can give a false E135 or E140 that a rerun clears. |
| 9 | health |
The stack services are healthy and each member answers HTTP 200 on /health. |
The first failing step stops the run; later steps show skipped. Outside an nSelf project the plugins are installed and recorded, the later steps are skipped, and the output says NOT VERIFIED; the exit code stays 0.
nself bundle install <bundle> --dry-run prints the nine steps as planned and changes nothing: no files, no ledger, no hooks.
A bundle that is not app-ready fails with E135 (exit 2) naming the bundle, the step and the plugin. Starting the stack and the readiness, tracking and health steps are NSELF_V15=1 behaviour. With NSELF_V15 off the command installs, records the ledger and reconciles (a failed reconcile is an error in both modes), prints those four steps as skipped / not verified (v1.5 behaviour, NSELF_V15=1), ends with NOT VERIFIED instead of APP-READY, and exits 0. A free bundle skips the ping_api bundle entitlement call; each plugin's licence is still checked. Rerun the command after fixing the cause; members that are already installed are not reinstalled.
The Task Bundle reaches app-ready (v1.5). With NSELF_V15=1, nself bundle install task --yes on a clean project on Linux with Docker (nself init && nself start) installs the free plugins served by plugins.nself.org and ends with APP-READY: all eight members (storage, notifications, jobs, search, feature-flags, audit-log, webhooks, tokens) apply their own migrations at boot, the tracking step tracks every table in their manifests (the behaviour in step 8), and each member answers /health. One prerequisite: set TOKENS_ENCRYPTION_KEY in the project .env before the install (an exported shell variable is not passed to the plugin) (a random 32-byte hex string, for example openssl rand -hex 32). The tokens plugin refuses to start without it and nothing in the CLI generates it yet, so without it the tokens container restarts in a loop and the readiness step ends with E118 for tokens after its bounded wait.
nself plugin install <name> of a plugin with boot migrations waits for them too when the stack is running (E118 or E119, exit 2; a warning with NSELF_V15 off). With the stack stopped it says readiness is checked at the next nself start or nself doctor.
nself doctor --deep reports E136 for each plugin the ledger lists whose plugin.json is gone from the plugin directory, and nothing when the ledger matches the disk. The fix is to reinstall the bundle (nself bundle install <bundle>) to restore the missing plugin; do not delete the ledger. A deliberate nself plugin remove of a bundle member drops that plugin's record from the ledger, so it is not reported.
The plugin directory ($NSELF_PLUGIN_DIR, default ~/.nself/plugins) keeps one machine-owned file, .nself-state/bundles.json. It is the install-state ledger, contract cli.bundle-ledger v1. Its JSON Schema is schemas/bundle-ledger.v1.schema.json.
The file starts with "_generated": "GENERATED BY nself — DO NOT HAND EDIT". nself writes it; do not edit it. A hand-edited or damaged ledger is reported as an error with the file path, never silently replaced.
{
"_generated": "GENERATED BY nself — DO NOT HAND EDIT",
"schema_version": 1,
"bundles": {
"chat": { "installed_at": "2026-10-10T12:00:00Z" }
},
"plugins": {
"bots": {
"installed_by": ["chat"],
"explicit": false,
"tier": "licensed",
"version": "1.2.0",
"checksum": ""
}
}
}| Field | Meaning |
|---|---|
bundles.<slug>.installed_at |
When the bundle was recorded as installed (RFC 3339, UTC). |
plugins.<slug>.installed_by |
The installed bundles that reference the plugin, sorted. |
plugins.<slug>.explicit |
True when the plugin was installed on its own, so removing a bundle must keep it. |
plugins.<slug>.tier |
free or licensed. |
plugins.<slug>.version |
The installed version. |
plugins.<slug>.checksum |
Lowercase hex sha256 of the installed artifact, empty when none was recorded. |
A plugin record exists only while installed_by is not empty or explicit is true.
- Writers take an OS lock on
bundles.json.lock(next to the ledger), write a temp file in the same directory, fsync it, and rename it over the ledger. A reader sees the old file or the new one, never a partial file. - A crash between the temp write and the rename leaves the previous ledger intact.
- The file is mode 0644 (it holds no secret) in a 0700 directory,
<plugin dir>/.nself-state. That directory has noplugin.json, so plugin scans skip it. Before P7-PLUG-19 the ledger lived at<project>/.nself/state/bundles.json; it now follows the plugins, so every project sharing a plugin directory shares one ledger. - Windows is supported through WSL2, where the lock applies.
A bundle remove deletes a plugin only when the bundle was the last one in its installed_by and explicit is false. A plugin that another installed bundle still references, or that was installed on its own, stays. The ledger package implements the rule (RemoveBundle); nself bundle remove (and nself remove <bundle>) apply it.
nself bundle remove <bundle> plans from the ledger, prints the plan, then removes:
-
Refcounts. Only plugins whose
installed_bybecomes empty and that are notexplicitare removed, in reverse bundle order. Every other member stays and the plan says why (kept: py (installed_by: beta)). With bundles A={x,y} and B={y,z}, removing A removes x; y stays withinstalled_by: [B]. -
Shared plugin directory. The ledger lives in the plugin directory (
$NSELF_PLUGIN_DIRor~/.nself/plugins), so every project that uses that directory shares one record per bundle.bundle remove Ain one project removes A's plugins for every project sharing the directory, asplugin removealways has. Use a per-projectNSELF_PLUGIN_DIRto isolate projects. - Free bundles (the Task Bundle) remove like any other bundle.
-
Data is kept (schema and tables) unless you pass
--delete-data.--delete-dataasks for confirmation on a terminal; without a terminal it needs--yes, otherwise it exits 4 with E403 and changes nothing. A dry run needs neither. -
Not recorded. The ledger at
<plugin dir>/.nself-state/bundles.jsonrecords a bundle only when all its plugins are installed. For a bundle it does not record,bundle removefails with E134 (exit 1): nothing it installed is removable as a unit. Remove the individual plugins withnself plugin remove <plugin>. -
--dry-runprints the plan and changes nothing; it never creates the ledger.nself remove <plugin> --dry-runprints the plugin, the data rule, the referencing bundles and whether E133 would refuse, and changes nothing. - A failure stops the run. The ledger then keeps the bundle recorded and drops only the plugins already removed, so running the same command again continues from there.
-
--forcekeeps its meaning (overwrite hand-edited generated files).--keep-datais a hidden, deprecated alias; from v1.5 it does nothing and prints a warning. It is removed in v1.6.0.
nself remove <plugin> (and nself plugin remove) refuses a plugin an installed bundle still references: E133, exit 1, naming the bundle. Remove the bundle, or pass --force to remove only that plugin; the ledger then drops the plugin's record.
| v1.4 mode (default today) | v1.5 mode (NSELF_V15=1) |
|
|---|---|---|
| Paid bundle, no data flag | drops plugin data, with one warning naming --delete-data and --keep-data
|
keeps plugin data |
| Free bundle, no data flag | keeps plugin data | keeps plugin data |
--delete-data |
needs --yes or a terminal confirmation (E403, exit 4) |
same |
remove <plugin> of a bundle member |
removes it, with a warning | refuses with E133 unless --force
|
| Unrecorded bundle | warns, removes nothing, exit 0 | E134, exit 1 |
--keep-data |
keeps data | deprecated no-op, with a warning |
Plugin removal inside a bundle remove still bypasses the reverse-dependency check (force), as before.
When no ledger exists, nself derives one from the installed plugin directories and the bundle membership in bundles.json:
- A bundle counts as installed when it can be installed as a unit (every bundle except the ɳSelf+ meta-bundle) and every member plugin is installed.
-
installed_bylists every installed bundle that contains the plugin (bundles may overlap). - A plugin in no installed bundle is
explicit: true.
Bootstrap cannot tell a plugin you installed yourself from one a bundle installed, if a bundle later covers it. It treats that plugin as bundle-owned. Installs recorded after the ledger exists are exact.
A plugin or bundle whose name is not a valid slug cannot be recorded; bootstrap skips it and prints a warning. If the installed plugins cannot be read, there is no bootstrap: nself reports the error and writes nothing, rather than recording an empty ledger.
nself bundle list --json reads the ledger; the plain table never loads it. When the file does not exist it computes the bootstrap view in memory and writes nothing.
Each row of the JSON array gains an additive ledger object, that bundle's slice of the ledger:
{
"slug": "chat",
"ledger": {
"installed": true,
"installed_at": "2026-10-10T12:00:00Z",
"plugins": [
{ "slug": "bots", "installed_by": ["chat"], "explicit": false, "tier": "licensed", "version": "1.2.0", "checksum": "" }
]
}
}installed is false and installed_at is absent for a bundle that is not recorded as installed. plugins lists the bundle's member plugins that the ledger records, and is [] when there are none. All earlier fields are unchanged.
See also cmd-bundle.
Getting Started
Commands
- Commands, Overview
- Lifecycle: cmd-init · cmd-build · cmd-start · cmd-stop · cmd-restart · cmd-dev
- Monitoring: cmd-status · cmd-logs · cmd-health · cmd-urls · cmd-doctor · cmd-monitor · cmd-alerts · cmd-sentry · cmd-watchdog
- Data: cmd-db · cmd-backup · cmd-dr · cmd-queue · cmd-webhooks
- Config: cmd-config · cmd-service · cmd-env · cmd-promote
- Networking: cmd-ssl · cmd-trust · cmd-dns-setup
- Security: cmd-access · cmd-security · cmd-secrets
- Tenancy: cmd-tenant · cmd-billing
- Plugins: cmd-plugin · cmd-license · cmd-dogfood (extracted, CLI-R11) · cmd-k8s (extracted, CLI-R11) · cmd-encryption (extracted, CLI-R11) · cmd-waf (extracted, CLI-R11) · cmd-federation (extracted, CLI-R11) · cmd-mail (extracted, CLI-R11) · cmd-dlq (extracted, CLI-R11)
- AI: cmd-ai · cmd-claw · cmd-model
- Templates: cmd-template
- Utilities: cmd-exec · cmd-clean · cmd-reset · cmd-update · cmd-upgrade · cmd-version · cmd-admin · cmd-migrate · cmd-migrate-firebase · cmd-migrate-supabase · cmd-completion
Features
- Features, Overview
- Feature-Auth
- Feature-Storage
- Feature-Search
- Feature-Functions
- Feature-Email
- Feature-Monitoring
- Feature-Plugins
- Feature-nClaw, AI Assistant
- Feature-nChat, Messaging
- Feature-nFamily, Family Social
- Feature-nCloud, Managed Hosting
- Feature-Memory-Rooms, Knowledge Organization
- Feature-Agent-Dashboard, Agent Metrics
- Feature-Image-Generation, AI Image Generation
Configuration
- Configuration, Overview
- Config-Env-Vars
- Config-Postgres
- Config-Hasura
- Config-Auth
- Config-Nginx
- Shared-Proxy
- Config-Optional-Services
- Config-Custom-Services
- Config-System
Plugins (87 + 10 monitoring)
- Plugin-Overview
- Plugin-Catalog
- Plugin-Install
- Plugin-Licensing
- Plugin-Architecture
- Plugin-Dev-Guide
- Plugin-Migrations-Contract
Free (25)
- plugin-backup
- plugin-content-acquisition
- plugin-content-progress
- plugin-cron
- plugin-donorbox
- plugin-feature-flags
- plugin-github
- plugin-github-runner
- plugin-invitations
- plugin-jobs
- plugin-link-preview
- plugin-mdns
- plugin-mlflow
- plugin-monitoring
- plugin-notifications
- plugin-notify
- plugin-paypal
- plugin-search
- plugin-shopify
- plugin-stripe
- plugin-subtitle-manager
- plugin-tokens
- plugin-torrent-manager
- plugin-vpn
- plugin-webhooks
Pro (62)
- plugin-access-controls
- plugin-activity-feed
- plugin-admin-api
- plugin-analytics
- plugin-auth
- plugin-bots
- plugin-browser
- plugin-calendar
- plugin-cdn
- plugin-chat
- plugin-claw
- plugin-claw-budget
- plugin-claw-news
- plugin-claw-web
- plugin-cloudflare
- plugin-cms
- plugin-compliance
- plugin-ddns
- plugin-devices
- plugin-documents
- plugin-entitlements
- plugin-file-processing
- plugin-game-metadata
- plugin-geocoding
- plugin-geolocation
- plugin-google
- plugin-home
- plugin-idme
- plugin-knowledge-base
- plugin-linkedin
- plugin-livekit
- plugin-media-processing
- plugin-meetings
- plugin-moderation
- plugin-mux
- plugin-object-storage
- plugin-observability
- plugin-photos
- plugin-podcast
- plugin-post
- plugin-realtime
- plugin-retro-gaming
- plugin-rom-discovery
- plugin-social
- plugin-sports
- plugin-support
- plugin-tmdb
- plugin-voice
- plugin-web3
- plugin-workflows
Planned (26)
plugin-auditplugin-blogplugin-checkoutplugin-commerceplugin-drmplugin-exportplugin-flowplugin-importplugin-ldapplugin-mailgunplugin-mediaplugin-oauth-providersplugin-pagesplugin-postmarkplugin-rate-limitplugin-reportsplugin-samlplugin-schedulerplugin-sendgridplugin-ssoplugin-subscriptionplugin-thumbplugin-transcoderplugin-twilioplugin-wafplugin-watermark
Guides
- Guide-Production-Deployment
- Guide-SSL-Setup
- Guide-Multi-Tenancy
- Guide-Security-Hardening
- Guide-Monitoring-Setup
- Guide-Backup-Restore
- Guide-Custom-Services
- Guide-Migration-from-v1
Architecture
- Architecture
- Architecture-Guardrails
- Service-Graph
- Compose-Generation
- Nginx-Generation
- Plugin-Architecture
Reference
Machine interface
-
JSON-Output,
--jsonenvelope -
Command-Registry,
nself help --json - Exit-Codes
- error-codes, Error Codes
-
Compat-V15,
NSELF_V15gate
Licensing
Security
Brand
Operations
- operations/release-cascade, Release Cascade
- operations/self-healing, Self-Healing Schema
- operations/redis-tuning, Redis Pool Tuning
- operations/meilisearch-warmup, MeiliSearch Warm-Up
- operations/jwt-rotation, JWT Key Rotation
- operations/windows-wsl2-setup, Windows / WSL2 Setup
- operations/gemini-oauth-reauth, Gemini OAuth Reauth
Contributing
Admin
- USER-ACTION-QUEUE, Pending Admin Actions
All commands (21)
- A: cmd-add · cmd-admin
- B: cmd-backup · cmd-build
- C: cmd-clean · cmd-config
- D: cmd-db · cmd-deploy · cmd-doctor
- E: cmd-exec
- I: cmd-init
- L: cmd-license · cmd-logs
- M: cmd-mcp
- R: cmd-remove · cmd-reset · cmd-restart
- S: cmd-start · cmd-status · cmd-stop
- U: cmd-update