go-run

module
v0.1.47 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2026 License: MIT

README

go-run

File watching run utilities for general use.

Tool Package Description
execrun pkg/execrun Generic, language-agnostic file-watching command runner (YAML config)
runctl pkg/runctl Multi-target orchestrator — manage multiple execrun targets with HTTP API and optional web dashboard (-ui)

All tools use content-based change detection (SHA-256 hashing) with polling and fsnotify. All rebuilding and watching is based on glob patterns.

Additionally, provides helper packages so a go application can work even better with these utilities:

Library Package Description
backoffice pkg/backoffice Embeddable admin/debug HTTP server with service status, env, info, pprof
pprofdump pkg/pprofdump On-demand pprof dump HTTP handler for pre-OOM diagnostics
chiutil pkg/chiutil Self-documenting route folders for chi routers with auto-generated navigation UI

Start With The Examples

If you cloned this repo, the fastest way to understand the project is to run the configs under examples/.

# Full multi-target demo with the dashboard
go run ./cmd/runctl -c examples/runctl.yaml -ui

# Or run one example directly with execrun
go run ./cmd/execrun -c examples/hello-go/execrun.yaml

Open http://localhost:28099 for the example runui dashboard when running examples/runctl.yaml. The example targets cover:

  • examples/hello-go — small Go HTTP app
  • examples/ticker — Go ticker process
  • examples/build-css — build-only target
  • examples/echo-server — shell-based HTTP server
  • examples/backoffice-demo — embedded backoffice demo

Install

# execrun — generic file watcher
go install github.com/gur-shatz/go-run/cmd/execrun@latest

# runctl — multi-target orchestrator (API + optional web dashboard)
go install github.com/gur-shatz/go-run/cmd/runctl@latest

Or from source:

make install

execrun

Generic, language-agnostic file-watching command runner. Works with any language or toolchain — configured via a simple YAML file.

Quick Start
# Run one of the repo examples
go run ./cmd/execrun -c examples/hello-go/execrun.yaml

# Or generate a starter config for your own project
execrun init
execrun
Usage
execrun [flags] [command]
execrun init
execrun test
execrun sum
Flags
Flag Default Description
-c, --config <path> execrun.yaml Path to config file
--poll <duration> 500ms Poll interval for file changes
--debounce <duration> 300ms Debounce window
--stdout <file> Redirect child stdout to file (append mode)
--stderr <file> Redirect child stderr to file (append mode)
-v false Verbose output
Commands
Command Description
execrun init Generate a starter execrun.yaml
execrun -c myapp.yaml init Generate myapp.yaml
execrun test Run configured test: steps and exit
execrun sum Snapshot watched file hashes to execrun.sum
Config File

execrun.yaml:

title: Hello App
description: Main HTTP service

vars:
  LISTEN_ADDR: '0.0.0.0:{{ .PORT | default "8081" }}'

# File patterns to watch (gitignore-style globs)
watch:
  - "**/*.go"
  - "go.mod"
  - "go.sum"

# Build commands — preparation steps that run to completion.
build:
  - "go build -o ./bin/app ."

# Test commands — run after build and before the managed process starts.
test:
  - "go test ./..."

# Exec commands — the last command is the long-running managed process.
exec:
  - "echo address is {{ .LISTEN_ADDR }}"
  - "./bin/app"
Field Required Description
title no Optional display title used by runui for this target
description no Optional display description used by runui for this target
vars no Template variables (see Template Variables)
watch yes Glob patterns for files to watch (gitignore-style, ! for exclusions)
build no Build commands that run to completion before tests or process start
test no Test commands that run after build and before the managed process starts
exec no Run commands — the last is the managed process. Empty = build/test-only target

At least one of build, test, or exec must be non-empty.

Examples

Go:

watch:
  - "**/*.go"
  - "go.mod"
  - "go.sum"
build:
  - "go build -o ./bin/server ./cmd/server"
test:
  - "go test ./..."
exec:
  - "./bin/server"

Python (no build steps):

watch:
  - "**/*.py"
  - "requirements.txt"
exec:
  - "python app.py"

Node.js with TypeScript:

watch:
  - "src/**/*.ts"
  - "package.json"
build:
  - "npm run build"
exec:
  - "node dist/index.js"

Rust:

watch:
  - "src/**/*.rs"
  - "Cargo.toml"
build:
  - "cargo build"
exec:
  - "./target/debug/myapp"

Build-only (no managed process):

watch:
  - "*.css"
build:
  - "mkdir -p dist && cp style.src.css dist/style.css"
  - "echo CSS build complete"

Test-only:

watch:
  - "**/*.go"
test:
  - "go test ./..."

Multi-step with code generation:

watch:
  - "**/*.go"
  - "api/**/*.proto"
  - "!**/*.pb.go"
build:
  - "protoc --go_out=. api/*.proto"
  - "go generate ./..."
  - "go build -o ./bin/server ./cmd/server"
test:
  - "go test ./..."
exec:
  - "./bin/server"
Restart Flow
File change detected
  → Run build steps sequentially (fail → keep old process)
  → Run test steps sequentially (fail → keep old process)
  → Stop old process (SIGTERM → 5s timeout → SIGKILL)
  → Start last exec command as new process

If there are no build or test steps, the old process is stopped and restarted directly.

If the managed process exits on its own, execrun waits for the next file change to re-run the pipeline.

Library Usage
import (
    "github.com/gur-shatz/go-run/pkg/execrun"
    "github.com/gur-shatz/go-run/pkg/config"
)

cfg, vars, err := execrun.LoadConfig("execrun.yaml")
// Or with parent vars:
cfg, vars, err = execrun.LoadConfig("execrun.yaml", config.WithVars(parentVars))

runctl

Multi-target orchestrator. Manage multiple execrun targets from a single runctl.yaml, with an HTTP API for status and control. Use -ui to enable the embedded web dashboard.

runctl                    # Watch all enabled targets (API + watchers)
runctl -ui                # API + web dashboard at http://localhost:9100
runctl -ui -T "Local Dev" # API + web dashboard with custom title
runctl -t api             # Watch only the "api" target
runctl build              # Build all enabled targets and exit
runctl test               # Run tests for all enabled targets and exit
runctl -t api build       # Build only "api" and exit
runctl -t api test        # Test only "api" and exit
runctl sum                # Write .sum files for all enabled targets
runctl -t api -t web sum  # Write .sum files for "api" and "web" only
Commands
Command Description
init Generate a starter runctl.yaml
build Run build steps for selected targets and exit (no watchers, no HTTP server)
test Run test steps for selected targets and exit (no watchers, no HTTP server)
sum Snapshot watched file hashes to .sum files and exit
Flags
Flag Default Description
-c, --config runctl.yaml Config file path
-e <file> Load fallback environment variables from a YAML file
-t <name> Target filter (repeatable). Applies to watch, build, test, sum
-T, --title Override the web dashboard title
-ui false Serve embedded web dashboard
-v false Verbose output

The -t flag can be specified multiple times to select specific targets. Without -t, all enabled targets are used. An error is returned if a target name doesn't exist in the config.

Config File
title: Local Dev
description: API, worker, and frontend dev stack

vars:
  BASE_PORT: '{{ env "BASE_PORT" | default "8000" }}'
  API_PORT: "{{ add .BASE_PORT 80 }}"
  UI_PORT: "{{ add .BASE_PORT 99 }}"
  DATA_DIR: '{{ env "DATA_DIR" | default "/tmp/runctl-data" }}'

api:
  port: { { .UI_PORT } }

logs_dir: "{{ .DATA_DIR }}/logs"

targets:
  api:
    config: services/api/execrun.yaml
    vars:
      GREETING: '{{ env "GREETING" | default "Hello!" }}'
    links:
      - name: HTTP
        description: Main API endpoint
        url: "http://localhost:{{ .API_PORT }}"

  frontend:
    config: services/frontend/execrun.yaml
Field Required Description
title no Project title shown in the UI header and browser title
description no Optional summary text shown on the UI summary page
vars no Global template variables (see Template Variables)
api.port no HTTP API port (default: 9100)
logs_dir no Directory for log files (<target>.build.log/.test.log/.run.log)
targets yes Map of target name to target config
targets.*.config yes Path to the target's execrun YAML config
targets.*.enabled no Whether to start on launch (default: true)
targets.*.vars no Per-target template variables (override global vars)
targets.*.links no Named URLs or files shown in the dashboard
targets.*.links.*.name yes Link label
targets.*.links.*.description no Optional link description shown on the component page
targets.*.links.*.url no External URL. Mutually exclusive with file
targets.*.links.*.file no Local file to expose through the dashboard. Mutually exclusive with url

The config path is relative to the runctl.yaml directory. The target's working directory is derived from the config path's directory.

Resolved vars from runctl.yaml (both global and per-target) are automatically passed down to child execrun configs via config.WithVars(). Per-target vars override global vars of the same key. Child configs can reference parent vars with template syntax (e.g., {{ .API_PORT | default "8080" }}) and add their own vars: section.

Fallback Variables File (-e)

Use -e <file> to load fallback environment variables before runctl.yaml is parsed:

runctl -e vars.yaml

The file is a flat YAML map of environment variable names to values:

BASE_PORT: 28000
DATA_DIR: /tmp/runctl-data
HELLO_GREETING: Hello from vars.yaml

Values from -e are only applied when the variable is not already present in the process environment. Existing shell/system environment variables win over the fallback file.

BASE_PORT=9000 runctl -e vars.yaml
# BASE_PORT remains 9000, even if vars.yaml contains BASE_PORT: 28000

runctl.yaml can then read these values with {{ env "NAME" }}. Resolved vars: from runctl.yaml, including per-target vars, are later exported into the process environment for child processes; per-target vars override global vars of the same key.

Web Dashboard (-ui)

The web UI provides four tabs:

  • Summary — aggregate build/run/test state, counts, and failing target names
  • Build — last build duration/timestamp, build count, errors, and a rebuild button
  • Tests — last test duration/timestamp, test count, errors, and a re-run button
  • Run — target state, PID, uptime, restart count, custom links, and start/stop/restart buttons

Target names in the Build, Tests, and Run tabs link to a component page with the target's run state, build/test status, actions, links, logs, and backoffice entry point.

If title is set in runctl.yaml, or overridden with --title/-T, the browser title becomes:

<build-emoji><run-emoji><test-emoji> <title>

Each emoji is computed per dimension:

  • 🔴 when any target in that dimension is failed or exited
  • 🟡 when nothing is failing but at least one target is pending/in progress
  • 🟢 when all relevant targets in that dimension are healthy

Each target has a log viewer with virtual scrolling and a real-time tail mode.

HTTP API
GET  /api/health                    Health check
GET  /api/overview                  Project metadata and all target statuses
GET  /api/targets                   List all targets
GET  /api/targets/{name}            Get target status
POST /api/targets/{name}/build      Trigger rebuild + restart
POST /api/targets/{name}/test       Trigger tests only
POST /api/targets/{name}/start      Start target
POST /api/targets/{name}/stop       Stop target
POST /api/targets/{name}/restart    Stop + rebuild + restart
POST /api/targets/{name}/enable     Enable + start
POST /api/targets/{name}/disable    Disable + stop
GET  /api/targets/{name}/logs       Get logs (?stage=build|test|run&offset=N&limit=M)
Library Usage
import "github.com/gur-shatz/go-run/pkg/runctl"

cfg, err := runctl.LoadConfig("runctl.yaml")
if err != nil {
    log.Fatal(err)
}

ctl, err := runctl.New(*cfg, ".")
if err != nil {
    log.Fatal(err)
}

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()

ctl.Run(ctx)

Watch Patterns

All tools use the same glob pattern syntax (doublestar):

Pattern Matches
**/*.go All .go files recursively
cmd/**/*.go .go files under cmd/
*.go .go files in root only
{src,internal}/**/*.go .go files under src/ or internal/
**/*.{go,mdx,yaml} Multiple extensions

Patterns starting with ! are exclusions:

Pattern Effect
!**/*.pb.go Exclude protobuf generated files
!vendor/** Exclude vendor directory

Excludes always win. All include patterns are expanded first, then all exclude patterns are removed. You cannot re-include a file that was excluded.

Sum File

The sum file (e.g., execrun.sum) is a human-readable snapshot of watched files and their SHA-256 hashes:

cmd/server/main.go a1b2c3d
go.mod 9abcdef
internal/handler.go e4f5678

Sum files are derived from the config filename (x.yaml generates x.sum), persisted in the working directory, and updated on each rebuild.

Template Variables

All YAML configs (execrun.yaml, runctl.yaml) support Go template syntax for variable substitution, powered by pkg/config.

vars: Section

Define template variables in a top-level vars: section. Variables can reference environment variables, provide defaults, and depend on each other:

vars:
  BASE_PORT: '{{ env "BASE_PORT" | default "8000" }}'
  API_PORT: "{{ add .BASE_PORT 80 }}"
  DB_HOST: '{{ env "DB_HOST" | default "localhost" }}'

exec:
  - "./bin/server --port {{ .API_PORT }} --db {{ .DB_HOST }}"

The vars: section is removed from the final parsed config — it exists only for template resolution.

Template Syntax

Two delimiter styles are supported (useful when one conflicts with YAML quoting):

Syntax Example
{{ .VAR }} "http://localhost:{{ .API_PORT }}"
[[ .VAR ]] port: [[.API_PORT]]
Template Functions
Function Description Example
default Fallback value if empty/nil {{ .PORT | default "8080" }}
env Read OS environment variable {{ env "HOME" }}
required Error if value is empty/nil {{ .DB_URL | required "DB_URL must be set" }}
add Integer addition {{ add .BASE_PORT 80 }}
int / asInt Cast to integer {{ .PORT | int }}
Resolution

Variables are resolved iteratively (up to 10 passes) to handle dependency chains. For example, API_PORT depends on BASE_PORT — the resolver evaluates BASE_PORT first, then uses its value to resolve API_PORT.

Priority within a single config (highest wins):

  1. Parent vars passed via config.WithVars() (runctl → child configs)
  2. The config's own vars: section

Environment variables are not implicitly injected into template data. To read an env var, use {{ env "VAR" }} explicitly. This gives you full control — a var can read from the environment, provide a default, or ignore the environment entirely.

Variable Propagation (runctl)

When runctl loads child configs, resolved vars from runctl.yaml are passed down automatically. Child configs can reference parent vars and define their own.

Global vars

Defined at the top level of runctl.yaml. Available to all targets and to the runctl config itself (e.g., api.port, logs_dir):

# runctl.yaml
vars:
  BASE_PORT: '{{ env "BASE_PORT" | default "8000" }}'
  HELLO_PORT: "{{ add .BASE_PORT 80 }}"

targets:
  hello:
    config: hello/execrun.yaml
Per-target vars

Defined under targets.<name>.vars. These are resolved after global vars and can reference global vars via template syntax. Per-target vars override global vars of the same key:

vars:
  GREETING: "Hello" # global default

targets:
  hello:
    config: hello/execrun.yaml
    vars:
      GREETING: "Hello from hello!" # overrides global GREETING for this target
      EXTRA: "{{ .BASE_PORT }}-extra" # can reference global vars

  other:
    config: other/execrun.yaml
    # inherits GREETING: "Hello" from global vars

The merged vars (global + target overrides) are passed to the child execrun config as template data.

# hello/execrun.yaml — HELLO_PORT comes from parent, GREETING is the target override
build:
  - "go build -o ./bin/hello ./main.go"
exec:
  - './bin/hello -port {{ .HELLO_PORT | default "8080" }} -greeting "{{ .GREETING }}"'
How vars reach child processes

Child processes (build steps, exec commands, compiled binaries) receive vars in two ways:

  1. Template substitution — vars are injected into the child's execrun config at load time, so {{ .MY_VAR }} in command strings is replaced before execution.
  2. Environment inheritance — resolved vars (global and per-target) are set in the process environment via os.Setenv. Child processes inherit the full parent environment, so they can read vars as env vars even without template syntax. Per-target vars override global vars in the environment.

Supervisor OOM Hooks

The supervisor can track and enforce memory budgets for managed components. When a component stays above its hard memory threshold, or when pod-level pressure requires shedding a component, the supervisor treats the termination as a crash so normal restart, backoff, and rollback behavior still apply.

For Go services, the recommended pre-kill path is:

  1. The component registers an HTTP pprof dump endpoint.
  2. The supervisor component config sets memory.overflow-path to that endpoint path.
  3. On memory overflow, the supervisor POSTs to http://127.0.0.1:<component port><overflow-path>, waits up to kill_grace_period, then terminates the component.

Component example:

package main

import (
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/gur-shatz/go-run/pkg/pprofdump"
)

func main() {
	r := chi.NewRouter()
	pprofdump.RegisterChi(r, "/debug/pprof/dump", pprofdump.Options{
		Dir: "/var/lib/myapp/pprof-dumps",
	})
	_ = http.ListenAndServe(":8080", r)
}

Supervisor config example:

kill_grace_period: 10s

memory:
  enabled: true
  enforce: true
  limit_env_var: MEMORY_LIMIT_BYTES
  sustained_for: 30s
  pod_pressure_high: 0.90

components:
  - name: api
    port: 8080
    command: "./api"
    memory:
      overflow-path: /debug/pprof/dump
      hardlimit: 512m
      softlimit: 420m

The dump handler writes a timestamped directory containing heap, allocs, goroutine, threadcreate, block, and mutex profiles. The supervisor sends these headers on the overflow request:

Header Description
X-Go-Run-Overflow-PID PID of the child being terminated
X-Go-Run-Overflow-Child-PID Alias for X-Go-Run-Overflow-PID
X-Go-Run-Overflow-Reason Human-readable memory kill reason

Memory accounting keeps raw cgroup charge and working set separate. Raw memory.current is still reported for visibility, but pod-pressure decisions use working set (memory.current - inactive_file) when the kernel exposes it, so reclaimable file cache does not trigger avoidable restarts.


Backoffice

pkg/backoffice — an embeddable admin/debug HTTP server for Go services. Provides built-in endpoints for health status, environment inspection, runtime info, and pprof profiling, plus an extensible route folder for custom endpoints.

Quick Start
import "github.com/gur-shatz/go-run/pkg/backoffice"

bo := backoffice.New()

// Add custom endpoints
bo.Folder().GetDesc("/debug", "App debug info", myDebugHandler)

// Optional: protect TCP with basic auth (UDS stays open)
bo.SetAuth("admin", "secret")

// Start on Unix domain socket (no-op if GORUN_BACKOFFICE_SOCK is unset)
bo.ListenAndServeBackground(ctx)

// Start on TCP (independent of UDS)
bo.ListenAndServeTCPBackground(ctx, ":9090")
Built-in Endpoints
Endpoint Description
/status Service health status (JSON) — see Service Status
/env Environment variables (sensitive values masked)
/info PID, uptime, Go version, goroutines, memory
/debug/pprof/ Go pprof profiling (opens in new tab)
/ Auto-generated HTML route index
/index.json Machine-readable route index
Listening

UDS and TCP are independent — use either or both:

// UDS — driven by GORUN_BACKOFFICE_SOCK env var. No-op if unset.
bo.ListenAndServe(ctx)            // blocking
bo.ListenAndServeBackground(ctx)  // fire-and-forget

// TCP — explicit address
bo.ListenAndServeTCP(ctx, ":9090")            // blocking
bo.ListenAndServeTCPBackground(ctx, ":9090")  // fire-and-forget

Both shut down gracefully when ctx is cancelled.

Authentication
bo.SetAuth(username, password string, scope ...AuthScope)
Scope Protects Default
AuthTCPOnly TCP only yes
AuthUnixOnly Unix socket only
AuthBoth Both transports

Uses HTTP Basic Auth with constant-time comparison. The default AuthTCPOnly is practical: UDS is already protected by filesystem permissions, while TCP is network-exposed.

Custom Routes

Use Folder() to get the root chiutil.RouteFolder and register endpoints:

// Single endpoints
bo.Folder().GetDesc("/metrics", "Prometheus metrics", metricsHandler)
bo.Folder().PostDesc("/cache/flush", "Flush cache", flushHandler)

// Sub-folders group related endpoints
app := bo.Folder().Folder("/app")
app.GetDesc("/config", "App configuration", configHandler)
app.GetDesc("/connections", "Connection pools", connHandler)

All registered routes automatically appear in the HTML navigation UI.

Panic Recovery

The backoffice router includes chi/middleware.Recoverer. Handler panics return HTTP 500 without crashing the process.


pprofdump

pkg/pprofdump provides a small POST handler for writing pprof files to disk on demand. It is intended for last-chance diagnostics, especially from supervisor memory.overflow-path.

Standard library mux:

mux := http.NewServeMux()
pprofdump.Register(mux, "/debug/pprof/dump", pprofdump.Options{
	Dir: "/var/lib/myapp/pprof-dumps",
})

chi router:

r := chi.NewRouter()
pprofdump.RegisterChi(r, "/debug/pprof/dump", pprofdump.Options{
	Dir: "/var/lib/myapp/pprof-dumps",
})

Invoke it with:

curl -fsS -XPOST http://127.0.0.1:8080/debug/pprof/dump

The JSON response includes the dump directory and file names.


Service Status

pkg/backoffice includes a global, thread-safe service status registry. Services register themselves at startup and report their health; the backoffice /status endpoint exposes the aggregated state.

Levels
Level Value Meaning
OK 0 Healthy
RunningWithErrors 1 Operational with minor issues
Degraded 2 Reduced functionality
Down 3 Not operational
Usage
// Register at startup — starts at OK
dbSvc := backoffice.CreateServiceStatus("database", true)   // critical
cacheSvc := backoffice.CreateServiceStatus("cache", false)   // non-critical

// Update as conditions change
dbSvc.SetStatus(backoffice.OK, map[string]string{"version": "15.2"})
cacheSvc.SetStatus(backoffice.Down, map[string]string{"error": "connection refused"})

// Read aggregated status
info := backoffice.GetStatus()
// info.GlobalLevel, info.CausedBy, info.Services
Critical vs Non-Critical
  • Critical services can push the global level to any severity (including Degraded and Down).
  • Non-critical services cap their contribution to the global level at RunningWithErrors, regardless of their actual level.

This means a non-critical cache going Down results in a global RunningWithErrors, not Down.

Global Level

The global level is the worst level across all services (after applying the critical/non-critical cap). CausedBy names the service responsible.

Status Response (GET /status)
{
  "global_level": "OK",
  "caused_by": "",
  "services": [
    {
      "name": "database",
      "level": "OK",
      "critical": true,
      "time_in_state": "5m30s",
      "data": { "version": "15.2" },
      "history": [{ "timestamp": "...", "level": "OK" }],
      "uptime_pct": 99.8
    }
  ]
}
Per-Service Fields
Field Description
name Service name
level Current level (OK, RUNNING_WITH_ERRORS, etc.)
critical Whether the service is critical
time_in_state Duration since last level change
data Arbitrary JSON from the last SetStatus call
history Last 10 level changes with timestamps
uptime_pct Percentage of time not in Degraded or Down

chiutil

pkg/chiutil — self-documenting route folders for chi routers. Wraps a chi router and automatically generates a browsable HTML navigation UI and a JSON index at each level.

Route Folders
r := chi.NewRouter()
folder := chiutil.NewRouteFolderOn(r, "/")
folder.ServiceName("My Service")
folder.Title("Admin Panel")

// Register routes — they appear in the auto-generated index
folder.GetDesc("/health", "Health check", healthHandler)
folder.PostDesc("/cache/flush", "Flush all caches", flushHandler)

Each folder serves:

  • GET / — HTML navigation page
  • GET /index.json — machine-readable index
Sub-Folders
api := folder.Folder("/api")
api.GetDesc("/users", "List users", usersHandler)
api.GetDesc("/config", "Configuration", configHandler)

admin := folder.Folder("/admin")
admin.PostDesc("/restart", "Restart service", restartHandler)

Sub-folders appear as navigable directories in the parent's index.

Route Registration Methods

All methods register the route on the underlying chi router and add it to the folder index:

Method Description
Get GET route
GetDesc GET route with description
Post POST route
PostDesc POST route with description
Put PUT route
PutDesc PUT route with description
Patch PATCH route
PatchDesc PATCH route with description
Delete DELETE route
DeleteDesc DELETE route with description
Handle Any method
HandleDesc Any method with description
// Mount an existing chi router as a sub-folder
folder.MountDesc("/app", "Application routes", appRouter)

// Link to a path registered on a parent router (no handler mounted)
folder.Link("/debug/pprof", "Go profiling")

// External link — opens in a new browser tab
folder.ExternalLink("/debug/pprof", "Go profiling (pprof)")
Wildcard Folders

For dynamic collections where items are added/removed at runtime:

accounts := folder.WildcardFolder("accounts", "accountId", func(r chi.Router) {
    r.Get("/details", detailsHandler)
    r.Get("/settings", settingsHandler)
}).Title("Accounts")

// Manage instances dynamically
accounts.Add("acct-123", "Acme Corp")
accounts.Add("acct-456", "Globex Inc")
accounts.Remove("acct-123")

This creates:

  • /accounts/ — lists dynamic instances
  • /accounts/acct-123/ — lists routes for that instance
  • /accounts/acct-123/details — your handler
Static Files Folder
folder.StaticFilesFolder("logs", "/var/log/myapp")

Creates a browsable file system view. Files larger than 1 MB show a size warning in the preview but can still be downloaded directly.

FolderIndex JSON
{
  "serviceName": "My Service",
  "title": "Routes",
  "description": "",
  "path": "/",
  "entries": [
    {
      "name": "health",
      "method": "GET",
      "path": "health",
      "description": "Health check"
    },
    { "name": "api", "method": "GET", "path": "api/", "isFolder": true }
  ]
}

Design

  • Polling + hashing over fsnotify: simpler, portable, no file descriptor limits on macOS, catches content-only changes
  • Nagle debounce: batches rapid IDE saves into a single rebuild without adding latency to single-file changes
  • Content-based detection: only rebuilds when file contents actually change, not on metadata updates

Directories

Path Synopsis
cmd
execrun command
runctl command
supervisor command
examples
supervisor/origin command
origin is the vendor-side simulator for the supervisor demo.
origin is the vendor-side simulator for the supervisor demo.
internal
log
logtail
Package logtail provides log-file utilities shared between runctl and the supervisor: timestamped rotation, marker writing, and efficient tail / paginated reads of large files.
Package logtail provides log-file utilities shared between runctl and the supervisor: timestamped rotation, marker writing, and efficient tail / paginated reads of large files.
pkg
backoffice/logparser
Package logparser contains parsers for the backoffice log viewer.
Package logparser contains parsers for the backoffice log viewer.
backoffice/logviewer
Package logviewer exposes log files below a configured root as a small backoffice HTTP application.
Package logviewer exposes log files below a configured root as a small backoffice HTTP application.
chiutil
Package chiutil provides utilities for chi routers including self-documenting route folders with automatic navigation.
Package chiutil provides utilities for chi routers including self-documenting route folders with automatic navigation.
chiutil/htmlview
Package htmlview renders Go values as simple HTML pages for chiutil routes.
Package htmlview renders Go values as simple HTML pages for chiutil routes.
pprofdump
Package pprofdump provides an HTTP handler that writes a bounded set of Go pprof dumps to disk on demand.
Package pprofdump provides an HTTP handler that writes a bounded set of Go pprof dumps to disk on demand.

Jump to

Keyboard shortcuts

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