diff --git a/DEVELOPER.md b/DEVELOPER.md index c7d6364..d40b05e 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -1,11 +1,12 @@ -# UVM Developer Guide +# Nest Developer Guide -This document is the developer handoff for the entire UVM codebase. Read it +This document is the developer handoff for Nest and its UVM backend. Read it before changing lifecycle behavior, host networking, Firecracker launch semantics, persistent state, or the HTTP API. -`uvm` is a local, privileged Firecracker microVM manager. It is an -application with a CLI and optional FastAPI management server. It is not a +Nest owns the workspace dashboard and its server entry point. Its `uvm` +component is a local, privileged Firecracker microVM manager with a CLI and +standalone FastAPI management API. These are application components, not a general-purpose Python library or a multi-host control plane. ## 1. Goals and Boundaries @@ -13,7 +14,7 @@ general-purpose Python library or a multi-host control plane. UVM installs host/guest assets, provisions private disks, and manages named VMs and private IPv4 TCP allowlists on one host. The CLI offers retained-disk start/stop/restart, CPU/RAM/storage resizing, deletion, fixed-target bulk operations, inspection, SSH, -logs, doctor, and explicit port edits/sync. The optional API and local dashboard +logs, doctor, and explicit port edits/sync. The UVM API and Nest dashboard expose health, installation, VM lifecycle controls, and managed port-policy controls, host diagnostics, bounded VM-log tails, and fixed-target bulk operations. Interactive SSH remains CLI-only. @@ -30,16 +31,18 @@ flag reference, see [USAGE.md](USAGE.md). ## 2. First Run -All UVM-specific files live in this directory. Start here: +All Nest and UVM files live in this project. Start here: ```sh python3 -m pip install -e '.[test]' python3 -B -m unittest discover -s tests -v +./nest.py --help ./uvm.py --help ``` -The `uvm` console script is registered by `pyproject.toml`. During source -development, `./uvm.py` and `python3 -B -m uvm` are equivalent entry points. +The `nest` and `uvm` console scripts are registered by `pyproject.toml`. +During source development, `./nest.py` and `python3 -B -m nest` are equivalent +Nest entry points; `./uvm.py` and `python3 -B -m uvm` launch UVM. Requirements for real host operations: @@ -63,7 +66,15 @@ Packet integration tests must remain inside disposable isolated namespaces. ├── README.md # Project overview ├── USAGE.md # Operator command/deployment guide ├── pyproject.toml # Package metadata and dependencies -├── uvm.py # Executable source-tree launcher +├── nest.py # Executable Nest source-tree launcher +├── nest/ +│ ├── __main__.py # `python -m nest` +│ ├── cli.py # Nest workspace serve command +│ ├── server.py # Nest composition of the dashboard and UVM API +│ └── dashboard/ +│ ├── router.py # Nest dashboard document route +│ └── static/ # Packaged HTML, CSS, JavaScript, and fonts +├── uvm.py # Executable UVM source-tree launcher ├── uvm/ │ ├── __main__.py # `python -m uvm` │ ├── cli.py # argparse, CLI rendering, serve dispatch @@ -83,11 +94,10 @@ Packet integration tests must remain inside disposable isolated namespaces. │ ├── lifecycle.py # Lifecycle, bulk operations, recovery, port intent │ ├── api_models.py # Pydantic HTTP request/response models │ ├── server.py # FastAPI factory and Uvicorn launcher -│ ├── dashboard/ # Packaged local dashboard HTML, CSS, and JS │ ├── routers/ -│ │ ├── dashboard.py # Dashboard document route │ │ ├── diagnostics.py # Host diagnostic endpoint │ │ ├── dependencies.py # API application/token dependencies +│ │ ├── exit.py # Authenticated server shutdown │ │ ├── health.py # Liveness endpoint │ │ ├── installation.py # Installation endpoint │ │ └── vms.py # VM lifecycle endpoints @@ -106,8 +116,10 @@ are intentionally small and operationally focused. ```mermaid flowchart LR - CLI[CLI: uvm] --> APP[app.py composition root] - HTTP[FastAPI server] --> APP + DASH[Nest dashboard] --> NEST[nest/server.py workspace] + NEST --> HTTP[UVM management API] + CLI[CLI: uvm] --> APP[uvm/app.py composition root] + HTTP --> APP APP --> INSTALL[Installer] APP --> LIFE[LifecycleService] APP --> STATE[StateStore] @@ -125,8 +137,9 @@ flowchart LR FC --> KVM[/dev/kvm] ``` -The public entry points are thin adapters. The lifecycle layer is the only -place that coordinates multiple host resources for a VM. Lower-level modules +The public entry points are thin adapters. Nest composes the UVM API and adds +the workspace dashboard; UVM does not import Nest. The lifecycle layer is the +only place that coordinates multiple host resources for a VM. Lower-level modules must not import `cli.py`, `server.py`, or `lifecycle.py`. Leaf modules are `config.py`, `domain.py`, `errors.py`, and `validation.py`. @@ -163,6 +176,11 @@ checks, not per-VM repair or guest application probes. ### HTTP Path +`nest/server.py` owns the workspace application. Its `create_app()` composes +`uvm.server.create_api()` with the dashboard document and static assets from +`nest/dashboard/`. The same-origin API paths and `X-UVM-Token` authentication +are shared with the standalone UVM API. + Routers validate the token and Pydantic request, invoke application services, and render whitelisted response models. Lifecycle, managed port-policy, diagnostics, and bounded log-tail operations have explicit HTTP routes for the @@ -171,9 +189,11 @@ SSH has no HTTP endpoint. The public dashboard shell does not bypass token validation: it stores a supplied token only for the active browser session and sends it with management requests. -Use only `uvm --serve` or `./uvm.py --serve` to launch the API. The -`create_api()` factory requires an explicit host specifically to prevent it -from being used as an insecure generic Uvicorn factory. +Launch the workspace with `nest --serve` or `./nest.py --serve`. The standalone +backend uses `uvm --serve` or `./uvm.py --serve`. Both `create_app()` and +`create_api()` require an explicit host so validation covers the intended bind. +Nest passes its factory to the shared UVM server runner, which enforces token +and TLS requirements and wires graceful server shutdown. ## 6. Application Composition @@ -837,8 +857,9 @@ Python's safe tar extraction filter. ### Server Startup See [USAGE.md](USAGE.md) for dependency installation, loopback deployment, -TLS deployment, and authenticated curl examples. Launch through `uvm --serve` -using the Python environment with the installed API dependencies. Every server +TLS deployment, and authenticated curl examples. Launch the Nest workspace +through `nest --serve`, or the standalone backend through `uvm --serve`, using +the Python environment with the installed API dependencies. Every server requires `UVM_API_TOKEN`, even at `127.0.0.1`. Non-loopback binds additionally require `UVM_API_TLS_CERT` and `UVM_API_TLS_KEY`; paired certificate/key files are loaded into an `ssl.SSLContext` before Uvicorn starts. @@ -851,10 +872,13 @@ or secret manager when that matters. ### API Contract +The root redirect and dashboard document are Nest routes. Both server entry +points expose the remaining management routes below. + | Method | Route | Router | Notes | |---|---|---|---| -| `GET` | `/` | `server.py` | Redirect to `/dashboard` | -| `GET` | `/dashboard` | `dashboard.py` | Public static dashboard shell | +| `GET` | `/` | `nest/server.py` | Redirect to `/dashboard` (Nest only) | +| `GET` | `/dashboard` | `nest/dashboard/router.py` | Public Nest dashboard shell (Nest only) | | `GET` | `/health` | `health.py` | Unauthenticated liveness response | | `POST` | `/exit` | `exit.py` | Request graceful Uvicorn shutdown | | `GET` | `/diagnostics` | `diagnostics.py` | Host readiness checks | @@ -878,14 +902,14 @@ All management routes use `get_authorized_application()` and require: X-UVM-Token: ``` -`POST /exit` is a management route. `run_server()` creates the Uvicorn server, -stores it on the FastAPI application state, and runs it in-process. The route -returns `202` before a background task sets `should_exit`, allowing Uvicorn to -deliver the response and perform its normal graceful shutdown. It does not -stop or delete any managed VM. +`POST /exit` is a management route. The shared `uvm.server.run_server()` creates +the Uvicorn server, stores it on the FastAPI application state, and runs it +in-process. The route returns `202` before a background task sets `should_exit`, +allowing Uvicorn to deliver the response and perform its normal graceful +shutdown for Nest or UVM. It does not stop or delete any managed VM. Request schemas reject unexpected fields. `UvmError` instances are rendered as -JSON errors by `server.py`; Pydantic request validation uses FastAPI's normal +JSON errors by `uvm/server.py`; Pydantic request validation uses FastAPI's normal 422 response format. Create accepts `name: str | null`, `ports: list | null`, and strict boolean `no_default_ports` (default false). Port objects contain a strict integer `port` in 1-65535 and uppercase `protocol` TCP/HTTP/HTTPS (TCP @@ -996,11 +1020,13 @@ Ordinary tests require no root, KVM, Firecracker, or host network mutations. They cover validation, schema migration/locking/permissions, artifact trust, private disks/credentials, network/firewall rendering and readback, conntrack semantics, pidfd/legacy identity, lifecycle recovery/resume/bulk targeting, -CLI parsing/output, and server security. Mock/fixture policy checks do not -prove the kernel's actual packet behavior. +CLI parsing/output, Nest dashboard delivery, and server security. Mock/fixture +policy checks do not prove the kernel's actual packet behavior. FastAPI route tests are conditionally enabled when both `fastapi` and `httpx` are installed. They should be enabled in CI using the `test` extra. +`test_nest_server.py` checks the workspace launcher, relocated frontend assets, +authenticated machine controls, API documentation, and graceful shutdown. When `nft` and `conntrack` are installed, command-parser tests append an invalid final command/option so parsing is checked without executing any mutation. These checks supplement fixture-based readback and cleanup tests; they do not @@ -1075,8 +1101,10 @@ on an isolated Linux host: `get_authorized_application()`, and register new routers in `create_api()`. Add API tests and update all route tables. New CLI commands do not implicitly authorize new HTTP endpoints; `/health` remains the only unauthenticated - operational API route, while `/` and `/dashboard` only serve the public - dashboard shell. + operational API route. Nest owns the public `/` and `/dashboard` routes. +- Dashboard: keep the document route and frontend assets in `nest/dashboard/`. + Compose workspace-level routes in `nest/server.py`; machine-management + operations continue through the UVM API and its application services. - Host primitives: use the specific network/firewall/install/process module and `CommandRunner` for external commands. Define ownership, idempotence, and compensation before invoking a primitive from lifecycle code. Use fake @@ -1137,7 +1165,7 @@ Before submitting a change: 1. Run the complete unit suite. 2. Run `git diff --check`. -3. Confirm a new feature stays inside the `uvm/` project directory. +3. Keep workspace/dashboard features in `nest/` and machine-management features in `uvm/`. 4. Verify command/API errors use `UvmError` rather than raw tracebacks. 5. Verify host mutations have a lock owner and cleanup path. 6. Verify persistent state remains readable across upgrades. diff --git a/README.md b/README.md index a0a8b92..6f83eca 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,17 @@ -# uvm +# Nest -`uvm` is a small, local Firecracker microVM command-line manager for Ubuntu. -It creates one Firecracker process per VM, attaches it to a host TAP device, +Nest is a local infrastructure workspace with a browser dashboard for your +machines, network, and host health. Launch it with `nest --serve`. + +Its `uvm` component is a small, local Firecracker microVM manager for Ubuntu. +UVM creates one Firecracker process per VM, attaches it to a host TAP device, and keeps a private disk and local registry for named VMs. The CLI supports create, start, stop, restart, resize, delete, inspection, SSH, logs, diagnostics, host-shared IPv4 internet access, and enforced private-IPv4 TCP port allowlists. -This directory is a self-contained project. All UVM code, tests, packaging -metadata, launcher scripts, and documentation live here. +This directory is a self-contained project. Nest owns the dashboard and +workspace server; UVM provides the machine-management CLI and backend services. +All code, tests, packaging metadata, launchers, and documentation live here. For architecture, lifecycle invariants, extension guidance, and contributor workflows, see [DEVELOPER.md](DEVELOPER.md). @@ -24,11 +28,13 @@ feature contract; these guides describe the implementation in the workspace. Run commands from this project root: ```sh +./nest.py --help +python3 -B -m nest --help ./uvm.py --help python3 -B -m uvm --help ``` -The `pyproject.toml` file also defines an installable `uvm` console command. +The `pyproject.toml` file defines installable `nest` and `uvm` console commands. The project is an application, not a reusable Python SDK. ## Requirements @@ -271,7 +277,7 @@ DNS publication, reverse proxies, or guest-service TLS. UDP and IPv6 service pol yet supported. Guest-initiated IPv4 TCP/UDP egress, its replies, and required ARP/ICMP traffic remain allowed; managed TAPs block IPv6. -## Management API +## Nest Dashboard and Management API Install the project dependencies before serving the API: @@ -279,28 +285,33 @@ Install the project dependencies before serving the API: python3 -m pip install . ``` -Launch the FastAPI management server using the environment where the project +Launch the Nest workspace server using the environment where the project dependencies were installed (use the full console-script path if needed): ```sh export UVM_API_TOKEN='replace-with-a-long-random-secret' sudo env UVM_API_TOKEN="$UVM_API_TOKEN" \ - uvm --serve --host 127.0.0.1 --port 8000 + nest --serve --host 127.0.0.1 --port 8000 # Equivalent when running the source checkout directly. sudo env UVM_API_TOKEN="$UVM_API_TOKEN" \ - ./uvm.py --serve --host 127.0.0.1 --port 8000 + ./nest.py --serve --host 127.0.0.1 --port 8000 ``` The default bind address is `127.0.0.1` and the default port is `8000`. OpenAPI documentation is available at `/docs` once the server is running. The -root URL redirects to the local dashboard at `/dashboard`; enter the API token +root URL redirects to the Nest dashboard at `/dashboard`; enter the API token there for the current browser session. -The dashboard includes a fleet overview, searchable machine collection, private -network and port-policy view, host diagnostics, and machine provisioning. Open a -machine to manage its lifecycle, edit or sync its port rules, copy its SSH command, -or read recent logs. Select machines in the collection for bulk operations. +Nest serves the dashboard and UVM-backed management endpoints together, using +the existing `UVM_API_TOKEN` and `UVM_*` host configuration. The standalone UVM +API is available through `uvm --serve`, with its reference at `/docs`; the `/` +and `/dashboard` routes belong to the Nest server. + +The Nest dashboard includes a fleet overview, searchable machine collection, +private network and port-policy view, host diagnostics, and machine provisioning. +Open a machine to manage its lifecycle, edit or sync its port rules, copy its SSH +command, or read recent logs. Select machines in the collection for bulk operations. Use `Ctrl+K` / `⌘K` (or `/`) to find a machine or page. Fleet data refreshes every five seconds while the tab is visible; host checks refresh every thirty seconds and can also be run manually. Charts show observed machine states and allocated @@ -311,10 +322,10 @@ The server exposes these routes: | Method | Route | Purpose | |---|---|---| -| `GET` | `/` | Redirect to the local dashboard | -| `GET` | `/dashboard` | Serve the local management dashboard | +| `GET` | `/` | Redirect to the dashboard (Nest server) | +| `GET` | `/dashboard` | Serve the Nest workspace dashboard (Nest server) | | `GET` | `/health` | Unauthenticated liveness response | -| `POST` | `/exit` | Gracefully stop the UVM API server | +| `POST` | `/exit` | Gracefully stop the management server | | `GET` | `/diagnostics` | Run host readiness checks for the dashboard/system view | | `POST` | `/install` | Install or refresh host assets; body: `{ "force": false }` | | `GET` | `/vms` | List VM records and observed status | @@ -365,7 +376,7 @@ sudo env \ UVM_API_TOKEN="$UVM_API_TOKEN" \ UVM_API_TLS_CERT="$UVM_API_TLS_CERT" \ UVM_API_TLS_KEY="$UVM_API_TLS_KEY" \ - ./uvm.py --serve --host 0.0.0.0 --port 8000 + ./nest.py --serve --host 0.0.0.0 --port 8000 ``` Send the token on management requests: @@ -385,8 +396,8 @@ curl -X POST \ ``` The endpoint returns `202 {"status":"stopping"}` and then gracefully terminates -the UVM API process. It is a protected management endpoint; it does not stop or -delete managed VMs. +the serving Nest or UVM process. It is a protected management endpoint; it does +not stop or delete managed VMs. Do not expose this server directly to an untrusted network. For remote access, keep TLS enabled and consider placing it behind an additional authenticated @@ -570,7 +581,7 @@ Host and API settings can be overridden through environment variables: | `UVM_ROOTFS_SHA256` | Trusted prebuilt rootfs SHA-256; use with `UVM_ROOTFS_URL` | | `UVM_FIRECRACKER_BINARY_SHA256` | Optional trusted local Firecracker binary SHA-256 | | `UVM_ALLOW_UNVERIFIED_DOWNLOADS` | Set to `1` only for unverified local development | -| `UVM_API_TOKEN` | Required for every API server; sent as `X-UVM-Token` | +| `UVM_API_TOKEN` | Required for Nest and standalone UVM servers; sent as `X-UVM-Token` | | `UVM_API_TLS_CERT` | TLS certificate path required for a non-loopback API bind | | `UVM_API_TLS_KEY` | TLS private-key path required for a non-loopback API bind | @@ -584,14 +595,18 @@ Selected files: ├── USAGE.md ├── DEVELOPER.md ├── pyproject.toml -├── uvm.py # Executable compatibility launcher -├── uvm/ # Private application implementation +├── nest.py # Executable Nest workspace launcher +├── nest/ # Nest workspace application +│ ├── cli.py # nest --serve entry point +│ ├── server.py # Workspace server, composing the UVM management API +│ └── dashboard/ # Nest document route and local HTML, CSS, JS, and fonts +├── uvm.py # Executable UVM compatibility launcher +├── uvm/ # Machine-management implementation │ ├── cli.py # argparse, terminal output, SSH execution │ ├── lifecycle.py # Lifecycle, bulk operations, recovery, port-policy intent │ ├── network.py # IP/MAC/TAP/bridge/NAT management │ ├── firewall.py # Owned nftables bridge policies and offline conntrack cleanup -│ ├── dashboard/ # Local dashboard HTML, CSS, and JavaScript -│ ├── routers/ # FastAPI routes, including the dashboard document +│ ├── routers/ # UVM management API routes │ └── firecracker/ # Firecracker config, API, and process adapters └── tests/ # Unit tests and opt-in isolated namespace packet tests ``` diff --git a/USAGE.md b/USAGE.md index 27a7508..507c4f6 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1,8 +1,8 @@ -# UVM Usage Guide +# Nest and UVM Usage Guide -This guide explains how to install, operate, and troubleshoot UVM as an -operator. It covers the command-line tool, the optional FastAPI server, common -scenarios, and expected limitations. +This guide explains how to install, operate, and troubleshoot Nest and its UVM +machine-management backend. It covers the Nest dashboard, UVM command-line +tool, FastAPI server, common scenarios, and expected limitations. For the project architecture and contributor guidance, see [DEVELOPER.md](DEVELOPER.md). For a concise overview, see [README.md](README.md). @@ -68,20 +68,23 @@ lscpu | grep -i virtualization From this project root: ```sh +./nest.py --help ./uvm.py --help python3 -B -m uvm --help ``` -To install the optional HTTP API dependencies and the console command: +To install the HTTP server dependencies and console commands: ```sh python3 -m pip install . +nest --help uvm --help ``` The rest of this guide uses `./uvm.py` so it works from a source checkout. If -you installed the package, replace `./uvm.py` with `uvm`. For the API, use the -Python environment containing the installed dependencies; under `sudo`, use +you installed the package, replace `./uvm.py` with `uvm` and `./nest.py` with +`nest`. For the server, use the Python environment containing the installed +dependencies; under `sudo`, use the full path to that environment's console script when necessary. ### Privilege Rules @@ -107,6 +110,7 @@ Examples elsewhere are separate scenarios; substitute your VM identifiers and do not run every destructive example as a single script. ```text +nest --serve [--host HOST] [--port PORT] uvm install [--force] uvm create [--name NAME] [--cpu CPU] [--ram RAM] [--storage STORAGE] [--guest-ip GUEST_IP] [--username USER] [--password PASSWORD | --password-stdin] @@ -144,7 +148,8 @@ not disable confirmations, and errors may still be plain text on stderr. ### Stop the Management API The API can be stopped remotely through its authenticated shutdown endpoint. -This stops only the UVM API process; it does not stop or delete any VM: +This stops the serving Nest or standalone UVM process; it does not stop or +delete any VM: ```sh curl -X POST \ @@ -766,31 +771,38 @@ UVM will not blindly signal that PID or delete a possibly in-use disk. Listing does not perform adoption. Inspect such failures rather than manually inventing identity fields in `state.json`. -## 10. Use the FastAPI Server +## 10. Use the Nest Workspace and FastAPI Server The API uses the same local lifecycle services and exposes health, installation, VM lifecycle, diagnostics, bounded VM-log tails, and managed private-port -controls. The local dashboard at `/dashboard` uses those same endpoints. There -API also supports fixed-target bulk actions; interactive SSH remains CLI-only. +controls. Nest owns the local dashboard at `/dashboard` and serves it alongside +these UVM-backed endpoints. The API also supports fixed-target bulk actions; +interactive SSH remains CLI-only. The server does not autostart VMs or reconcile rules, and is not safe to expose directly to the public internet. ### Start a Local Server -After installing the project dependencies, start the server as root: +After installing the project dependencies, start the Nest workspace as root: ```sh export UVM_API_TOKEN='replace-with-a-long-random-visible-ascii-token' sudo env \ UVM_API_TOKEN="$UVM_API_TOKEN" \ - ./uvm.py --serve --host 127.0.0.1 --port 8000 + ./nest.py --serve --host 127.0.0.1 --port 8000 ``` Liveness is at `http://127.0.0.1:8000/health`; the dashboard is at `http://127.0.0.1:8000/dashboard`; OpenAPI docs are at `http://127.0.0.1:8000/docs`. The default bind is `127.0.0.1:8000`. +The installed command is `nest --serve`; `python3 -B -m nest --serve` also +works from the source checkout. Nest uses `UVM_API_TOKEN` and the `UVM_*` host +settings for its machine-management backend. To launch the standalone UVM API, +use `./uvm.py --serve` with the same host/port options and configuration. That +server exposes the management endpoints and `/docs`; Nest serves the dashboard. + The API token is required even for a loopback server. All VM, diagnostics, and installation routes require it. The dashboard shell, liveness route, and FastAPI's informational `/docs`, `/redoc`, and `/openapi.json` endpoints are available @@ -814,7 +826,7 @@ sudo env \ UVM_API_TOKEN="$UVM_API_TOKEN" \ UVM_API_TLS_CERT="$UVM_API_TLS_CERT" \ UVM_API_TLS_KEY="$UVM_API_TLS_KEY" \ - ./uvm.py --serve --host 0.0.0.0 --port 8443 + ./nest.py --serve --host 0.0.0.0 --port 8443 ``` For real remote use, use a certificate trusted by clients and restrict network @@ -845,8 +857,8 @@ curl --cacert /path/to/ca.pem \ | Method | Path | Operation | |---|---|---| -| `GET` | `/` | Redirect to `/dashboard` | -| `GET` | `/dashboard` | Serve the local management dashboard | +| `GET` | `/` | Redirect to `/dashboard` (Nest server) | +| `GET` | `/dashboard` | Serve the Nest workspace dashboard (Nest server) | | `GET` | `/health` | Unauthenticated liveness | | `GET` | `/diagnostics` | Run host readiness checks | | `POST` | `/install` | Install/refresh with a `force` boolean | @@ -1318,7 +1330,7 @@ matching checksum with the required drivers instead. For an explicitly unverified development installation, retain the existing `UVM_ALLOW_UNVERIFIED_DOWNLOADS=1` setting as described in the installation section. -Restart a running UVM API server to load the updated code before using dashboard +Restart a running Nest server to load the updated code before using dashboard start/restart actions. Look for `Started OpenBSD Secure Shell server` in the new boot log and confirm an actual SSH connection. `start` on an already-running VM only refreshes networking; use a stopped VM or `restart` to attach the device. diff --git a/nest.py b/nest.py new file mode 100755 index 0000000..de235bb --- /dev/null +++ b/nest.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +"""Source-tree launcher for the Nest workspace.""" + +from nest.cli import main + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/nest/__init__.py b/nest/__init__.py new file mode 100644 index 0000000..8bcf631 --- /dev/null +++ b/nest/__init__.py @@ -0,0 +1 @@ +"""Nest workspace application.""" diff --git a/nest/__main__.py b/nest/__main__.py new file mode 100644 index 0000000..1e7cb3b --- /dev/null +++ b/nest/__main__.py @@ -0,0 +1,7 @@ +"""Run the Nest workspace with ``python -m nest``.""" + +from .cli import main + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/nest/cli.py b/nest/cli.py new file mode 100644 index 0000000..535f7be --- /dev/null +++ b/nest/cli.py @@ -0,0 +1,44 @@ +"""Command-line entry point for the Nest workspace server.""" + +from __future__ import annotations + +import argparse +import sys +from collections.abc import Sequence + +from uvm.app import build_application +from uvm.errors import UvmError + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + prog="nest", + description="Nest dashboard and local machine management workspace.", + allow_abbrev=False, + ) + parser.add_argument("--serve", action="store_true", help="launch the Nest workspace server") + parser.add_argument("--host", default="127.0.0.1", help="bind address (default: 127.0.0.1)") + parser.add_argument("--port", type=int, default=8000, help="bind port (default: 8000)") + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + parser = build_parser() + args = parser.parse_args(argv) + if not args.serve: + parser.error("--serve is required to launch the Nest workspace") + if not 1 <= args.port <= 65535: + parser.error("--port must be between 1 and 65535") + + try: + from .server import run_server + + application = build_application(emit=lambda message: print(message, file=sys.stderr)) + run_server(application, host=args.host, port=args.port) + return 0 + except UvmError as error: + print(f"nest: error: {error}", file=sys.stderr) + return 1 + except KeyboardInterrupt: + print("nest: interrupted", file=sys.stderr) + return 130 diff --git a/nest/dashboard/__init__.py b/nest/dashboard/__init__.py new file mode 100644 index 0000000..abeb727 --- /dev/null +++ b/nest/dashboard/__init__.py @@ -0,0 +1 @@ +"""Packaged frontend assets for the Nest workspace.""" diff --git a/uvm/routers/dashboard.py b/nest/dashboard/router.py similarity index 70% rename from uvm/routers/dashboard.py rename to nest/dashboard/router.py index f512852..dfb5592 100644 --- a/uvm/routers/dashboard.py +++ b/nest/dashboard/router.py @@ -1,4 +1,4 @@ -"""Dashboard frontend routes.""" +"""Nest dashboard document route.""" from __future__ import annotations @@ -9,7 +9,7 @@ from fastapi.responses import HTMLResponse router = APIRouter() -_DASHBOARD_HTML = Path(__file__).parent.parent / "dashboard" / "static" / "index.html" +_DASHBOARD_HTML = Path(__file__).parent / "static" / "index.html" @router.get("", response_class=HTMLResponse) diff --git a/uvm/dashboard/static/css/dashboard.css b/nest/dashboard/static/css/dashboard.css similarity index 100% rename from uvm/dashboard/static/css/dashboard.css rename to nest/dashboard/static/css/dashboard.css diff --git a/uvm/dashboard/static/fonts/OFL.txt b/nest/dashboard/static/fonts/OFL.txt similarity index 100% rename from uvm/dashboard/static/fonts/OFL.txt rename to nest/dashboard/static/fonts/OFL.txt diff --git a/uvm/dashboard/static/fonts/manrope-latin.woff2 b/nest/dashboard/static/fonts/manrope-latin.woff2 similarity index 100% rename from uvm/dashboard/static/fonts/manrope-latin.woff2 rename to nest/dashboard/static/fonts/manrope-latin.woff2 diff --git a/uvm/dashboard/static/index.html b/nest/dashboard/static/index.html similarity index 92% rename from uvm/dashboard/static/index.html rename to nest/dashboard/static/index.html index 9c093ef..a635bf0 100644 --- a/uvm/dashboard/static/index.html +++ b/nest/dashboard/static/index.html @@ -5,11 +5,12 @@ - UVM Control - + Nest + + - - + +
- uvm.workspace -
A little space for big ideas

Your compute.
A little calmer.

A considered workspace for your machines,
your network, and everything in between.

+ nest.workspace +
A little space for big ideas

Your workspace.
Right at home.

A home for your machines,
your network, and your next idea.

- +
Your local workspace -

Welcome to UVM

+

Welcome to Nest

Bring your machines into view.
Connect with your management token to begin.

@@ -80,8 +81,8 @@