Spaces:
Running on Zero
Running on Zero
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: MIT | |
| # | |
| # Permission is hereby granted, free of charge, to any person obtaining a | |
| # copy of this software and associated documentation files (the "Software"), | |
| # to deal in the Software without restriction, including without limitation | |
| # the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| # and/or sell copies of the Software, and to permit persons to whom the | |
| # Software is furnished to do so, subject to the following conditions: | |
| # | |
| # The above copyright notice and this permission notice shall be included in | |
| # all copies or substantial portions of the Software. | |
| # | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| # DEALINGS IN THE SOFTWARE. | |
| import socket | |
| import subprocess | |
| import platform | |
| from shutil import which | |
| import urllib.request | |
| from pathlib import Path | |
| from .logging import setup_logger | |
| logger = setup_logger(__name__) | |
| def get_lan_ip(): | |
| """ | |
| get_lan_ip() | |
| Get the LAN IP address of the current machine. | |
| Returns | |
| ------- | |
| str | |
| LAN IP address as a string. | |
| """ | |
| s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
| try: | |
| s.connect(("8.8.8.8", 80)) | |
| return s.getsockname()[0] | |
| finally: | |
| s.close() | |
| def ensure_mkcert(): | |
| """ | |
| Ensure mkcert exists; if not, download the correct binary. | |
| Returns | |
| ------- | |
| str | None | |
| Path to mkcert binary, or None if installation failed. | |
| """ | |
| # mkcert already installed? | |
| mkcert_bin_path = which("mkcert") | |
| if mkcert_bin_path: | |
| return mkcert_bin_path | |
| logger.info("[auto-cert] mkcert not found, attempting to download...") | |
| # Determine OS + arch | |
| system = platform.system().lower() # "linux", "darwin", "windows" | |
| machine = platform.machine().lower() # "x86_64", "amd64", "arm64", etc. | |
| # Normalize architecture | |
| if machine in ("x86_64", "amd64"): | |
| arch = "amd64" | |
| elif machine in ("aarch64", "arm64"): | |
| arch = "arm64" | |
| elif machine.startswith("arm"): | |
| arch = "arm" | |
| else: | |
| logger.error(f"[auto-cert] Unsupported architecture: {machine}") | |
| return None | |
| # Determine filename and URL | |
| if system == "linux": | |
| filename = f"mkcert-v1.4.4-linux-{arch}" | |
| elif system == "darwin": | |
| filename = f"mkcert-v1.4.4-darwin-{arch}" | |
| elif system == "windows": | |
| filename = f"mkcert-v1.4.4-windows-{arch}.exe" | |
| else: | |
| logger.error(f"[auto-cert] Unsupported OS: {system}") | |
| return None | |
| url = f"https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/{filename}" | |
| dest_dir = Path.home() / ".local" / "bin" | |
| dest_dir.mkdir(parents=True, exist_ok=True) | |
| dest = dest_dir / ("mkcert.exe" if system == "windows" else "mkcert") | |
| logger.info(f"[auto-cert] Downloading: {url}") | |
| try: | |
| urllib.request.urlretrieve(url, dest) | |
| except Exception as e: | |
| logger.error(f"[auto-cert] Failed to download mkcert: {e}") | |
| return None | |
| # Make executable if Unix | |
| if system != "windows": | |
| dest.chmod(0o755) | |
| logger.info(f"[auto-cert] mkcert installed at {dest}") | |
| # Verify it works | |
| try: | |
| subprocess.check_call([str(dest), "-help"], | |
| stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | |
| except Exception: | |
| logger.error("[auto-cert] mkcert failed to run after install.") | |
| return None | |
| return str(dest) | |
| def _run_command(cmd): | |
| """Run command, return True on success.""" | |
| try: | |
| subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | |
| return True | |
| except subprocess.CalledProcessError: | |
| return False | |
| def ensure_mkcert_ca(mkcert_bin: str): | |
| """ | |
| Install mkcert CA if not installed yet. | |
| Parameters | |
| ---------- | |
| mkcert_bin : str | |
| Path to mkcert binary. | |
| """ | |
| _run_command([mkcert_bin, "-install"]) | |
| def create_cert_if_needed(cert_dir: str): | |
| """ | |
| Create cert.pem and key.pem using mkcert if they don't already exist. | |
| Returns (cert_file, key_file) or (None, None) if mkcert unavailable. | |
| Parameters | |
| ---------- | |
| cert_dir : str | |
| Directory to store or find cert.pem and key.pem. | |
| Returns | |
| ------- | |
| pathlib.Path | None | |
| Path to cert.pem, or None if not created. | |
| pathlib.Path | None | |
| Path to key.pem, or None if not created. | |
| """ | |
| cert_dir = Path(cert_dir) | |
| cert_dir.mkdir(parents=True, exist_ok=True) | |
| cert_file = cert_dir / "cert.pem" | |
| key_file = cert_dir / "key.pem" | |
| # Already exists → nothing to do | |
| if cert_file.exists() and key_file.exists(): | |
| return cert_file, key_file | |
| mkcert_bin = ensure_mkcert() | |
| if not mkcert_bin: | |
| logger.warning("[auto-cert] mkcert not installed; falling back to HTTP.") | |
| return None, None | |
| logger.info("[auto-cert] mkcert detected. Ensuring local CA installed...") | |
| ensure_mkcert_ca(mkcert_bin) | |
| # Create cert for localhost, loopback, and LAN IP | |
| lan_ip = get_lan_ip() | |
| logger.info(f"[auto-cert] Generating certificate for localhost and {lan_ip}...") | |
| success = _run_command([ | |
| mkcert_bin, | |
| "-cert-file", str(cert_file), | |
| "-key-file", str(key_file), | |
| "localhost", | |
| "127.0.0.1", | |
| "::1", | |
| lan_ip | |
| ]) | |
| if not success: | |
| logger.warning("[auto-cert] mkcert failed. Using HTTP.") | |
| return None, None | |
| logger.info("[auto-cert] Certificate generated.") | |
| return cert_file, key_file | |
| def create_ssl_context(cert_dir: str): | |
| """ | |
| Main entry: create SSL context if certificates can be created. | |
| Returns (ssl_context, protocol_str) where protocol_str is "http" or "https". | |
| Parameters | |
| ---------- | |
| cert_dir : str | |
| Directory to store or find cert.pem and key.pem. | |
| Returns | |
| ------- | |
| ssl.SSLContext | None | |
| SSL context if HTTPS is available, else None. | |
| str | |
| "https" if SSL context created, else "http". | |
| """ | |
| cert_file, key_file = create_cert_if_needed(cert_dir) | |
| if cert_file is None: | |
| # mkcert missing → use HTTP | |
| return None, "http" | |
| import ssl | |
| ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) | |
| ctx.load_cert_chain(certfile=str(cert_file), keyfile=str(key_file)) | |
| return ctx, "https" | |