Launch from a template
An admin curates templates and an image catalog; everyone else picks one, sets CPU/memory/GPU, and launches. Credentials like a Jupyter token are generated per deployment and shown only to the owner.
v0.3.0 Apache-2.0 amd64 & arm64
Aether turns a single Kubernetes namespace into a self-service platform. Your team launches JupyterLab, RStudio, or an LLM inference engine from a form — and gets a working URL, already logged in. No kubectl, no YAML, no cluster credentials handed out.
Self-hosted. Runs entirely inside your cluster. Nothing calls home.
A shared GPU cluster is only useful if people can actually get onto it. The usual answers are a shared SSH box that drifts, a pile of hand-edited manifests, or handing out kubeconfigs and hoping. Aether is the small piece in between: a login, a form, and a URL — with the cluster's own primitives doing the work underneath.
It is deliberately single-namespace and asks for a
Role, never a ClusterRole. It manages what you point it
at and nothing else.
An admin curates templates and an image catalog; everyone else picks one, sets CPU/memory/GPU, and launches. Credentials like a Jupyter token are generated per deployment and shown only to the owner.
Aether reverse-proxies into the pod and injects the generated credential, so Open lands you in a live session with no token to copy. WebSocket upgrades are tunnelled, so Jupyter kernels work.
CPU, memory and GPU limits with a global default and per-user overrides. Usage is summed from Deployment specs — desired state, correct the instant it's written — rather than from observed pods, which lag and double-count mid-rollout.
GPUs are detected from the device-plugin resource name, so NVIDIA, AMD, Intel, Habana, AWS Neuron and TPU all count toward quota without a per-vendor code path.
Scale, edit resources and env, restart, roll back to the previous ReplicaSet, or delete — all scoped by ownership. Regenerate a leaked credential without redeploying.
Pin a user's launches to a node label, and assign a per-user UID/GID so different people own their own files on shared NFS instead of colliding on one identity.
Every login and launch is recorded — who ran what, with which resources. Generated secrets are redacted in the log rather than stored twice.
Mint a long-lived bearer token to script against the API without a browser session. Only a SHA-256 hash is ever stored.
If you proxy someone's notebook at
https://aether.example.com/proxy/their-app/, that notebook is
same-origin with your dashboard. Any JavaScript running in
it — a pasted cell, a malicious dependency — can call
/api/* as whoever is browsing it. HttpOnly doesn't
help: the browser attaches the cookie to same-origin fetch
automatically.
Aether serves every deployment from its own origin,
<name>.proxy.example.com. The browser then treats it as a
different site, and Aether's host-only session cookie never travels there.
Access is granted by an OAuth-shaped handshake that mints a single-use token
on the app origin and exchanges it for a per-deployment cookie.
Role, never a ClusterRoleSecure, HttpOnly, SameSite=LaxSet-Cookie from a pod can't overwrite your session
Traefik terminates TLS and routes on Host. Aether talks to the
Kubernetes API with a namespace-scoped Role, keeps its catalog in Postgres, and
reverse-proxies each launched workload from its own origin.
A published Helm chart and a public multi-arch image. No registry credentials needed.
Bundled single-replica Postgres, TLS off. For evaluation only — no backups.
helm install aether \
oci://ghcr.io/techboredom/charts/aether \
--version 0.3.0 \
--namespace aether --create-namespace \
--set host=aether.example.com \
--set database.deploy.enabled=true \
--set ingress.tls.enabled=false \
--set adminBootstrap.password=change-me
Your own Postgres, and cert-manager issuing the certificate.
helm install aether \
oci://ghcr.io/techboredom/charts/aether \
--version 0.3.0 \
--namespace aether --create-namespace \
--set host=aether.example.com \
--set ingress.className=traefik \
--set database.existingSecret=aether-db-app \
--set ingress.tls.issuerRef.name=letsencrypt
*.proxy.<your host>, plus a matching certificate, to give each deployment its own originThe chart refuses to render a broken or quietly-insecure install rather than producing something that half-works — an unset host, a missing issuer, or same-origin proxying without an explicit acknowledgement all fail fast with the reason.
A Rust workspace: Axum and kube-rs on the server, Leptos compiled to WebAssembly in the browser, and sqlx against Postgres. One static binary on a distroless base, running as non-root with a read-only root filesystem and all capabilities dropped.