TifEra is a browser-based, terminal-first operations console for Kubernetes - "MobaXterm for K8s". It runs as a single pod inside the cluster and drives everything through the Kubernetes API with the pod's own ServiceAccount: no agents, no kubeconfig, no client CLI. Built with FastAPI + dependency-free vanilla JS and vendored xterm.js.
Two invariants define it:
- 🔒 In-cluster only - all cluster credentials come from the pod environment; run the image anywhere else and it exits in 5s (no bypass, CI-enforced).
- 👤 Login + roles - first run bootstraps an admin; users are Admin / Operator / Viewer, enforced server-side. A Viewer sees only non-sensitive data (no shells, files, kubectl, logs, Secrets or writes).
Important
TifEra terminates no TLS - keep the Service ClusterIP (kubectl port-forward) or front it with a TLS proxy. See SECURITY.md.
| ⌨ | Shell | One-click PTY (xterm.js) into any container; tabs + split panes, reconnect-with-replay, debug container for distroless |
| 📂 | Files | Browse, drag-drop upload, download (dirs as tar.gz), edit/rename/chmod - all over exec, no agent |
| 📜 | Logs | Live follow with filter & highlighting, previous-instance, merged multi-container |
| 📊 | Metrics | CPU/mem sparklines vs requests/limits, node usage (via metrics-server; degrades without) |
| 🕸 | Topology | Services → Pods graph, drag-to-pan, unhealthy paths highlighted |
| 👥 | Multi-operator | Presence badges, shared sessions (join a colleague's live shell), edit-conflict warnings |
| 🔍 | Command palette | Ctrl+K to find pods/containers/resources or jump to any view |
| ⎈ | kubectl console | Rancher-style in-cluster kubectl shell (bounded by TifEra's RBAC) |
| 🔐 | Auth | First-run admin in a k8s Secret, scrypt passwords, HMAC sessions, roles enforced server-side |
| 🧾 | Accountability | Action log (JSONL export) + optional session recording with in-browser playback |
| 🛠 | Tools | Pod restart, bulk actions, YAML edit & apply (Secrets masked), events, snippets, broadcast input |
| ⚙️ | Settings | Theme, font size, workspace persistence, resizable sidebar |
From a published release (multi-arch image on GHCR):
kubectl apply -f https://github.com/stratza/tiferea/releases/latest/download/tifera-0.2.0.yaml
# …or Helm: helm install tifera oci://ghcr.io/stratza/charts/tifera --version 0.2.0 -n tifera --create-namespace
kubectl -n tifera port-forward svc/tifera 8080:80 # → http://localhost:8080From source (build into a local cluster):
docker build -t tifera:0.2.0 backend
kind load docker-image tifera:0.2.0 # or: k3d image import tifera:0.2.0
kubectl apply -f deploy/tifera.yaml
kubectl -n tifera port-forward svc/tifera 8080:80On first visit you create the admin account; after that, sign in or continue as a read-only Viewer. Needs a default StorageClass (RWO PVC); metrics-server is optional.
⚙️ Configuration (env vars)
| Var | Default | Purpose |
|---|---|---|
TIFERA_LISTEN_PORT |
8080 |
listen port |
TIFERA_DATA_DIR |
/data |
PVC mount for SQLite + recordings |
TIFERA_IDLE_TIMEOUT |
1800 |
terminal idle timeout, seconds |
TIFERA_RECONNECT_GRACE |
10 |
seconds a dropped session waits for reattach |
TIFERA_MAX_UPLOAD |
2147483648 |
max upload size in bytes |
TIFERA_DEBUG_IMAGE |
busybox:1.36 |
ephemeral debug container image |
TIFERA_RECORD_SESSIONS |
off | 1 = record sessions as playable .cast files |
TIFERA_METRICS_INTERVAL |
15 |
metrics poll seconds |
TIFERA_AUTH_SECRET |
tifera-auth |
k8s Secret holding users + session key |
TIFERA_SESSION_TTL |
43200 |
login session lifetime, seconds |
Helm exposes these via values.yaml (config.*, persistence.*, rbac.*, networkPolicy.*).
🛰️ Air-gapped install
At runtime TifEra pulls nothing (kubectl + xterm.js are baked in); only the build and image distribution need internet.
- Build on a connected machine:
docker build -t tifera:0.2.0 backend(add--build-arg KUBECTL_VERSION=v1.31.4to pin kubectl;docker buildx --platform ...for other arches). - Move it in - push to an internal registry, or
docker save … |copy| sudo k3s ctr images import …(orkind load image-archive). - Mirror the one runtime image - the debug container (default
busybox:1.36); mirror it and setTIFERA_DEBUG_IMAGE/ Helmconfig.debugImage, or skip it. - Deploy with the image rewritten:
sed 's#image: tifera:0.2.0#image: registry.internal/tifera:0.2.0#' deploy/tifera.yaml | kubectl apply -f -, or Helm--set image.repository=…,image.tag=0.2.0.
📁 Repository layout
backend/tifera/ Python 3.12 backend (FastAPI + official kubernetes client)
auth · terminal · kubeshell · fsops · inventory · metrics ·
topology · resources · recordings · logs · presence ·
actionlog · snippets · debug · incluster · app
backend/static/ frontend: dependency-free ES modules + vendored xterm.js
backend/tests/ unit tests incl. the outside-cluster refusal test
deploy/ single-file manifest + Helm chart
pip install -r backend/requirements.txt pytest
PYTHONPATH=backend pytest backend/tests -qThere's deliberately no way to run against a remote cluster from a workstation - develop against a local cluster (kind/k3d/k3s) by building and loading the image. See CONTRIBUTING.md (incl. building in-cluster with Kaniko when you have no local Docker).
Login required with server-enforced Admin/Operator/Viewer roles; TifEra never exceeds its own least-privilege ClusterRole; hardened pod (non-root, read-only rootfs, all caps dropped). Full model, reporting and hardening in SECURITY.md.
Contributions welcome - see CONTRIBUTING.md. MIT licensed; see LICENSE.