Documentation
¶
Overview ¶
np_plugins_init.go — P97 G0-T04
Writes an idempotent SQL seed script that ensures one row exists in np_plugins for every plugin installed under pluginDir. The script is named 05-np-plugins-seed.sql so it runs strictly AFTER 04-np-plugins.sql (which CREATE TABLE IF NOT EXISTS np_plugins ... ) emitted by internal/postgres/generator.go.
Idempotency is guaranteed by INSERT ... ON CONFLICT (name) DO NOTHING. Re-running `nself build` produces the same script with the same content; re-running `nself start` (or, equivalently, re-applying init scripts on a rebuilt postgres volume) inserts no duplicate rows.
Why a "seed empty row" matters: downstream consumers (admin UI, doctor, plugin loader) join np_plugins by name. When a plugin is installed on disk but no row exists yet, those joins drop the plugin from views. Seeding an empty row at build time keeps every installed plugin discoverable from SQL, even before its first runtime touch.
Index ¶
- Constants
- func CheckGoPluginDockerfiles(pluginDir string) []string
- func ComputePluginEnvVars(workdir, pluginDir string) map[string]string
- func DefaultPluginDir() string
- func DetectServices(cfg *config.Config) []string
- func DiscoverPluginComposeFiles(workdir, pluginDir string) ([]string, error)
- func GenerateNpPluginsSeed(workdir, pluginDir string) (string, error)
- func InjectPluginNginxRoutes(workdir, pluginDir string, cfg *config.Config) (int, error)
- func MergeOllamaSidecar(composeYAML []byte, ollamaEnv map[string]string) ([]byte, bool, error)
- func NeedsRebuild(workdir string) (bool, error)
- func ReadComposeManifest(workdir string) ([]string, error)
- func RenderPluginConfigs(workdir, pluginDir string, cfg *config.Config) (int, error)
- func ShouldAutoEnableRedis(pluginDir string) bool
- func WriteComposeManifest(workdir string, baseCompose string, pluginFiles []string) error
- type BuildOptions
- type BuildResult
- type PostValidateResult
Constants ¶
const ComposeGeneratedHeader = "# GENERATED BY nself build - DO NOT HAND EDIT\n" +
"# Run 'nself build' to regenerate this file.\n"
ComposeGeneratedHeader is the canonical marker prepended to every generated docker-compose.yml. Pre-commit hooks and auditors grep for this exact line to confirm a compose file was produced by `nself build` and not hand-edited. See S32-T12 and the PPI nSelf-First Doctrine.
Variables ¶
This section is empty.
Functions ¶
func CheckGoPluginDockerfiles ¶ added in v1.0.3
CheckGoPluginDockerfiles scans pluginDir for Go plugins and verifies each has a Dockerfile containing a HEALTHCHECK instruction. Returns a list of warning strings for plugins where the check fails. Never returns an error — missing Dockerfiles or unreadable files produce warnings, not hard failures.
func ComputePluginEnvVars ¶
ComputePluginEnvVars returns environment variables needed by plugin compose files. These are written to .env.computed so that `docker compose` can interpolate them in plugin compose fragments.
Variables returned:
- NSELF_PLUGIN_DIR: absolute path to the global plugin directory
- PLUGIN_{NAME}_INTERNAL_URL: http://plugin-{name}:{port} for every declared dependency (required + optional) of every installed plugin. Only wired when the dependency plugin is also installed.
func DefaultPluginDir ¶
func DefaultPluginDir() string
DefaultPluginDir returns the default global plugin installation directory (~/.nself/plugins). Falls back to /tmp/.nself/plugins when the home directory cannot be determined.
func DetectServices ¶
DetectServices returns the list of Docker service names that should be generated in docker-compose.yml based on the current configuration.
Core services (4): postgres, hasura, auth, nginx Optional services (6): redis, minio, mailpit (email), search, functions, admin Custom services: CS_1..CS_10
Monitoring and MLflow are free plugins — not generated by core.
Redis auto-enable: when any BullMQ-backed plugin (ai, claw, mux, cron, notify) is installed in DefaultPluginDir() and cfg.Redis.Enabled is false, "redis" is appended automatically. The compose generator is driven from cfg, so callers (e.g. Build) must also set cfg.Redis.Enabled = true before passing cfg to compose.NewGenerator to keep both outputs consistent.
func DiscoverPluginComposeFiles ¶
DiscoverPluginComposeFiles scans pluginDir for installed plugins that contain a docker-compose.plugin.yml file. It returns absolute paths to each discovered compose file, sorted by plugin directory name for deterministic ordering. Plugins without a compose file are silently skipped (they are background-process plugins, not compose plugins).
func GenerateNpPluginsSeed ¶ added in v1.0.13
GenerateNpPluginsSeed scans pluginDir for installed plugins (each is a subdir with a plugin.json manifest), determines a tier ('free' / 'pro') for each, and writes postgres/init/05-np-plugins-seed.sql. The script uses INSERT ... ON CONFLICT (name) DO NOTHING so re-runs are no-ops.
If pluginDir is missing or empty, the function still writes a header-only file (a single comment + no INSERTs). That keeps the path stable across builds — operators inspecting postgres/init/ won't see the file vanish when they uninstall every plugin.
Returns the absolute path of the file that was written.
func InjectPluginNginxRoutes ¶
InjectPluginNginxRoutes scans pluginDir for installed plugins that ship nginx route configs (in a nginx/ subdirectory) and copies them into the project's nginx/sites/ directory after templating config variables.
Returns the number of config files injected. Plugins without a nginx/ directory are silently skipped.
func MergeOllamaSidecar ¶ added in v1.0.9
MergeOllamaSidecar injects an Ollama service into composeYAML when AI_OLLAMA_ENABLED=true is present in ollamaEnv. The injected service runs the official Ollama image on localhost:11434, which plugin-ai uses for local LLM inference without an external API key.
func NeedsRebuild ¶
NeedsRebuild reports whether the build outputs are stale and a rebuild is required. It returns true when any of the following hold:
- .env or docker-compose.yml does not exist
- .env is newer than docker-compose.yml
- The CLI version that produced the last build differs from the running version
- The build-version file is missing or unreadable
It returns false only when docker-compose.yml is at least as new as .env and the recorded build version matches the current CLI version.
func ReadComposeManifest ¶
ReadComposeManifest reads .nself/compose-files.txt and returns the ordered list of compose file paths. If the manifest does not exist, it returns a single-element slice with "docker-compose.yml" (relative, base only) so callers always get a usable default. Lines pointing to files that no longer exist on disk (e.g. a plugin was uninstalled) are silently skipped.
func RenderPluginConfigs ¶
RenderPluginConfigs walks installed plugins' configs/ directories and renders templates into the project workdir. For each installed plugin that has a configs/ directory, every file inside it is read, ${VAR} substitutions are replaced with project values, and the result is written to {workdir}/{relative_path} (stripping the configs/ prefix).
Returns the number of files rendered, or an error if any file operation fails.
func ShouldAutoEnableRedis ¶
ShouldAutoEnableRedis reports whether any BullMQ-backed plugin that requires Redis (ai, claw, mux, cron, notify) is installed in pluginDir. Detection is based on the presence of a plugin.json manifest file in the plugin's sub-directory.
Use DefaultPluginDir() when no override is needed.
func WriteComposeManifest ¶
WriteComposeManifest writes .nself/compose-files.txt with one compose file path per line. The first line is always the base docker-compose.yml (absolute path). Subsequent lines are absolute paths to plugin compose files. The .nself/ directory is created if it does not exist.
Types ¶
type BuildOptions ¶
type BuildOptions struct {
// Force rebuilds everything regardless of cache freshness.
Force bool
// Verbose enables detailed build progress output.
Verbose bool
// Check validates configuration and exits without generating files.
Check bool
// SecurityReport prints a detailed security audit after validation.
SecurityReport bool
}
BuildOptions controls build behavior via CLI flags.
type BuildResult ¶
type BuildResult struct {
// ProjectName is the sanitized project name from config.
ProjectName string
// ComposeFile is the path to the generated docker-compose.yml.
ComposeFile string
// NginxConfig is the path to the generated nginx/nginx.conf.
NginxConfig string
// SSLCerts is the number of SSL certificate sets generated.
SSLCerts int
// Duration is the wall-clock time the build took.
Duration time.Duration
// FilesGenerated is the total number of files written.
FilesGenerated int
// PluginComposeFiles lists the absolute paths to plugin compose files
// discovered during build. Empty when no plugins with compose files
// are installed.
PluginComposeFiles []string
// CAInstalled is true when the mkcert CA is trusted by the OS.
CAInstalled bool
// CAManualCmd is non-empty when the user must manually trust the CA.
CAManualCmd string
// HostsAdded is the number of new /etc/hosts entries written.
HostsAdded int
// HostsManualNote is non-empty when /etc/hosts could not be updated automatically.
HostsManualNote string
}
BuildResult summarizes what the build produced.
func Build ¶
func Build(workdir string, opts BuildOptions) (*BuildResult, error)
Build orchestrates the full nself build pipeline.
The sequence follows BUILD_SPEC.md:
- Load config via env cascade
- Validate config (security, passwords, ports, CORS)
- If --check: return after validation
- Check cache (skip rebuild if not --force and cache fresh)
- Create required directories
- Generate SSL certificates
- Generate nginx configuration files
- Generate docker-compose.yml
- Write docker-compose.yml with 0600 permissions
- Write .env.computed (DATABASE_URL + DOCKER_NETWORK)
- Save build version to .nself/build-version
- Return BuildResult with summary
type PostValidateResult ¶
type PostValidateResult struct {
ComposeValid bool
NginxValid bool
PortsUnique bool
NamesUnique bool
Errors []string
Warnings []string
}
PostValidateResult contains the results of all post-build checks.
func PostValidate ¶
func PostValidate(composePath, nginxConfDir string) PostValidateResult
PostValidate runs all post-build checks on the generated files.
composePath: path to docker-compose.yml nginxConfDir: path to nginx/sites/ directory (used to locate the parent nginx.conf)
Returns result with all findings. Never returns a Go error — all findings go in the result Errors/Warnings slices.