|
|
||
|---|---|---|
| .github/workflows | ||
| .kube/cache | ||
| deploy | ||
| docs | ||
| scripts | ||
| server | ||
| shared | ||
| web | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
physx-lab
physx-lab is a self-hosted multi-tenant hosting panel built for Kubernetes. It gives an administrator a single control surface for creating client accounts, provisioning isolated namespaces, and managing application services from a web interface. Each client account owns a namespace and a resource envelope; each service inside that namespace is an independently managed deployment with its own storage, domains, ports, lifecycle, and operational controls.
This project focuses on operational simplicity: the panel is the only public entry point, Kubernetes is the execution substrate, and the end user interacts with a service-oriented abstraction rather than raw cluster objects.
Product model
The product is organized around two layers of ownership:
- Account: a client tenant with identity, authentication, namespace, and quota envelope.
- Service: a deployed application instance inside the tenant namespace, with its own image, storage, domains, public ports, and runtime settings.
This distinction matters because service changes do not require account-level churn. A tenant may run several independent applications side by side, and each service can be created, updated, restarted, or deleted without modifying the parent account.
Architecture overview
The system is composed of a small number of clear layers:
-
Browser layer
- Users access a React single-page application served by the panel.
- The UI is split between admin views and client views.
- Client-facing actions are scoped to the selected account and selected service.
-
Panel layer
- A Fastify server exposes the JSON API and the WebSocket endpoints.
- It handles authentication, authorization, persistence, provisioning jobs, file operations, shell access, and Kubernetes orchestration.
- It owns the single public origin for the product and acts as the only gateway between users and the Kubernetes API.
-
Data layer
- SQLite stores users, client accounts, services, configuration, and audit logs.
- The database is local to the panel deployment and is used for operational state and historical tracking.
-
Kubernetes layer
- The panel interacts with the Kubernetes API to create namespaces, quotas, services, deployments, ingresses, and PVCs.
- Each client namespace is isolated with its own quota, limit ranges, network policy, and service account configuration.
- Application workloads run as standard Kubernetes objects, while the panel coordinates the lifecycle at a higher abstraction level.
-
Edge layer
- Traefik terminates public traffic for the platform.
- It routes panel traffic and public service hostnames through managed TLS, wildcard certificates, and ingress rules.
Core runtime components
Web application
The frontend is built with Vite and React. It renders admin and user experiences for the following flows:
- administrator dashboard and client management
- account-level service listing
- service dashboard for runtime diagnosis
- terminal access inside the running pod
- logs streaming
- file browsing and uploads
- service configuration and limits
- application catalog installation
The frontend is designed around a client/account context and a selected service context. Most operations are done by calling the panel API rather than interacting with Kubernetes directly.
Fastify API server
The server is the orchestration engine. It is responsible for:
- user authentication and authorization
- JWT validation and session checks
- account and service CRUD operations
- Kubernetes manifest generation
- server-side apply operations
- job orchestration and reconciliation
- terminal exec, log streaming, and file transfer
- audit logging and operational status tracking
The API enforces tenancy boundaries. A client may only access the resources of their own account, and the admin role remains the only path to cluster-level operations such as catalog-based app installation and resource envelopes.
SQLite persistence
The database is intentionally lightweight but operationally central. It stores:
- users and roles
- client accounts
- service definitions and runtime metadata
- generated environment variable values
- audit records for administrative actions
- tokens and security-related bookkeeping
The panel uses transactional workflows for operations that must stay consistent, particularly changes that affect quotas, service allocations, and public ports.
Kubernetes orchestration
The panel creates and reconciles infrastructure in a structured way:
- namespace for each account
- resource quota and resource limits
- network policy for isolation
- service accounts and middleware configuration
- deployment and PVC for each service
- ingress and service objects to expose the service internally and publicly
- optional public TCP/UDP service objects for game servers and similar workloads
The project uses idempotent server-side apply patterns so that re-provisioning the same account or service re-runs the same sequence without requiring manual cleanup.
Tenant isolation model
Each client is isolated by namespace, not by a single monolithic deployment. In other words:
- the namespace is the protection boundary for the account
- all service workloads inside that namespace share the same account-level policy
- services are independent units with separate PVCs, env configuration, and public endpoints
- different clients never share the same namespace or the same service-level identity space
This model is more robust than exposing raw Kubernetes objects to end users because it keeps the platform understandable, enforceable, and easier to manage at scale.
Main execution flows
Authentication and access control
The login flow validates credentials, issues a signed JWT, and associates the token with the user and account context. Every protected API endpoint re-checks the stored user state to ensure the identity and token version remain valid.
The system also creates short-lived WebSocket tickets for privileged operations such as terminal access or log streaming. Those tickets are validated against the user and their account before the bridge to Kubernetes is opened.
Account provisioning
When an administrator creates a client account, the panel does the following:
- validates the account and service configuration
- creates or reuses the client namespace metadata
- inserts the account and user records into SQLite
- enqueues a provisioning job
- applies the account-scoped Kubernetes objects
- creates the first service and its associated deployment objects
- marks the account as active when the work succeeds
This is a coordinated job flow, not a best-effort patch. The namespace objects are recalculated from the current set of services so that quotas and policies stay consistent.
Service lifecycle
Each service is a complete application unit with its own:
- image and start command
- volume-backed filesystem
- public hostname or dedicated TCP/UDP endpoint
- resource limits
- environment variables and secrets
- ingress and service configuration
The service can be started, stopped, restarted, edited, or deleted without invalidating the parent client account. Re-provisioning can be replayed safely because the manifests are generated deterministically.
Terminal and logs
The panel provides terminal access by opening an exec session into the target pod. The server bridges browser input/output to the Kubernetes streaming API and preserves terminal semantics through a proper TTY session.
The log path follows the same pattern: the server connects to the pod logs stream, decodes chunked output, and forwards line-oriented events to the browser in real time. This keeps the user experience aligned with container runtime behavior without exposing direct Kubernetes access.
File management
File operations are executed inside the target pod via shell commands and the Kubernetes exec API. The system supports:
- listing directories
- uploading files and folders
- downloading files or folder archives
- deleting and renaming entries
- changing permissions
- extracting archives inside the service data volume
The file-transfer pipeline is designed to avoid buffering full uploads in the panel process. It streams the payload directly into the runtime container, which keeps large file transfer operations manageable and avoids unnecessary memory pressure.
Catalog application installation
The project includes a catalog of application templates. An admin chooses a template, fills required values, and the panel generates the appropriate deployment configuration, environment variables, secret values, and port mappings.
The catalog is not stored as runtime state in a separate source of truth. The runtime service retains the template identity and version, while the server reads the canonical template metadata from the shared API definitions. This keeps the template catalog stable, versioned, and consistent between the backend and frontend.
Security and operational boundaries
Security is enforced both at the platform boundary and inside the Kubernetes layer:
- public traffic is routed through Traefik and TLS-managed ingress
- only the panel is allowed to talk directly to the Kubernetes API
- client users never receive unrestricted kubeconfig access
- namespace-level policies and quotas constrain resource use
- admission policies protect namespace scoping and tenant boundaries
- service workloads run under constrained permissions and limited network exposure
This design minimizes the blast radius of a compromised tenant account while preserving a usable self-service hosting model.
Deployment model
The project ships as a Kubernetes deployment with the following operational pattern:
- panel itself runs in its own namespace
- panel database and runtime data live in persistent storage
- the panel service account is granted controlled access to the cluster
- a wildcard certificate supports public app hostnames
- Traefik handles inbound TLS and domain routing
- client workloads live in separate namespaces and are provisioned from the panel
The deployment manifests are organized under the deploy directory and are intended to be managed with Kustomize. The panel can also run in a local docker-compose configuration for development or validation.
Repository layout
- server: Fastify API, Kubernetes orchestration, job management, auth, and service lifecycle logic
- web: React frontend and user interface
- shared: shared DTOs, catalog definitions, and protocol contracts
- deploy: Docker, Kubernetes manifests, and deployment configuration
- docs: architecture notes, migration guidance, and implementation specifications
Development notes
For a local development workflow, the project runs the API and the frontend together, with the frontend proxying API calls to the backend. The service is designed to make local iteration straightforward while keeping the same resource model used in cluster deployment.
The main design goal is to provide a user-friendly hosting panel without exposing raw Kubernetes management to the end user. Instead, the UI works with higher-level account and service abstractions, while the backend translates those abstractions into the exact Kubernetes resources required to run each workload.
Summary
physx-lab is a Kubernetes-native hosting control plane. It adds a tenant model, service abstraction, and user-friendly operations layer on top of Kubernetes so that administrators can manage client accounts and services without requiring direct cluster access. The software is shaped around isolation, policy enforcement, repeatable provisioning, and a clear separation between account ownership and service deployment.