Enhance run-aspire.cmd to support Tailscale integration for remote access. The script now checks for Tailscale availability and connection status, providing warnings if not found or connected. Updated documentation in AGENTS.md and README.md to reflect changes, including instructions for using Tailscale with the Vite client. Fixed Vite configuration to pin the client port to 5173 and support HMR over Tailscale. Adjusted AppHost.cs to ensure proper endpoint targeting for Tailscale.
ci / server (push) Failing after 3m45s
ci / client (push) Successful in 17s

This commit is contained in:
Leonid Pershin
2026-08-20 10:02:42 +03:00
parent 51b4c22b69
commit cb869e4977
5 changed files with 68 additions and 6 deletions
+5 -4
View File
@@ -54,13 +54,14 @@ dotnet run --project src/HSchool.AppHost
``` ```
`run-aspire.cmd` is the Windows entry point: it skips MSBuild when AppHost and Server dlls are `run-aspire.cmd` is the Windows entry point: it skips MSBuild when AppHost and Server dlls are
newer than C# / csproj / props. Pass `--rebuild` to force a build. Keep the script in sync with newer than C# / csproj / props. Pass `--rebuild` to force a build. When `tailscale` is on PATH
`tools/apphost-uptodate.ps1` if the AppHost path ever moves. it also runs `tailscale serve --bg 5173` when `tailscale` is on PATH and connected; otherwise
it warns and continues locally only. Pass `--no-tailscale` to skip Serve. Keep the script in sync
with `tools/apphost-uptodate.ps1` if the AppHost path ever moves.
`dotnet run --project src/HSchool.AppHost` still compiles every time — that is for a dirty tree, `dotnet run --project src/HSchool.AppHost` still compiles every time — that is for a dirty tree,
not a fast relaunch. It starts the server *and* the Vite dev server and opens the Aspire not a fast relaunch. It starts the server *and* the Vite dev server and opens the Aspire
dashboard. The Vite port is assigned per run (`npm run dev -- --port <random>`), so read the dashboard. The Vite client listens on port **5173** (pinned in AppHost for Tailscale Serve).
client URL off the dashboard instead of assuming 5173.
Do not start a dev server with a bare `npm run dev` when you meant to run the whole app — the Do not start a dev server with a bare `npm run dev` when you meant to run the whole app — the
client only finds the backend through the Aspire-injected `SERVER_HTTP` environment variable, or client only finds the backend through the Aspire-injected `SERVER_HTTP` environment variable, or
+21 -2
View File
@@ -36,8 +36,27 @@ and Node are on PATH first and passes any arguments through
`run-aspire.cmd --rebuild` forces a build. `run-aspire.cmd --rebuild` forces a build.
The Aspire dashboard opens with two resources: `server` (ASP.NET Core) and `client` (Vite dev The Aspire dashboard opens with two resources: `server` (ASP.NET Core) and `client` (Vite dev
server). Aspire assigns the client a random port on every run, so take its URL from the dashboard server on port **5173**).
rather than guessing.
## Share with friends on Tailscale
For a dev session over your tailnet, expose only the Vite client — not the game server (5180),
Aspire dashboard (15180), or SwarmUI.
`run-aspire.cmd` runs `tailscale serve --bg 5173` before the AppHost when `tailscale` is on
PATH and connected. If Tailscale is missing, offline, or `serve` fails, the script prints a
warning and continues with local access only (`http://localhost:5173`). Pass `--no-tailscale`
to skip Serve entirely. You can also run the command yourself once per boot if you start with
`dotnet run --project src/HSchool.AppHost` instead.
Friends on the same tailnet open `https://<your-machine>.<tailnet>.ts.net`. The page, REST API
and game WebSocket all go through Vites proxy on one origin; Kestrel stays on localhost.
Change `HSchool:AlphaPassword` from the repo default before sharing the link. Optional: set
`HSCHOOL_TAILSCALE_SERVE=1` in the client process environment if you want Vite HMR through Serve
(`wss` on port 443); gameplay works without it.
Do **not** add port 5180 or the Aspire dashboard to `tailscale serve`.
## What you can do ## What you can do
+32
View File
@@ -5,6 +5,7 @@ rem Starts the whole app: game server, Vite client and the Aspire dashboard.
rem Skips MSBuild when AppHost and Server dlls are newer than C# / csproj / props. rem Skips MSBuild when AppHost and Server dlls are newer than C# / csproj / props.
rem run-aspire.cmd --launch-profile http rem run-aspire.cmd --launch-profile http
rem run-aspire.cmd --rebuild rem run-aspire.cmd --rebuild
rem run-aspire.cmd --no-tailscale skip `tailscale serve --bg 5173`
cd /d "%~dp0" cd /d "%~dp0"
@@ -25,6 +26,7 @@ if errorlevel 1 (
set "CONFIG=Debug" set "CONFIG=Debug"
set "REBUILD=0" set "REBUILD=0"
set "TAILSCALE_SERVE=1"
set "ARGS=" set "ARGS="
:parse :parse
@@ -34,6 +36,11 @@ if /I "%~1"=="--rebuild" (
shift shift
goto parse goto parse
) )
if /I "%~1"=="--no-tailscale" (
set "TAILSCALE_SERVE=0"
shift
goto parse
)
if /I "%~1"=="-c" if not "%~2"=="" set "CONFIG=%~2" if /I "%~1"=="-c" if not "%~2"=="" set "CONFIG=%~2"
if /I "%~1"=="--configuration" if not "%~2"=="" set "CONFIG=%~2" if /I "%~1"=="--configuration" if not "%~2"=="" set "CONFIG=%~2"
set "ARGS=!ARGS! %1" set "ARGS=!ARGS! %1"
@@ -74,6 +81,31 @@ if errorlevel 1 (
echo. echo.
:run :run
if "%TAILSCALE_SERVE%"=="1" (
where tailscale >nul 2>&1
if errorlevel 1 (
echo [run-aspire] WARNING: tailscale not found in PATH.
echo Friends cannot reach the client over the tailnet.
echo Continuing with local access only ^(http://localhost:5173^).
) else (
tailscale status >nul 2>&1
if errorlevel 1 (
echo [run-aspire] WARNING: tailscale is not connected or not running.
echo Friends cannot reach the client over the tailnet.
echo Continuing with local access only ^(http://localhost:5173^).
) else (
echo [run-aspire] tailscale serve --bg 5173
tailscale serve --bg 5173
if errorlevel 1 (
echo [run-aspire] WARNING: tailscale serve failed.
echo Friends cannot reach the client over the tailnet.
echo Continuing with local access only ^(http://localhost:5173^).
)
)
)
echo.
)
echo [run-aspire] Starting the Aspire AppHost. Press Ctrl+C to shut everything down. echo [run-aspire] Starting the Aspire AppHost. Press Ctrl+C to shut everything down.
echo. echo.
+2
View File
@@ -23,7 +23,9 @@ if (headless)
if (!headless) if (!headless)
{ {
// Pin the Vite port so `tailscale serve --bg 5173` always targets the client.
var client = builder.AddViteApp("client", "../HSchool.Client") var client = builder.AddViteApp("client", "../HSchool.Client")
.WithHttpEndpoint(port: 5173, targetPort: 5173)
.WithReference(server) .WithReference(server)
.WaitFor(server); .WaitFor(server);
+8
View File
@@ -4,8 +4,16 @@ import { defineConfig } from 'vitest/config';
// so the dev server proxies to whatever port the backend actually got. // so the dev server proxies to whatever port the backend actually got.
const backend = process.env.SERVER_HTTPS ?? process.env.SERVER_HTTP ?? 'http://localhost:5180'; const backend = process.env.SERVER_HTTPS ?? process.env.SERVER_HTTP ?? 'http://localhost:5180';
// Set HSCHOOL_TAILSCALE_SERVE=1 when exposing Vite through `tailscale serve` so HMR
// uses wss:443 instead of the dev-server port. Gameplay does not need this.
const tailscaleServe = process.env.HSCHOOL_TAILSCALE_SERVE === '1';
export default defineConfig({ export default defineConfig({
server: { server: {
port: 5173,
strictPort: true,
allowedHosts: ['.ts.net'],
hmr: tailscaleServe ? { protocol: 'wss', clientPort: 443 } : undefined,
proxy: { proxy: {
'/api': { '/api': {
target: backend, target: backend,