MCPcopy Index your code
hub / github.com/bex-co/bex

github.com/bex-co/bex @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
633 symbols 1,812 edges 171 files 210 documented · 33%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

bex

The open-source Render alternative — AI-native.

Push a Git repo (or a prebuilt image), get a running HTTPS service at <name>.onbex.co — on machines you own. bex runs identically as a local Docker mock and on Hetzner; only the infrastructure provider overlay changes. Built so AI agents can deploy and operate apps as first-class users, not an afterthought.

License deploy docs

Why bex

  • Own your PaaS. Render's developer experience — deploy-from-git, custom domains + TLS, suspend/resume — on your own hardware, Apache-2.0.
  • Drop-in familiar. bex.yml is render.yaml-shaped, and bex-api speaks Render's REST and GraphQL, verified against Render's OpenAPI spec (docs/bex-api.md).
  • Built for agents. Every action is an API call or a Kubernetes CR; state is machine-readable (phase / revision / url). No dashboard-only actions. See the mission and roadmap in docs/vision.md.

Quickstart: local mock (machines = Docker containers)

Prereqs: Docker (OrbStack works), Go 1.25+, kubectl, kind, clusterctl.

# 1. stand up the mock substrate: kind infra cluster + Cluster API + CAPD
#    + an app cluster whose nodes are Docker containers
bash scripts/mock-cluster.sh            # writes infra/local/bex.kubeconfig
export KUBECONFIG=$PWD/infra/local/bex.kubeconfig

# 2. build the operator image, load it into every node, deploy it as a pod
( cd lego/operator && make docker-build IMG=bex-operator:dev )
docker save bex-operator:dev -o /tmp/bex-op.tar
for n in $(kubectl get nodes -o name | sed 's|node/||'); do
  docker cp /tmp/bex-op.tar "$n":/op.tar && docker exec "$n" ctr -n k8s.io images import /op.tar
done
( cd lego/operator && make deploy IMG=bex-operator:dev )   # ns bex-system, BEX_RUNTIME=kubernetes
# local CAPD only: pin the operator to the control-plane node (see docs/deployment.md)
kubectl -n bex-system patch deploy bex-controller-manager --type merge -p \
 '{"spec":{"template":{"spec":{"nodeSelector":{"node-role.kubernetes.io/control-plane":""},
  "tolerations":[{"key":"node-role.kubernetes.io/control-plane","effect":"NoSchedule"}]}}}}'
kubectl -n bex-system rollout status deploy/bex-controller-manager

# 3. deploy an App — the operator reconciles it onto the worker machines
kubectl apply -f examples/whoami-app.yaml
kubectl get pods -l app.bex.co/app=whoami -o wide

# 4. ★ add a machine, then scale the App onto it
bash scripts/mock-cluster.sh scale 2
kubectl patch apps.app.bex.co whoami --type merge -p '{"spec":{"replicas":6}}'

# fast dev loop (optional): run the operator from source instead of as a pod —
# ( cd lego/operator && make install && BEX_RUNTIME=kubernetes make run )

Deploy to Hetzner: same bex, different provider — swap infra/clusterapi/overlays/local-capd…/hetzner-caph. See infra/README.md.

The App resource

apiVersion: app.bex.co/v1alpha1
kind: App
metadata: { name: whoami }
spec:
  image: traefik/whoami # prebuilt image; OR build from git with `repo:` + `branch:`
  port: 80
  replicas: 2 # pods bin-pack across machines

kubectl get apps.app.bex.co shows phase / revision / url. Prefer Render-style config? scripts/app-apply.sh <bex.yml> applies a render.yaml-shaped bex.yml as an App CR (DRY_RUN=1 to preview).

bex vs Render

Capability bex
Deploy from git (CNB / Dockerfile)
Custom domains + TLS
Suspend / resume / restart
REST API (Render-compatible) ✅ lifecycle verbs — create-service / deploys / logs planned
GraphQL (Render dashboard-compatible)
Elastic machines ✅ manual scale — autoscaler planned
Postgres control plane (tenants/auth) 🔜 planned
MCP server 🔜 planned
Managed databases — non-goal

AI-native

Today: a bearer-authed, Render-compatible REST + GraphQL API (docs/bex-api.md) an agent can drive end-to-end, and structured state on the App CR (status.phase, status.revision, status.url) that agents read without scraping. Next: an MCP server over the same verbs, deploy-from-chat (repo → URL in one call), and E2B-compatible sandboxes. The thesis and roadmap live in docs/vision.md.

Architecture

Two clusters: the app cluster runs the bex operator and your Apps; the infra cluster runs Cluster API, which provisions the app cluster's machines (Docker containers locally via CAPD, Hetzner servers via CAPH — same manifests, different overlay). The operator is the mechanism (reconciles App CRs into Deployment/Service/Ingress); the planned Postgres control plane is the intent layer (tenants/apps/domains) that will write those CRs — docs/control-plane.md. Two runtimes via BEX_RUNTIME: kubernetes (elastic, multi-machine) and opensandbox (single host, real pause/resume). Full map with diagrams: docs/architecture.md.

Layout

All Go lives in lego/ — a workspace of three modules; one image, two binaries. Details: lego/README.md.

lego/            the product: ALL Go (Latin legō, "I assemble"). go.work · Dockerfile — one image, two binaries
  types/            App/Database CRD contract (app.bex.co/v1alpha1); leaf, imports nothing
  operator/         mechanism: cmd/manager · internal/{controller,build,runtime} · config/ · codegen (make)
  backend/          bex-api: cmd/api · internal/api — Render REST/GraphQL/MCP + authz + API keys + metrics
                    dependency: operator → types ← backend  (operator never imports backend)
dashboard/       the human-facing dashboard (TanStack Start + Apollo + shadcn), client of bex-api's GraphQL
infra/           bex-infra: terraform/ · clusterapi/{base,overlays/{local-capd,hetzner-caph}} · local/
deploy/          gitops/{bootstrap,base,overlays/{local,staging,prod},charts} · opensandbox/ configs
examples/        whoami-app.yaml (prebuilt) · hello-go/ (build-from-git sample)
docs/            vision · architecture · control-plane · bex-api · deployment · custom-domain ·
                 restart-suspend-and-resume · go-and-gitops
scripts/         mock-cluster.sh · app-apply.sh · domain-add.sh · deploy-sample.sh ·
                 up.sh + start-opensandbox*.sh (legacy single-host path)

Status & roadmap

Working and verified: App CRD + reconcile, kubernetes runtime (App → Deployment → pods on machines), local CAPD mock with add/remove machine, opensandbox runtime with real pause/resume, custom domains + TLS, lifecycle verbs over REST/GraphQL, and a live Hetzner deployment. Tracked next — Postgres control plane, wake activator + HMAC webhook, autoscaler wiring, in-cluster builds, MCP server: docs/vision.md.

Contributing

See CONTRIBUTING.md. This repo is agent-friendly (CLAUDE.md). Licensed Apache-2.0.

Extension points exported contracts — how you extend this code

APIKeyStore (Interface)
APIKeyStore is Core's seam to the OAuth2 client registry — Hydra's admin API in production (NewHydraAPIKeys), a fake in [2 …
operator/internal/api/apikeys.go
Register (Interface)
(no doc)
dashboard/src/router.tsx
PodLogStream (FuncType)
PodLogStream streams a pod container's logs live (Follow:true) until ctx is cancelled. It is PodLogSource's tail-follow
operator/internal/api/logs.go
ImportMetaEnv (Interface)
(no doc)
dashboard/src/vite-env.d.ts
ResourceMetricsSource (FuncType)
ResourceMetricsSource returns the current per-pod usage for an App's pods. Core depends on this narrow function instead
operator/internal/api/metrics.go
ImportMeta (Interface)
(no doc)
dashboard/src/vite-env.d.ts
RequestMetricsSource (FuncType)
RequestMetricsSource returns request time-series for an App from the ingress metrics backend (Traefik scraped by Prometh
operator/internal/api/metrics.go
Window (Interface)
(no doc)
dashboard/src/vite-env.d.ts

Core symbols most depended-on inside this repo

cn
called by 189
dashboard/src/common/lib/utils/utils.ts
Get
called by 47
operator/internal/api/core.go
gqlField
called by 35
operator/internal/api/graphql.go
Error
called by 29
operator/internal/api/server.go
useLocalStorageState
called by 19
dashboard/src/common/hooks/use-local-storage-state.ts
sleep
called by 18
dashboard/src/common/lib/utils/time.ts
formatDateISO
called by 18
dashboard/src/common/lib/format/format-date-iso.ts
writeJSON
called by 17
operator/internal/api/rest.go

Shape

Function 446
Method 103
Struct 57
Interface 18
FuncType 4
TypeAlias 3
Class 2

Languages

Go51%
TypeScript49%

Modules by API surface

dashboard/src/common/components/ui/sidebar.tsx25 symbols
operator/api/v1alpha1/zz_generated.deepcopy.go20 symbols
operator/internal/api/core.go17 symbols
operator/internal/api/apikeys.go17 symbols
operator/internal/api/api_test.go17 symbols
operator/internal/api/render.go16 symbols
dashboard/src/common/components/ui/menubar.tsx16 symbols
operator/internal/api/metrics_test.go15 symbols
operator/internal/api/metrics.go15 symbols
operator/internal/api/auth.go15 symbols
dashboard/src/common/components/ui/dropdown-menu.tsx15 symbols
operator/internal/runtime/opensandbox.go14 symbols

Datastores touched

conn_dbDatabase · 1 repos

For agents

$ claude mcp add bex \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact