Add the fluksio CLI: serve, enroll, worker

`pip install fluksio && fluksio serve` on a machine with no Docker, no
database and no configuration — which is the case this is for: a node on
a cluster where ports cannot be opened. It makes its data directory, its
key and an admin account, prints the password once, and serves. Pairing
is `fluksio enroll <code> --portal …`, doing what the Settings screen
does through the same function, before the engine starts and without one
running — a machine nobody can route to has no browser pointed at it
either. The portal serves the dashboard, so nothing is served here.

Two things had to give way. `fastapi[standard]` pulls a cloud CLI that
wants sentry-sdk 2.x while we pinned below it — no pip resolution
existed, so the pin is lifted, which the comment beside it had been
waiting for and which also lets the Python cap go. And `uv` is now a
dependency rather than something to find on PATH: the Modules screen is
how a data scientist installs torch, and it was quietly falling back to
the engine's own interpreter.

The CLI imports nothing from the engine before it has set DATA_DIR — the
settings are built on the first import of core.config, and reaching it
early put the database in the working directory. There is a test for
that now, because the failure is silent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 22:51:13 +02:00
co-authored by Claude Opus 5
parent 961a8f881d
commit 10b0ba9e49
15 changed files with 1811 additions and 197 deletions
+4 -9
View File
@@ -10,7 +10,7 @@ nothing, which is what makes deleting that local account a revocation.
from __future__ import annotations
import uuid
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import Mock, patch
import jwt
import pytest
@@ -206,9 +206,7 @@ def test_adding_a_remote_user_maps_and_revokes(
return_value={"user_id": "portal-user-9", "email": "remote@example.com"}
),
)
with patch(
"fluksio.api.routes.cloud.httpx.post", return_value=portal_reply
) as post:
with patch("fluksio.cloud.enroll.httpx.post", return_value=portal_reply) as post:
added = client.post(
f"{settings.API_V1_STR}/cloud/users",
headers=superuser_token_headers,
@@ -226,7 +224,7 @@ def test_adding_a_remote_user_maps_and_revokes(
resolved = user_from_token(db, theirs)
assert resolved is not None and resolved.email == "remote@example.com"
with patch("fluksio.api.routes.cloud.httpx.post", return_value=portal_reply):
with patch("fluksio.cloud.enroll.httpx.post", return_value=portal_reply):
again = client.post(
f"{settings.API_V1_STR}/cloud/users",
headers=superuser_token_headers,
@@ -281,10 +279,7 @@ def test_enrolling_against_a_portal_without_an_owner_is_refused(
),
)
try:
with patch("fluksio.api.routes.cloud.httpx.AsyncClient") as client_cls:
client_cls.return_value.__aenter__.return_value.post = AsyncMock(
return_value=reply
)
with patch("fluksio.cloud.enroll.httpx.post", return_value=reply):
response = client.post(
f"{settings.API_V1_STR}/cloud/enroll",
headers=superuser_token_headers,