Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# Nebula provider credentials — SECRETS.
# Nebula provider credentials — SECRETS (plus one per-cluster endpoint).
#
# Copy to .env and fill in real values. .env is gitignored — never commit tokens.
# Consumed by hack/deploy.sh (make deploy-all), which turns the credentials into one
# Kubernetes Secret PER PROVIDER. Static non-secret config (image, namespace, Kind
# cluster) is NOT here — pass it as make variables, e.g.
# make deploy-all IMG=myrepo/nebula:v1 DEPLOY_KIND_CLUSTER=nebula-test-e2e
#
# The lone non-secret that DOES belong here is SANDD_TUNNEL_SERVER (bottom): it is a
# per-cluster value discovered at deploy time (the headscale NLB hostname), so keeping
# it beside the deploy that consumes it — rather than a hand-passed make flag re-typed
# every run — is the whole point. It rides the SAME parsed-not-sourced path as the
# creds, so it can't leak into kubectl either.
#
# cp .env.example .env
# # edit .env
# make deploy-all
Expand Down Expand Up @@ -38,3 +44,22 @@ AWS_SECRET_ACCESS_KEY=
# --- Additional providers (add as adapters land) ---------------------------
# Each provider gets its OWN secret (see hack/deploy.sh PROVIDER_SECRETS), e.g.:
# RUNPOD_API_KEY=

# --- SandD mesh endpoint (NON-SECRET, per-cluster) -------------------------
# The internet-facing headscale NLB hostname the SandD mesh dials, as a bare
# http://<host> (no port — the Service listens on 80; see config/sandd). It is only
# known AFTER config/samples/headscale-service.yaml provisions and changes on every
# recreate, so it can't live in the tracked manifests. deploy-all reads it from here
# and `make deploy` substitutes the __SANDD_TUNNEL_SERVER__ token into headscale's
# server_url and the nebula-sandd-config ConfigMap (byte-identical, as headscale
# requires) — no re-typing across files, no manager restart.
#
# Read it back after applying the Service (README config/sandd step 1):
# kubectl apply -f config/samples/headscale-service.yaml
# HS=$(kubectl -n nebula-system get svc nebula-headscale \
# -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
# echo "SANDD_TUNNEL_SERVER=http://$HS" # paste the result below, then make deploy-all
#
# Leave blank to skip SandD wiring — the token is then left in place (an obviously
# broken render, surfaced by a deploy.sh warning), not a silent misconfig.
SANDD_TUNNEL_SERVER=
37 changes: 37 additions & 0 deletions Dockerfile.keybroker
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# keybroker — the headscale pre-auth key broker (cmd/keybroker), packaged as a
# SIDECAR for the headscale pod. It shells out to the `headscale` CLI over the
# shared unix socket, so the final image must carry BOTH our Go binary AND the
# headscale CLI — hence the `FROM headscale/headscale` base rather than distroless.
#
# Build (from the repo root):
# docker build -f Dockerfile.keybroker -t inftyai/nebula-keybroker:latest .
#
# Keep the headscale tag here IN SYNC with config/sandd/headscale.yaml so the CLI
# in this sidecar speaks the same wire/flag version as the headscale it dials.

# --- build the broker binary ---------------------------------------------------
FROM golang:1.24 AS builder
ARG TARGETOS
ARG TARGETARCH

WORKDIR /workspace
COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download

# Only the broker's own sources are needed (it is stdlib-only), but copy the
# module dirs it might import so the build is self-contained.
COPY cmd/keybroker/ cmd/keybroker/

# CGO off => a static binary that runs on the headscale base image regardless of
# its libc.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} \
go build -a -o keybroker ./cmd/keybroker

# --- final image: headscale CLI + our broker -----------------------------------
# FROM headscale so `headscale preauthkeys create` is on PATH. The broker calls it
# over the local unix socket shared with the real headscale container in the pod.
FROM headscale/headscale:0.23
COPY --from=builder /workspace/keybroker /usr/local/bin/keybroker
# Override headscale's own entrypoint: this container runs the BROKER, not headscale.
ENTRYPOINT ["/usr/local/bin/keybroker"]
26 changes: 25 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Image URL to use all building/pushing image targets
IMG ?= inftyai/nebula-controller:latest

# KEYBROKER_IMG is the SandD key-broker sidecar image (cmd/keybroker,
# Dockerfile.keybroker) that runs alongside headscale — see config/sandd. Built and
# pushed separately from the manager IMG because it is a distinct, optional image
# with its own base (FROM headscale, so it carries the headscale CLI).
KEYBROKER_IMG ?= inftyai/nebula-keybroker:latest

# NAMESPACE is where the manager runs (must match config/manager). Consumed by
# hack/deploy.sh via the deploy-all target.
NAMESPACE ?= nebula-system
Expand Down Expand Up @@ -155,6 +161,14 @@ docker-build: ## Build docker image with the manager.
docker-push: ## Push docker image with the manager.
$(CONTAINER_TOOL) push ${IMG}

.PHONY: docker-build-keybroker
docker-build-keybroker: ## Build docker image for the SandD key-broker sidecar.
$(CONTAINER_TOOL) build -f Dockerfile.keybroker -t ${KEYBROKER_IMG} .

.PHONY: docker-push-keybroker
docker-push-keybroker: ## Push the SandD key-broker sidecar image.
$(CONTAINER_TOOL) push ${KEYBROKER_IMG}

# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/
Expand Down Expand Up @@ -195,7 +209,17 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified
.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
$(KUSTOMIZE) build $(KUSTOMIZE_BUILD_FLAGS) config/default | $(KUBECTL) apply -f -
# SANDD_TUNNEL_SERVER is the internet-facing headscale NLB hostname, only known
# after that Service provisions and different on every recreate — so it can't be
# baked into the tracked manifests. The config/sandd overlay carries the literal
# token __SANDD_TUNNEL_SERVER__ in headscale's server_url and the nebula-sandd-config
# ConfigMap; substitute it here so both render correct on the FIRST apply (no
# manager/headscale restart). Driven from .env via deploy-all (make deploy-all),
# or pass SANDD_TUNNEL_SERVER=... directly. When unset the token is left as-is (the
# render is then obviously broken rather than silently pointing nowhere).
$(KUSTOMIZE) build $(KUSTOMIZE_BUILD_FLAGS) config/default | \
$(if $(SANDD_TUNNEL_SERVER),sed 's|__SANDD_TUNNEL_SERVER__|$(SANDD_TUNNEL_SERVER)|g',cat) | \
$(KUBECTL) apply -f -

.PHONY: deploy-e2e
deploy-e2e: manifests kustomize ## Deploy for e2e: config/default plus the fake-provider env var (baked in at deploy time, not via a post-deploy rollout).
Expand Down
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/InftyAI/Nebula.svg)](https://pkg.go.dev/github.com/InftyAI/Nebula)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
![Go Version](https://img.shields.io/badge/go-1.24-00ADD8?logo=go&logoColor=white)
[![Discord](https://img.shields.io/badge/Discord-join-5865F2?logo=discord&logoColor=white)](https://discord.gg/7WTUuFqyS6)

Run GPU workloads on any NeoCloud or hyperscaler through one Kubernetes API.
Run GPU workloads on any NeoCloud or hyperscaler or your own infrastructure through one Kubernetes API.

</div>

Expand All @@ -21,6 +22,12 @@ per-cloud glue. The API follows a Karpenter-style split:
- **NodeClaim** — one provisioned instance and its lifecycle, held by a finalizer
so a paid instance is never leaked.

## Overview

<div align="center">
<img src="site/arch.png" alt="Nebula architecture overview" width="900">
</div>

## How it works

1. **Opt in.** You label a Pod to request an accelerator.
Expand Down Expand Up @@ -97,6 +104,14 @@ placement controller owns those.
- See [docs/add-a-provider.md](docs/add-a-provider.md) to add a provider backend.
- See [docs/architecture.md](docs/architecture.md) for design details.

## Community

Join us on [Discord](https://discord.gg/7WTUuFqyS6) for questions, design
discussions, and help getting started. Contributions of every kind are welcome —
see [CONTRIBUTING.md](CONTRIBUTING.md).

## License

Apache-2.0 — see [LICENSE](LICENSE).
Apache-2.0 — see [LICENSE](LICENSE). Third-party components (e.g. Tailscale, used by
the optional SandD channel) are listed in
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
20 changes: 20 additions & 0 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Third-party notices

Nebula is licensed under Apache-2.0 (see [LICENSE](LICENSE)). It relies on the
following third-party software, which is licensed separately.

## Tailscale

The optional [SandD](https://github.com/InftyAI/SandD) access channel uses the
[Tailscale](https://github.com/tailscale/tailscale) client (`tailscale`/`tailscaled`),
© Tailscale Inc., licensed under
[BSD-3-Clause](https://github.com/tailscale/tailscale/blob/main/LICENSE).

Nebula does not link or vendor it; the provider integration fetches the official
binary onto the GPU host at provision time. If you build and redistribute an image
with Tailscale baked in, retain its copyright notice per BSD-3-Clause.

## Headscale
The optional SandD access channel deploys [headscale](https://github.com/juanfont/headscale)
(container image `headscale/headscale`). headscale is licensed separately; see its
upstream license at https://github.com/juanfont/headscale/blob/main/LICENSE.
Loading
Loading