hotreload

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 10, 2026 License: AGPL-3.0 Imports: 11 Imported by: 0

Documentation

Overview

Package hotreload implements live config reloading for BubbleFish Nexus source files. It watches the sources directory with fsnotify and applies config changes atomically via an RWMutex.

Hot reload scope (per Tech Spec Section 14.1):

  • Source config changes: applied atomically. In-flight handlers complete with the old config. Auth hot path uses RLock; reload uses Lock.
  • Destination config changes: WARN logged, old config kept. Restart required.
  • Compiled JSON: written atomically (temp file + fsync + os.Rename).

INVARIANTS:

  • NEVER apply destination config changes via hot reload.
  • NEVER write compiled JSON non-atomically.
  • watchLoop goroutine exits cleanly when stop channel is closed.
  • NEVER use Lock() on the auth hot path. Only RLock().

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// SourcesDir is the directory watched for *.toml source config files.
	SourcesDir string

	// ConfigDir is the root config directory (used for compiled JSON output).
	ConfigDir string

	// Mu is the shared RWMutex that guards the daemon's config pointer.
	// Auth hot path must use RLock; reload uses Lock.
	Mu *sync.RWMutex

	// Snapshot returns the current *config.Config. The Watcher calls this
	// while holding Mu.RLock() — the caller must not acquire any additional locks.
	Snapshot func() *config.Config

	// Apply atomically installs a new *config.Config. The Watcher calls this
	// while holding Mu.Lock() — the caller must not acquire any additional locks.
	Apply func(*config.Config)

	// Reload loads a fresh *config.Config from disk. Called by the Watcher
	// outside of any mutex; must be safe to call from a goroutine.
	Reload func() (*config.Config, error)

	// SigningKey is the resolved signing key bytes. When non-nil, the watcher
	// re-verifies compiled config signatures before applying a reload.
	// Nil means signing is disabled — no verification is performed.
	// NEVER log this value.
	SigningKey []byte

	// SigningEvent is called when a config signature verification failure
	// occurs during hot reload. May be nil if signing is disabled.
	SigningEvent signing.SecurityEventFunc

	// Logger is the structured logger for watcher events.
	Logger *slog.Logger
}

Config holds all parameters required to construct a Watcher.

type Watcher

type Watcher struct {
	// contains filtered or unexported fields
}

Watcher monitors the sources directory for *.toml changes and hot-reloads source configuration. All state is in struct fields — no package-level vars.

func New

func New(cfg Config) *Watcher

New creates a Watcher from cfg. Call Start() to begin watching. Panics if any required field in cfg is nil.

func (*Watcher) Start

func (w *Watcher) Start() error

Start adds the sources directory to an fsnotify watcher and launches the watchLoop goroutine. Returns an error if the directory cannot be watched (e.g. it does not exist); the daemon should log a warning and continue.

Start must be called at most once. The goroutine is stopped by calling Stop().

func (*Watcher) Stop

func (w *Watcher) Stop()

Stop signals the watchLoop goroutine to exit and waits for it to finish. Safe to call multiple times; only the first call has any effect (sync.Once).

Reference: Phase 0D Behavioral Contract item 10.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL