- Drop ipykernel from the root virtual dependency list; the workspace relies on its subpackages and uv lockfile to manage per-service dependencies. - Refresh uv.lock to match the trimmed root dependency set. - Add CLAUDE.md describing the workspace layout, common development and database commands, the Nginx/backend/runtime auth request flow, and the boundaries to keep in mind when changing services.
6.3 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Repository overview
This is a Python 3.12 uv workspace for a small Jupyter workspace platform. The root pyproject.toml includes four workspace packages:
backend: FastAPI service used by Nginxauth_request. It reads the original workspace/URI headers, performs the current authentication and notebook-lock checks, asks Runtime to find or start a workspace, and returns the selected Jupyter upstream and internal token in response headers for Nginx.runtime: FastAPI process manager. At startup it mountsREMOTE_BUCKETwithrclone, scansWORKSPACES_ROOT, and auto-starts a Jupyter Notebook process for each workspace directory. It keeps process metadata (PID, port, token, URL, start time) in the in-memoryJUPYTER_PROCESSESdictionary and exposes health and start/stop/list/get actions.common: Shared package scaffold for SQLAlchemy/Alembic database code and object-storage helpers.common/src/common/migrations/env.pyusescommon.db.base.Base.metadata; the model/session/storage layers are currently mostly placeholders.schedule: Package scaffold only;main.pyandexecutor.pyare currently empty apart from module headers.
The request path in the deployed stack is client → Nginx (web) → backend auth subrequest → runtime → per-workspace Jupyter. default.conf also proxies /storage/ to RustFS and configures WebSocket forwarding for Jupyter. The frontend is not implemented in this repository; frontend/README.md only identifies it as the frontend.
Common commands
Run these from the repository root. Dependencies are managed by the committed uv.lock file.
# Install/synchronize all workspace dependencies
uv sync
# Run the services locally
make runtime # Runtime on 0.0.0.0:8001
make runtime-dev # Runtime with Uvicorn reload
make backend # Backend on 0.0.0.0:8000
make backend-dev # Backend with Uvicorn reload
# Equivalent direct commands
uv run --package runtime uvicorn runtime.main:app --host 0.0.0.0 --port 8001
uv run --package backend uvicorn backend.main:app --host 0.0.0.0 --port 8000
# Run the containerized stack (Nginx is exposed on localhost:8888)
docker compose up --build
docker compose down
Makefile variables can be overridden on the command line, for example:
make runtime-dev WORKSPACES_ROOT=./test/workspaces RUNTIME_PORT=8001
make backend-dev RUNTIME_BASE_URL=http://127.0.0.1:8001
The local Runtime defaults to WORKSPACES_ROOT=/app/workspaces in code, while the Makefile overrides it to ./test/workspaces; make sure the directory exists and contains workspace directories before expecting auto-start behavior. Runtime also needs rclone, FUSE support, and a valid remote configuration when exercising the mount path. The Docker image provides rclone and fuse3; a local process does not.
Tests, linting, and formatting
There is currently no test suite, test configuration, or lint/format command in the repository. Do not assume make test, pytest, Ruff, or Black is configured. If tests are added, run a single test with the project’s chosen runner (the conventional pytest form is uv run pytest path/to/test_file.py::test_name), then add the corresponding dependency and documented command rather than silently relying on a globally installed tool.
A lightweight current smoke check is to compile the Python sources:
python -m compileall backend/src common/src runtime/src schedule/src
Database migrations
Alembic is configured in common/alembic.ini, with scripts under common/src/common/migrations. The checked-in sqlalchemy.url is the generated placeholder driver://user:pass@localhost/dbname, so replace/configure it for a real database before running migrations. From the root, the usual commands are:
uv run alembic -c common/alembic.ini current
uv run alembic -c common/alembic.ini upgrade head
uv run alembic -c common/alembic.ini revision --autogenerate -m "describe change"
Important implementation details
backend/src/backend/main.pyis the ASGI entrypoint (backend.main:app). Nginx sends/internal-authtoGET /api/v1/auth/jupyter; the backend expectsX-Original-Workspace-IdandX-Original-URI, chooses a bearer token oraccess_tokencookie, and returnsx-upstream-addrplusx-jupyter-internal-tokenheaders. The JWT validation and database lock lookup are currently mocked/commented, so treat this as demo behavior rather than complete production authentication.runtime/src/runtime/main.pyis the ASGI entrypoint (runtime.main:app). Startup/shutdown is implemented through FastAPI lifespan: mount/scan/start on startup, stop Jupyter processes and unmount on shutdown. The API isGET /api/v1/healthandPOST /api/v1/jupyterwithactionvaluesstart,stop,list, orget(workspace ID is required for all exceptlist).- Runtime workspace IDs are directory names below
WORKSPACES_ROOT. A started Jupyter server receives a dynamic port, a generated token, and base URL/jupyter/<workspace_id>/. Because process state is only in memory, restarting Runtime loses the registry and causes startup scanning to rediscover workspaces. commonis a uv workspace dependency declared by backend/runtime/schedule, but the current service code does not yet contain substantial shared model or storage integration. Keep shared DB/storage behavior incommonrather than duplicating it in services.docker-compose.ymlbuilds backend and runtime from the root workspace, exposes backend on8000, runtime on8001, and Nginx on8888. Runtime requires Linux FUSE capabilities (SYS_ADMIN,/dev/fuse, and unconfined AppArmor). Its RustFS/rclone settings are supplied as compose environment variables; use deployment secrets or environment overrides instead of committing credentials.
Change boundaries
Keep service entrypoints and the Nginx header contract compatible when changing backend/runtime behavior. If an API action or response field changes, update both the caller in backend and the proxy rules in default.conf together. For runtime process changes, preserve cleanup in the lifespan shutdown path and consider failures from missing mounts, dead subprocesses, dynamic port allocation, and remote storage separately.