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.goimplementsNewApp,Initialize, and file‑system helpers used by the CLI and the server.internal/metadata/metadata.gostores app version, TLS certs, and RBAC data in a SQL store (internal/app/store/sql_store.go).internal/server/server.goruns the HTTP gateway, wiring routes frominternal/server/router.goto per‑app handlers ininternal/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:
- Removes any stale UNIX socket (
os.Remove). - Instantiates a
Router(internal/server/router.go) that registers theServeHTTPhandler (internal/app/app.go). - Calls
ServeHTTP(reaches 56 functions) for each request, which ultimately invokesStoreininternal/metadata/metadata.goto 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) andinternal/app/app.go. - Database –
internal/app/store/sql_store.go(23 functions) andinternal/metadata/metadata.go. - Network –
internal/app/container_handler.go(20 functions) viaexecCommand. - 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/*.ymluses mutable tags (e.g.,golangci/golangci-lint-action@v9). Replace with commit SHAs. - High – eval over runtime value –
internal/app/action/astatic/htmx.min.jscallseval()on computed input. Refactor to a safe parser. - Medium – Least‑privilege GITHUB_TOKEN – Workflows lack explicit
permissions:; addcontents: read. - Medium – Enable Dependabot – No dependabot config; add
.github/dependabot.yml. - Medium – Pin Docker base image –
deploy/Dockerfileusesalpine: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.goexceeds 2 600 lines. - Medium branching density – Heavy
if/elseininternal/app/apptype/param_loader.go. - Low TODO/FIXME markers – Stale comments in
internal/app/dev/styling.goandinternal/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.