### Local Development Setup Source: https://github.com/alexandre-daubois/ember/blob/main/CONTRIBUTING.md Instructions for starting a local instance for fast testing. Two setups are available: minimal FrankenPHP or exhaustive Caddy. ```bash cd local/frankenphp && make local # minimal FrankenPHP (worker, metrics) cd local/caddy && make local # exhaustive Caddy (TLS, upstreams, hosts) ``` -------------------------------- ### Add Ember Setup Skill Source: https://github.com/alexandre-daubois/ember/blob/main/docs/skills.md Install the ember-setup skill using skills.sh. This skill helps with installation and Caddy configuration. ```bash npx skills add https://github.com/alexandre-daubois/ember --skill ember-setup ``` -------------------------------- ### Install Ember using Go Source: https://github.com/alexandre-daubois/ember/blob/main/docs/getting-started.md Installs the Ember binary using the Go toolchain. ```bash go install github.com/alexandre-daubois/ember/cmd/ember@latest ``` -------------------------------- ### Install Fish Shell Completions Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Installs Ember CLI shell completions for Fish. ```bash ember completion fish > ~/.config/fish/completions/ember.fish ``` -------------------------------- ### Build Ember with Plugins Source: https://github.com/alexandre-daubois/ember/blob/main/README.md Example of how to build a custom Ember binary with plugins using Go's blank import pattern. ```go import ( "github.com/alexandre-daubois/ember" _ "github.com/myorg/ember-myplugin" ) func main() { ember.Run() } ``` -------------------------------- ### Install Ember via curl Source: https://github.com/alexandre-daubois/ember/blob/main/README.md Use this command to download and execute the installation script for Ember. ```bash curl -fsSL https://raw.githubusercontent.com/alexandre-daubois/ember/main/install.sh | sh ``` -------------------------------- ### Install Zsh Shell Completions Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Installs Ember CLI shell completions for Zsh. ```bash ember completion zsh > "${fpath[1]}/_ember" ``` -------------------------------- ### Install Ember using curl Source: https://github.com/alexandre-daubois/ember/blob/main/docs/getting-started.md Installs the latest Ember release to /usr/local/bin. You can override the install directory with EMBER_INSTALL_DIR. ```bash curl -fsSL https://raw.githubusercontent.com/alexandre-daubois/ember/main/install.sh | sh ``` ```bash curl -fsSL https://raw.githubusercontent.com/alexandre-daubois/ember/main/install.sh | EMBER_INSTALL_DIR=~/.local/bin sh ``` -------------------------------- ### Minimal Caddyfile Example Source: https://github.com/alexandre-daubois/ember/blob/main/docs/caddy-configuration.md A basic Caddyfile configuration that enables the admin API and metrics, along with a simple site definition. ```caddyfile { admin localhost:2019 metrics } example.com { respond "Hello, world!" 200 } ``` -------------------------------- ### Install Ember using Homebrew Source: https://github.com/alexandre-daubois/ember/blob/main/docs/getting-started.md Installs Ember via the Homebrew package manager. ```bash brew install alexandre-daubois/tap/ember ``` -------------------------------- ### Plugin Configuration via Environment Variables Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Example of how to configure a plugin named 'ratelimit' using environment variables. These variables are passed to the `Provision` method as options. ```bash export EMBER_PLUGIN_RATELIMIT_MAX_RPS=1000 export EMBER_PLUGIN_RATELIMIT_WINDOW=60s ``` -------------------------------- ### TUI with Prometheus Metrics Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Start the TUI and expose Prometheus metrics on the specified address. ```bash ember --expose :9191 ``` -------------------------------- ### Multi-Instance JSONL Output Example Source: https://github.com/alexandre-daubois/ember/blob/main/docs/json-output.md This is an example of the JSONL output when polling multiple instances. Each line includes an `instance` field, and lines interleave based on each instance's polling interval. ```jsonl {"instance":"web1","threads":{...},"metrics":{...},"hosts":[...],"fetchedAt":"..."} {"instance":"web2","threads":{...},"metrics":{...},"hosts":[...],"fetchedAt":"..."} {"instance":"web1","threads":{...},"metrics":{...},"hosts":[...],"fetchedAt":"..."} ``` -------------------------------- ### Install Bash Shell Completions Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Installs Ember CLI shell completions for Bash. ```bash ember completion bash > /etc/bash_completion.d/ember ``` -------------------------------- ### Start Ember Source: https://github.com/alexandre-daubois/ember/blob/main/docs/getting-started.md Starts Ember, which connects to the Caddy admin API at http://localhost:2019 and polls metrics every second. If FrankenPHP is detected, a second tab appears automatically. ```bash ember ``` -------------------------------- ### Run Ember Container - Quick Start Source: https://github.com/alexandre-daubois/ember/blob/main/docs/docker.md Launches the Ember container with default settings. Use --rm to automatically remove the container when it exits. --network host shares the host's network stack. ```bash docker run --rm --network host alexandredaubois/ember ``` -------------------------------- ### Export-only Plugin Example Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md An example of an export-only plugin that fetches data and exposes Prometheus metrics without a TUI tab. It implements Fetcher and Exporter interfaces. ```go package cachemetrics import ( "context" "fmt" "io" "net/http" "github.com/alexandre-daubois/ember/pkg/plugin" ) func init() { plugin.Register(&cachePlugin{}) } type cachePlugin struct { endpoint string } type cacheStats struct { Hits int64 Misses int64 } func (p *cachePlugin) Name() string { return "cache" } func (p *cachePlugin) Provision(_ context.Context, cfg plugin.PluginConfig) error { p.endpoint = cfg.Options["endpoint"] if p.endpoint == "" { p.endpoint = "http://localhost:6379/stats" } return nil } func (p *cachePlugin) Fetch(_ context.Context) (any, error) { resp, err := http.Get(p.endpoint) if err != nil { return nil, err } defer resp.Body.Close() // Parse response into cacheStats... return &cacheStats{Hits: 1000, Misses: 50}, nil } func (p *cachePlugin) WriteMetrics(w io.Writer, data any, prefix string) { stats, ok := data.(*cacheStats) if !ok || stats == nil { return } name := "cache_hits_total" if prefix != "" { name = prefix + "_" + name } fmt.Fprintf(w, "# TYPE %s counter\n", name) fmt.Fprintf(w, "%s %d\n", name, stats.Hits) name = "cache_misses_total" if prefix != "" { name = prefix + "_" + name } fmt.Fprintf(w, "# TYPE %s counter\n", name) fmt.Fprintf(w, "%s %d\n", name, stats.Misses) } ``` -------------------------------- ### Example of MultiInstancePlugin Marker Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md This is a marker interface and is never called. It simply signals that the plugin supports multi-instance mode. ```go type myPlugin struct{} func (p *myPlugin) EmberMultiInstance() {} // marker only, never called ``` -------------------------------- ### Initialize Ember CLI Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Checks Caddy configuration for Ember and optionally enables HTTP metrics. Use `--addr` for specific instances and `-y` to skip prompts, which is required for multi-instance setups. ```bash ember init ``` ```bash ember init --addr https://prod:2019 --ca-cert ca.pem ``` ```bash ember init -y # skip confirmation prompts ``` ```bash ember init -yq # skip prompts, suppress output ``` ```bash ember init -y --addr web1=https://a --addr web2=https://b # multi-instance ``` -------------------------------- ### Implement Fetch for Multi-Instance Plugins Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Use `plugin.InstanceFromContext` within the Fetch method to identify the specific instance being polled. This is crucial for multi-instance setups where Fetch is called per instance. ```go func (p *myPlugin) Fetch(ctx context.Context) (any, error) { inst, _ := plugin.InstanceFromContext(ctx) // inst.Name, inst.Addr identify the instance being polled right now return queryMyBackend(inst.Addr) } ``` -------------------------------- ### Run multi-Caddy stack and Ember Source: https://github.com/alexandre-daubois/ember/blob/main/docs/multi-instance.md This command sequence starts a local multi-Caddy stack and then launches the Ember process in a separate terminal, configuring it to monitor the Caddy instances. ```bash cd local/multi-caddy make up # starts caddy-blue on :2019 and caddy-green on :2020 make ember # in another terminal: ember --daemon --expose :9191 --addr blue=... --addr green=... ``` -------------------------------- ### Implement MultiRenderer for Multiple Tabs Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Implement `MultiRenderer` instead of `Renderer` when your plugin needs multiple TUI tabs. Each tab gets its own `Renderer`, but all tabs share a single `Fetch` call. `Tabs()` is called once after `Provision()` to determine the number and order of tabs. `RendererForTab` is called once per tab to create its initial `Renderer`. If a plugin implements both `Renderer` and `MultiRenderer`, `MultiRenderer` takes priority. ```go type TabDescriptor struct { Key string Name string } type MultiRenderer interface { Tabs() []TabDescriptor RendererForTab(key string) Renderer } ``` -------------------------------- ### Generate traffic and scrape metrics Source: https://github.com/alexandre-daubois/ember/blob/main/docs/multi-instance.md After setting up the multi-Caddy stack and Ember, this command generates traffic and then scrapes the aggregated metrics from Ember to verify the setup. ```bash make traffic # generate a bit of load against both curl -s http://localhost:9191/metrics | grep ember_host_rps ``` -------------------------------- ### Minimal Ember Plugin Example Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md A basic plugin demonstrating how to add a TUI tab that displays a tick counter. It implements the `Fetcher`, `Renderer`, and `HelpBinding` interfaces. Always handle zero/nil data gracefully in the `View` method. ```go package stats import ( "context" "fmt" "sync/atomic" tea "github.com/charmbracelet/bubbletea" "github.com/alexandre-daubois/ember/pkg/plugin" ) func init() { plugin.Register(&statsPlugin{}) } type statsPlugin struct { counter atomic.Int64 count int64 } func (p *statsPlugin) Name() string { return "stats" } func (p *statsPlugin) Provision(_ context.Context, _ plugin.PluginConfig) error { return nil } // Fetcher: called on every tick. func (p *statsPlugin) Fetch(_ context.Context) (any, error) { return p.counter.Add(1), nil } // Renderer: provides a TUI tab. func (p *statsPlugin) Update(data any, _, _ int) plugin.Renderer { p.count = data.(int64) return p } func (p *statsPlugin) View(_, _ int) string { if p.count == 0 { return " Waiting for data..." } return fmt.Sprintf("\n Tick counter: %d\n", p.count) } func (p *statsPlugin) HandleKey(_ tea.KeyMsg) bool { return false } func (p *statsPlugin) StatusCount() string { return fmt.Sprintf("%d", p.count) } func (p *statsPlugin) HelpBindings() []plugin.HelpBinding { return nil } ``` -------------------------------- ### Add Ember Troubleshoot Skill Source: https://github.com/alexandre-daubois/ember/blob/main/docs/skills.md Install the ember-troubleshoot skill using skills.sh. This skill aids in diagnosing connection and FrankenPHP issues. ```bash npx skills add https://github.com/alexandre-daubois/ember --skill ember-troubleshoot ``` -------------------------------- ### Add Ember Production Skill Source: https://github.com/alexandre-daubois/ember/blob/main/docs/skills.md Install the ember-production skill using skills.sh. This skill is for managing daemon mode and Docker deployment. ```bash npx skills add https://github.com/alexandre-daubois/ember --skill ember-production ``` -------------------------------- ### Enabling Custom Metric Prefix in Ember Source: https://github.com/alexandre-daubois/ember/blob/main/docs/prometheus-export.md This command demonstrates how to start Ember with a custom prefix for all its exposed metrics. This is useful for distinguishing metrics from different Ember instances or applications. ```bash ember --expose :9191 --metrics-prefix myapp ``` -------------------------------- ### Ember Application with Plugin Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md A minimal main file that imports a custom plugin using a blank import. This setup is required to include the plugin in the final binary. ```go package main import ( "fmt" "os" "github.com/alexandre-daubois/ember" _ "github.com/example/ember-stats" // your plugin ) func main() { if err := ember.Run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } ``` -------------------------------- ### Add Ember JSON Skill Source: https://github.com/alexandre-daubois/ember/blob/main/docs/skills.md Install the ember-json skill using skills.sh. This skill supports JSON output and CI pipeline tasks. ```bash npx skills add https://github.com/alexandre-daubois/ember --skill ember-json ``` -------------------------------- ### Docker Compose - Caddy and Ember Sidecar Source: https://github.com/alexandre-daubois/ember/blob/main/docs/docker.md Minimal Docker Compose setup for Caddy and Ember running as a sidecar. Ember connects to Caddy via its service network. ```yaml services: caddy: image: caddy:latest ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile ember: image: alexandredaubois/ember network_mode: "service:caddy" depends_on: - caddy ``` -------------------------------- ### Plugin Interface Definition Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md The required Plugin interface must be implemented by all plugins. It includes methods for getting the plugin name and provisioning the plugin with configuration. ```go type Plugin interface { Name() string Provision(ctx context.Context, cfg PluginConfig) error } ``` -------------------------------- ### Enable Metrics Endpoint Source: https://github.com/alexandre-daubois/ember/blob/main/docs/prometheus-export.md Start Ember with the --expose flag to enable the Prometheus metrics endpoint on the specified port. This can be used with or without the TUI. ```bash ember --expose :9191 ``` ```bash ember --expose :9191 --daemon ``` -------------------------------- ### Accessing Plugin Configuration in Go Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Demonstrates how to access configuration options passed via environment variables within the `Provision` method of a plugin. Options are available in `plugin.PluginConfig.Options` with lowercased keys. ```go func (p *myPlugin) Provision(ctx context.Context, cfg plugin.PluginConfig) error { maxRPS := cfg.Options["max_rps"] window := cfg.Options["window"] // ... } ``` -------------------------------- ### Building and Running Custom Ember Binary Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Commands to initialize a Go module, tidy dependencies, build a custom Ember binary including your plugin, and run it. ```bash go mod init my-ember go mod tidy go build -o ember-custom . ./ember-custom ``` -------------------------------- ### Clone and Build Ember Project Source: https://github.com/alexandre-daubois/ember/blob/main/CONTRIBUTING.md Clone the repository, navigate into the directory, and build the project binary. Use `make help` to see all available targets. ```bash git clone https://github.com/alexandre-daubois/ember.git cd ember make build ``` -------------------------------- ### Run Linting Source: https://github.com/alexandre-daubois/ember/blob/main/CONTRIBUTING.md Execute the golangci-lint tool to check code quality and style. This is a required step before pushing changes. ```bash make lint ``` -------------------------------- ### Initialize Ember configuration Source: https://github.com/alexandre-daubois/ember/blob/main/docs/getting-started.md Checks your Caddy configuration and enables metrics if needed. This command verifies Caddy API reachability and enables the 'metrics' directive if missing. ```bash ember init ``` -------------------------------- ### Available Make Targets Source: https://github.com/alexandre-daubois/ember/blob/main/CONTRIBUTING.md Lists all available targets for the Makefile, including build, test, lint, and clean operations. Run `make help` to display this list. ```bash make build Build the binary make test Run tests with race detector, shuffle and no cache make test-nocolor Run UI tests under NO_COLOR=1 make lint Run golangci-lint make bench Run benchmarks make integration Run integration tests (requires running Caddy) make fuzz Run all fuzz targets for 30s each make check Run lint + tests + NO_COLOR variant make clean Remove build artifacts ``` -------------------------------- ### Plugin Interface Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md The required interface for all Ember plugins. It defines methods for getting the plugin's name and provisioning its configuration. ```APIDOC ## Plugin Interface ```go type Plugin interface { Name() string Provision(ctx context.Context, cfg PluginConfig) error } ``` - `Name()`: Returns the unique name of the plugin. - `Provision(ctx context.Context, cfg PluginConfig)`: Initializes the plugin with the provided configuration. ``` -------------------------------- ### Single JSON Snapshot Output Source: https://github.com/alexandre-daubois/ember/blob/main/docs/json-output.md Use the --once flag to get a single JSON object and exit. This is useful for scripting and CI pipelines. ```bash ember --json --once ``` -------------------------------- ### Configure Per-Instance TLS Suffixes Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md When using multiple addresses, each can have its own TLS configuration via comma-separated suffixes. This allows for per-instance certificate management. ```bash ember --daemon --expose :9191 \ --addr web1=https://a,ca=/etc/ca1.pem \ --addr web2=https://b,ca=/etc/ca2.pem ``` -------------------------------- ### Run Ember Container - Custom Flags Source: https://github.com/alexandre-daubois/ember/blob/main/docs/docker.md Overrides default behavior by appending custom flags to the Ember container command. This example sets a custom metrics prefix and interval. ```bash docker run --rm --network host alexandredaubois/ember \ --daemon --expose :9191 --interval 2s --metrics-prefix myapp ``` -------------------------------- ### PluginConfig Structure Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md PluginConfig provides configuration details to plugins, including the Caddy address, a list of plugin instances for multi-instance mode, and other options. ```go type PluginConfig struct { CaddyAddr string Instances []PluginInstance Options map[string]string } type PluginInstance struct { Name string Addr string } ``` -------------------------------- ### HelpBinding Structure Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md The HelpBinding struct defines a keybinding for the help overlay, consisting of a key and its description. ```go type HelpBinding struct { Key string Desc string } ``` -------------------------------- ### Configure Caddyfile with Host Matchers Source: https://github.com/alexandre-daubois/ember/blob/main/docs/troubleshooting.md Ensure your Caddyfile routes use explicit hostnames with host matchers to enable per-host labels for metrics. This prevents traffic from being aggregated under a single '*' host. ```caddy example.com { respond "Hello" } ``` ```caddy :80 { respond "Hello" } ``` -------------------------------- ### Multi-Instance Polling with JSON Output Source: https://github.com/alexandre-daubois/ember/blob/main/docs/json-output.md Use this command to poll multiple web instances and receive JSONL output, with each line prefixed by an instance identifier. The first emission is sorted alphabetically by instance name. ```bash ember --json \ --addr web1=https://web1.fr \ --addr web2=https://web2.fr ``` -------------------------------- ### Run Unit Tests Source: https://github.com/alexandre-daubois/ember/blob/main/CONTRIBUTING.md Execute the unit test suite with specific flags for race detection, shuffling, and no caching. This command does not require a live Caddy instance. ```bash make test ``` -------------------------------- ### Get Ember CLI Status Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Performs a health check on Caddy. Use `--json` for machine-readable output. For multi-instance checks, repeat the `--addr` flag; all instances must be reachable for a zero exit code. ```bash ember status ``` ```bash ember status --json ``` ```bash ember status --json | jq .rps ``` ```bash ember status --addr http://prod:2019 ``` ```bash ember status --interval 2s ``` ```bash ember status --addr web1=https://a --addr web2=https://b # multi-instance ``` -------------------------------- ### Run Benchmarks Source: https://github.com/alexandre-daubois/ember/blob/main/CONTRIBUTING.md Execute benchmark tests to measure performance. This command is useful for identifying performance regressions. ```bash make bench ``` -------------------------------- ### Implement MultiInstancePlugin for Multi-Instance Mode Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Opt-in marker for plugins that handle Ember's multi-instance mode (`--addr` repeated). Without this marker, plugins are disabled when more than one `--addr` is passed and a warning is logged. ```go type MultiInstancePlugin interface { EmberMultiInstance() } ``` -------------------------------- ### Multi-instance Monitoring with Aliases Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Monitor multiple Caddy instances with explicit aliases, exposing aggregated metrics. Useful for managing a fleet of services. ```bash ember --daemon --expose :9191 \ --addr web1=https://web1.fr \ --addr web2=https://web2.fr ``` -------------------------------- ### Implement TabAvailability for Individual Tab Visibility Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Implement `TabAvailability` in a `MultiRenderer` plugin when individual tabs should be shown or hidden independently. `TabAvailable(key)` is checked after each successful `Fetch` for every tab key returned by `Tabs()`. When it returns `false` for a key, that tab is removed from the tab bar. When it returns `true`, the tab is re-added. If `TabAvailable` panics, the tab stays visible (fail-open). ```go type TabAvailability interface { TabAvailable(key string) bool } ``` -------------------------------- ### MetricsSubscriber Interface for Receiving Metrics Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Implement the MetricsSubscriber interface to receive Caddy and FrankenPHP metrics. The OnMetrics method is called synchronously after each successful core fetch. The snapshot must not be modified. ```go type MetricsSubscriber interface { OnMetrics(snap *metrics.Snapshot) } ``` -------------------------------- ### Implement Availability for Conditional Tab Visibility Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Implement `Availability` when your plugin's tab(s) should be shown or hidden based on runtime conditions. `Available()` is checked after each successful `Fetch`. When it returns `false`, the plugin's tab(s) are removed from the tab bar. When it returns `true`, they are re-added. If `Available()` panics, the tab stays visible (fail-open). ```go type Availability interface { Available() bool } ``` -------------------------------- ### HelpBinding Structure Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Structure representing a keybinding for the help overlay. ```APIDOC ## HelpBinding Structure ```go type HelpBinding struct { Key string Desc string } ``` - `Key`: The key to be pressed. - `Desc`: The description of the action. ``` -------------------------------- ### Separating Renderer in Plugin Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Demonstrates separating the Renderer from the main plugin struct for plugins with complex state, following the Elm architecture pattern. ```go func (p *statsPlugin) Update(data any, _, _ int) plugin.Renderer { return &statsRenderer{count: data.(int64), ts: time.Now()} } // Initial View, before the first Fetch completes func (p *statsPlugin) View(_, _ int) string { return " Waiting for data..." } type statsRenderer struct { count int64 ts time.Time } func (r *statsRenderer) Update(data any, _, _ int) plugin.Renderer { return &statsRenderer{count: data.(int64), ts: time.Now()} } func (r *statsRenderer) View(_, _ int) string { return fmt.Sprintf("\n Tick counter: %d\n Last update: %s\n", r.count, r.ts.Format("15:04:05")) } func (r *statsRenderer) HandleKey(msg tea.KeyMsg) bool { return msg.String() == "x" } func (r *statsRenderer) StatusCount() string { return fmt.Sprintf("%d ticks", r.count) } func (r *statsRenderer) HelpBindings() []plugin.HelpBinding { return []plugin.HelpBinding{{Key: "x", Desc: "Example action"}} } ``` -------------------------------- ### Connect via Unix Socket Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Connect to a Caddy instance using a Unix domain socket. ```bash ember --addr unix//run/caddy/admin.sock ``` -------------------------------- ### Check Caddy Admin API Reachability Source: https://github.com/alexandre-daubois/ember/blob/main/docs/troubleshooting.md Use this command to quickly verify if the Caddy admin API is accessible. If it returns JSON, the API is reachable. Otherwise, Caddy's configuration needs to be fixed first. ```bash curl -s http://localhost:2019/config/ | head -c 100 ``` -------------------------------- ### MultiRenderer Interface Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Implement `MultiRenderer` when your plugin requires multiple TUI tabs. Each tab can have its own `Renderer`, but they share a single `Fetch` call. The `Tabs()` method defines the tabs, and `RendererForTab()` creates the renderer for each. ```APIDOC ## MultiRenderer ### Description Implement `MultiRenderer` instead of `Renderer` when your plugin needs multiple TUI tabs. Each tab gets its own `Renderer`, but all tabs share a single `Fetch` call. `Tabs()` is called once after `Provision()` to determine the number and order of tabs. `RendererForTab` is called once per tab to create its initial `Renderer`. If a plugin implements both `Renderer` and `MultiRenderer`, `MultiRenderer` takes priority. ### Interface Definition ```go type TabDescriptor struct { Key string Name string } type MultiRenderer interface { Tabs() []TabDescriptor RendererForTab(key string) Renderer } ``` ``` -------------------------------- ### Run Fuzz Targets Source: https://github.com/alexandre-daubois/ember/blob/main/CONTRIBUTING.md Execute all fuzz targets for 30 seconds each using the provided script. This is part of the comprehensive testing suite. ```bash make fuzz ``` -------------------------------- ### Configure Ember with explicit instance aliases Source: https://github.com/alexandre-daubois/ember/blob/main/docs/multi-instance.md This command demonstrates how to configure Ember with explicit aliases for Caddy instances to avoid name collisions, especially when dealing with IP addresses that might slugify to invalid names. ```bash ember --daemon --expose :9191 \ --addr blue=http://10.0.0.10:2019 \ --addr green=http://10.0.0.11:2019 ``` -------------------------------- ### Connect to Remote Caddy Instance Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Specify the address of a remote Caddy instance to connect to. ```bash ember --addr http://prod:2019 ``` -------------------------------- ### Run Integration Tests Source: https://github.com/alexandre-daubois/ember/blob/main/CONTRIBUTING.md Execute integration tests which run against a live Caddy instance. Ensure Caddy is running and the EMBER_TEST_CADDY_ADDR environment variable is set if not using the default. ```bash make integration ``` -------------------------------- ### Run Ember in Daemon Mode with Multiple Addresses Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Use this command to run Ember in daemon mode, exposing a port and specifying multiple addresses for monitoring. The hostnames are slugified by default. ```bash ember --daemon --expose :9191 \ --addr https://web1.fr \ --addr https://web2.fr ``` -------------------------------- ### Configure Ember Logging Sinks Source: https://github.com/alexandre-daubois/ember/blob/main/docs/logs.md These are the Caddy API calls to register two sinks, `__ember__` for access logs and `__ember_runtime__` for other logs. Use `soft_start: true` to prevent Caddy from blocking if the listener is temporarily unavailable. ```json PUT /config/logging/logs/__ember__ { "writer": { "output": "net", "address": "tcp/HOST:PORT", "soft_start": true }, "encoder": { "format": "json" }, "include": ["http.log.access"] } ``` ```json PUT /config/logging/logs/__ember_runtime__ { "writer": { "output": "net", "address": "tcp/HOST:PORT", "soft_start": true }, "encoder": { "format": "json" }, "exclude": ["http.log.access"] } ``` -------------------------------- ### Headless Metrics Exporter Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Run ember in headless mode without a TUI, only exposing Prometheus metrics. Requires `--expose`. ```bash ember --expose :9191 --daemon ``` -------------------------------- ### MultiInstancePlugin Interface Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Implement `MultiInstancePlugin` to opt-in to Ember's multi-instance mode, which allows plugins to handle multiple Caddy instances when launched with repeated `--addr` flags. ```APIDOC ## MultiInstancePlugin ### Description Opt-in marker for plugins that handle Ember's multi-instance mode (`--addr` repeated). Without this marker, plugins are disabled when more than one `--addr` is passed and a warning is logged. See [Multi-Instance Plugins](#multi-instance-plugins) below for the full contract. ### Interface Definition ```go type MultiInstancePlugin interface { EmberMultiInstance() } ``` ``` -------------------------------- ### Check Caddy HTTP Request Metrics Source: https://github.com/alexandre-daubois/ember/blob/main/docs/troubleshooting.md Verify that the 'metrics' directive is enabled by checking for 'caddy_http_requests_total'. If no output is returned, the directive is not active. ```bash curl -s http://localhost:2019/metrics | grep caddy_http_requests_total ``` -------------------------------- ### PluginConfig Structure Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Configuration structure passed to the plugin's `Provision` method, containing Caddy address, instances, and options. ```APIDOC ## PluginConfig Structure ```go type PluginConfig struct { CaddyAddr string Instances []PluginInstance Options map[string]string } type PluginInstance struct { Name string Addr string } ``` - `CaddyAddr`: The address of the Caddy server. - `Instances`: A list of plugin instances, populated in multi-instance mode. - `Options`: A map of additional configuration options. ``` -------------------------------- ### Run Ember Container - Unix Socket Source: https://github.com/alexandre-daubois/ember/blob/main/docs/docker.md Connects Ember to Caddy's admin API via a Unix socket. Ensure the socket path is correctly mounted into the container. ```bash docker run --rm \ -v /run/caddy/admin.sock:/run/caddy/admin.sock \ alexandredaubois/ember \ --daemon --expose :9191 --addr unix//run/caddy/admin.sock ``` -------------------------------- ### Availability Interface Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Implement the `Availability` interface to control the visibility of plugin tabs based on runtime conditions. The `Available()` method is checked after each successful `Fetch`. ```APIDOC ## Availability ### Description Implement `Availability` when your plugin's tab(s) should be shown or hidden based on runtime conditions. For example, a plugin compiled into a custom build but talking to a Caddy instance that does not have the corresponding module enabled. `Available()` is checked after each successful `Fetch`. When it returns `false`, the plugin's tab(s) are removed from the tab bar. When it returns `true`, they are re-added. If `Available()` panics, the tab stays visible (fail-open). ### Interface Definition ```go type Availability interface { Available() bool } ``` ``` -------------------------------- ### Exporter Interface for Writing Metrics Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Implement the Exporter interface to write Prometheus-format metric lines. The WriteMetrics method receives an io.Writer, the data, and a prefix for the metrics. ```go type Exporter interface { WriteMetrics(w io.Writer, data any, prefix string) } ``` -------------------------------- ### Connect to a remote Caddy instance Source: https://github.com/alexandre-daubois/ember/blob/main/docs/getting-started.md Use this flag to connect Ember to a remote Caddy instance instead of the default localhost. ```bash ember --addr http://your-host:2019 ``` -------------------------------- ### Run Pre-Push Checks Source: https://github.com/alexandre-daubois/ember/blob/main/CONTRIBUTING.md Execute all checks performed by the CI pipeline, including linting and tests with the race detector. This ensures code quality and correctness before submitting a pull request. ```bash make check ``` -------------------------------- ### Fetcher Interface for Data Retrieval Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Implement the Fetcher interface to retrieve data periodically. The Fetch method is called on each poll interval, and the returned data is interpreted by the plugin's Renderer or Exporter. ```go type Fetcher interface { Fetch(ctx context.Context) (any, error) } ``` -------------------------------- ### Configure Admin API via Unix Socket Source: https://github.com/alexandre-daubois/ember/blob/main/docs/caddy-configuration.md For enhanced security, configure the Caddy admin API to listen on a Unix socket. This prevents network exposure and relies on filesystem permissions for access control. ```caddyfile { admin unix//run/caddy/admin.sock } ``` ```caddyfile { admin unix//run/caddy/admin.sock|0660 } ``` -------------------------------- ### Wait for Caddy API Reachability Source: https://github.com/alexandre-daubois/ember/blob/main/docs/cli-reference.md Blocks until the Caddy admin API is reachable. Exits with code 1 if the --timeout is reached before Caddy is available. Use --any to return as soon as the first instance responds. ```bash ember wait ``` ```bash ember wait --timeout 30s ``` ```bash ember wait -q --timeout 10s && ./deploy.sh ``` ```bash ember wait --addr http://prod:2019 && ember status ``` ```bash docker compose up -d && ember wait && ./deploy.sh ``` ```bash ember wait --addr web1=https://a --addr web2=https://b --timeout 30s ``` ```bash ember wait --any --addr http://primary --addr http://fallback ``` -------------------------------- ### Extract CPU Percent from Single Snapshot Source: https://github.com/alexandre-daubois/ember/blob/main/docs/json-output.md Pipe the single JSON output from `ember --json --once` to `jq` to extract specific fields like CPU percentage. ```bash # Get current state as JSON ember --json --once | jq '.process.cpuPercent' ``` -------------------------------- ### Parse Prometheus Metrics with Ember's Package Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Utilize the `pkg/metrics` package to parse Prometheus metrics internally using `metrics.ParsePrometheus`. This provides a `MetricsSnapshot` of all Caddy and FrankenPHP metrics. ```go import "github.com/alexandre-daubois/ember/pkg/metrics" snap, err := metrics.ParsePrometheus(reader) ``` -------------------------------- ### Docker Compose - Multi-instance Sidecar Source: https://github.com/alexandre-daubois/ember/blob/main/docs/docker.md Configures Ember to scrape multiple Caddy instances and aggregate their metrics. Each Caddy instance is defined as a separate service. ```yaml services: caddy-blue: image: caddy:latest volumes: - ./Caddyfile:/etc/caddy/Caddyfile caddy-green: image: caddy:latest volumes: - ./Caddyfile:/etc/caddy/Caddyfile ember: image: alexandredaubois/ember environment: EMBER_ADDR: blue=http://caddy-blue:2019;green=http://caddy-green:2019 ports: - "9191:9191" depends_on: - caddy-blue - caddy-green ``` -------------------------------- ### Implement WriteMetrics for Plugins Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md In WriteMetrics, emit metrics directly without adding instance labels, as Ember automatically injects `ember_instance=""` for multi-instance plugins. This ensures valid Prometheus output. ```go func (p *myPlugin) WriteMetrics(w io.Writer, data any, prefix string) { // Just write your metrics naked; Ember adds ember_instance="..." for you. fmt.Fprintln(w, "# HELP my_plugin_requests_total ...") fmt.Fprintln(w, "# TYPE my_plugin_requests_total counter") fmt.Fprintf(w, "my_plugin_requests_total %d\n", count) } ``` -------------------------------- ### Enable Caddy Admin API Source: https://github.com/alexandre-daubois/ember/blob/main/docs/caddy-configuration.md Ensure the Caddy admin API is enabled by specifying its address. The default is localhost:2019. Be cautious as this API is unauthenticated by default. ```caddyfile { admin localhost:2019 } ``` -------------------------------- ### MetricsSubscriber Interface Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Optional interface for plugins to receive Caddy and FrankenPHP metrics. ```APIDOC ## MetricsSubscriber Interface ```go type MetricsSubscriber interface { OnMetrics(snap *metrics.Snapshot) } ``` - `OnMetrics(snap *metrics.Snapshot)`: Called synchronously after each successful core fetch. The snapshot contains parsed Caddy and FrankenPHP metrics. `snap.Metrics.Extra` contains unconsumed Prometheus metric families. ``` -------------------------------- ### Fetcher Interface Source: https://github.com/alexandre-daubois/ember/blob/main/docs/plugins.md Optional interface for plugins that need to fetch data periodically. ```APIDOC ## Fetcher Interface ```go type Fetcher interface { Fetch(ctx context.Context) (any, error) } ``` - `Fetch(ctx context.Context)`: Called at a defined interval to retrieve data. The returned data is opaque to Ember core. ``` -------------------------------- ### Enabling Basic Authentication for Ember Metrics and Health Endpoints Source: https://github.com/alexandre-daubois/ember/blob/main/docs/prometheus-export.md This command enables HTTP Basic Authentication for the `/metrics` and `/healthz` endpoints. All requests must include valid credentials to access these endpoints, enhancing security. ```bash ember --expose :9191 --daemon --metrics-auth admin:secret ``` -------------------------------- ### Configure Mutual TLS (mTLS) Source: https://github.com/alexandre-daubois/ember/blob/main/docs/caddy-configuration.md For mTLS, Caddy requires clients to present a certificate. Provide both the client certificate and its private key to Ember using --client-cert and --client-key flags, along with the --ca-cert. ```bash ember --addr https://caddy.internal:2019 \ --ca-cert /path/to/ca.pem \ --client-cert /path/to/client.pem \ --client-key /path/to/client-key.pem ```