The Problem

Teams that run internal tools often have to juggle ad‑hoc scripts, manual Docker commands, and fragile Kubernetes manifests. Adding a new app or updating its config becomes a repeatable, error‑prone process, especially when the same codebase must support both single‑node Docker/Podman deployments and full‑cluster Kubernetes installations.

What This Does

OpenRun provides a single binary (cmd/openrun/main.go) that reads declarative app specifications (YAML/TOML) and translates them into either a local Docker container or a set of Kubernetes resources generated from the Terraform modules in deploy/terraform/. The core logic lives in the internal/ package:

  • internal/app/app.go implements NewApp, Initialize, and file‑system helpers used by the CLI and the server.
  • internal/metadata/metadata.go stores app version, TLS certs, and RBAC data in a SQL store (internal/app/store/sql_store.go).
  • internal/server/server.go runs the HTTP gateway, wiring routes from internal/server/router.go to per‑app handlers in internal/app/handler.go.

The CLI commands (cmd/openrun/*.go) expose sub‑commands such as apply, preview, and sync that invoke the same libraries used by the long‑running server, ensuring identical behaviour in CI/CD and interactive use.

How It Is Wired

Execution starts at func main() in cmd/openrun/main.go (169 lines, reaches 145 functions). It parses flags, builds a Config struct, then calls GetDefaultConfigs → getEmbeddedToml, which reads embedded configuration files from the binary (ReadFile on the embedded FS).

When the user runs openrun server, main creates a Server (internal/server/server.go) and calls its Start method (line 483, reaches 90 functions). Start:

  1. Removes any stale UNIX socket (os.Remove).
  2. Instantiates a Router (internal/server/router.go) that registers the ServeHTTP handler (internal/app/app.go).
  3. Calls ServeHTTP (reaches 56 functions) for each request, which ultimately invokes Store in internal/metadata/metadata.go to persist app state in the SQL database (ExecContext).

App updates flow through cmd/openrun/apply_cmd.go, which calls printApplyResponse → printStdout (20 ×). The CLI also uses downloadFile in internal/app/dev/jslibs.go to fetch external JS assets (http.Get → io.Copy).

Key effect owners:

  • Filesystem – internal/app/appfs/disk_fs.go (25 functions) and internal/app/app.go.
  • Database – internal/app/store/sql_store.go (23 functions) and internal/metadata/metadata.go.
  • Network – internal/app/container_handler.go (20 functions) via execCommand.
  • Crypto – internal/server/router.go (41 functions) for TLS termination and secret generation.

The internal call graph shows a few high‑blast‑radius helpers: TestLogger, AssertEqualsInt, and Close are used >70 times each in tests; production code’s widest fan‑out is Start and ServeHTTP. No circular import cycles were detected.

How To Use It

# Clone the repo
git clone https://github.com/moses-y/openrun
cd openrun

# Build the binary
go build -o openrun ./cmd/openrun

# Run the server locally (uses an embedded SQLite DB)
./openrun server

# Deploy a single‑node app from a spec file
./openrun apply -f examples/disk_usage/app.star

For Kubernetes, generate Terraform manifests:

cd deploy/terraform
terraform init
terraform apply -var-file=../tfvars.sample

The Docker image can be built with the provided Dockerfile:

docker build -t openrun:latest -f deploy/Dockerfile .

Configuration files are read from deploy/ (Terraform) and the embedded TOML in internal/app/app.go. No additional environment variables are required for a local run; production deployments must supply a PostgreSQL DSN (refer to internal/app/store/sql_store.go for the DB_URL flag).

Real‑World Use

A SaaS team stores each internal dashboard as a Git repo with an app.star file. A GitHub Actions workflow runs openrun preview -f app.star on PRs to validate the spec, then openrun apply on merge to push the change to the single‑node staging host. The same repo can be pointed at the Terraform output to roll the dashboard out to a shared EKS cluster without any code changes.

Code Health & Issues

  • High – Pin GitHub Actions – .github/workflows/*.yml uses mutable tags (e.g., golangci/golangci-lint-action@v9). Replace with commit SHAs.
  • High – eval over runtime value – internal/app/action/astatic/htmx.min.js calls eval() on computed input. Refactor to a safe parser.
  • Medium – Least‑privilege GITHUB_TOKEN – Workflows lack explicit permissions:; add contents: read.
  • Medium – Enable Dependabot – No dependabot config; add .github/dependabot.yml.
  • Medium – Pin Docker base image – deploy/Dockerfile uses alpine:3.22.2; replace with digest.
  • Medium – Dependency‑review gate – No vulnerability scan in CI; add dependency-review-action.
  • Medium – Persist‑credentials:false – Checkout step keeps the token; set persist-credentials: false.
  • Low – Job timeouts – CI jobs lack timeout-minutes; add reasonable limits.
  • Low – Missing convention files – Add .editorconfig, .gitattributes, and a formatter config.

Static analysis also reported:

  • High cognitive load – Deep nesting (max depth 8) in internal/app/handler.go, internal/app/plugin.go, internal/server/server.go.
  • High duplicated blocks – Repeated 6‑line snippets across multiple command files.
  • High oversized files – cmd/openrun/app_cmds.go exceeds 2 600 lines.
  • Medium branching density – Heavy if/else in internal/app/apptype/param_loader.go.
  • Low TODO/FIXME markers – Stale comments in internal/app/dev/styling.go and internal/app/store/sql_store.go.

The Bottom Line

OpenRun delivers a coherent Go‑based engine for declarative internal‑tool deployment, with clear separation between CLI, server, and storage layers. The codebase is functional but suffers from deep nesting, duplicated command logic, and several security‑hygiene gaps that should be addressed before production use. Teams comfortable with Go and willing to invest in refactoring will find it a solid foundation for unified single‑node and Kubernetes deployments.