History

fix(server): decouple CLI identity from launcher IPC

Bil0000 committed 2 months ago

Commit 56048cd1 parent 2aebbc112 files · +87 −24Browse files

Unified · Split

12 files
  1. apps/server/src/auth/EnvironmentAuth.test.ts +1 −1
  2. apps/server/src/auth/EnvironmentAuth.ts +1 −1
  3. apps/server/src/auth/EnvironmentAuthAdmin.test.ts +1 −1
  4. apps/server/src/auth/EnvironmentAuthPolicy.test.ts +1 −2
  5. apps/server/src/auth/EnvironmentAuthPolicy.ts +1 −1
  6. apps/server/src/auth/SessionStore.test.ts +1 −2
  7. apps/server/src/auth/SessionStore.ts +1 −1
  8. apps/server/src/bin.test.ts +26 −3
  9. apps/server/src/cli/connect.ts +2 −3
  10. apps/server/src/cli/pair.test.ts +24 −1
  11. apps/server/src/environment/ServerEnvironment.ts +27 −6
  12. apps/server/src/server.test.ts +1 −2
apps/server/src/auth/EnvironmentAuth.test.ts+1−1
@@ -35,7 +35,7 @@ const makeEnvironmentAuthLayer = (overrides?: Partial<ServerConfig.ServerConfig[ · 34 unmodified lines
3535 EnvironmentAuth.layer.pipe(
3636 Layer.provide(SqlitePersistenceMemory),
3737 Layer.provide(ServerSecretStore.layer),
38− Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))),
38+ Layer.provide(ServerEnvironment.identityLayer),
3939 Layer.provide(makeServerConfigLayer(overrides)),
4040 );
4141
apps/server/src/auth/EnvironmentAuth.ts+1−1
@@ -1023,5 +1023,5 @@ export const storageLayer = Layer.mergeAll(ServerSecretStore.layer, SqlitePersis · 1022 unmodified lines
10231023
10241024 export const runtimeLayer = layer.pipe(
10251025 Layer.provideMerge(storageLayer),
1026− Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))),
1026+ Layer.provideMerge(ServerEnvironment.identityLayer),
10271027 );
apps/server/src/auth/EnvironmentAuthAdmin.test.ts+1−1
@@ -36,7 +36,7 @@ const makeEnvironmentAuthLayer = ( · 35 unmodified lines
3636 EnvironmentAuth.layer.pipe(
3737 Layer.provideMerge(ServerSecretStore.layer),
3838 Layer.provideMerge(SqlitePersistenceMemory),
39− Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))),
39+ Layer.provide(ServerEnvironment.identityLayer),
4040 Layer.provide(makeServerConfigLayer(overrides)),
4141 );
4242
apps/server/src/auth/EnvironmentAuthPolicy.test.ts+1−2
@@ -6,13 +6,12 @@ import * as Layer from "effect/Layer"; · 5 unmodified lines
66 import * as ServerConfig from "../config.ts";
77 import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
88 import * as EnvironmentAuthPolicy from "./EnvironmentAuthPolicy.ts";
9−import * as ServerSecretStore from "./ServerSecretStore.ts";
109
1110 const makeEnvironmentAuthPolicyLayer = (
1211 overrides?: Partial<ServerConfig.ServerConfig["Service"]>,
1312 ) =>
1413 EnvironmentAuthPolicy.layer.pipe(
15− Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))),
14+ Layer.provide(ServerEnvironment.identityLayer),
1615 Layer.provide(
1716 Layer.effect(
1817 ServerConfig.ServerConfig,
apps/server/src/auth/EnvironmentAuthPolicy.ts+1−1
@@ -16,7 +16,7 @@ export class EnvironmentAuthPolicy extends Context.Service< · 15 unmodified lines
1616
1717 export const make = Effect.gen(function* () {
1818 const config = yield* ServerConfig.ServerConfig;
19− const serverEnvironment = yield* ServerEnvironment.ServerEnvironment;
19+ const serverEnvironment = yield* ServerEnvironment.ServerEnvironmentIdentity;
2020 const isRemoteReachable = isRemoteReachableHost(config.host);
2121
2222 const policy =
apps/server/src/auth/SessionStore.test.ts+1−2
@@ -28,9 +28,8 @@ const makeServerConfigLayer = (overrides?: Partial<ServerConfig.ServerConfig["Se · 27 unmodified lines
2828 ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-auth-session-test-" })));
2929
3030 const makeServerEnvironmentLayer = (environmentId: EnvironmentId) =>
31− Layer.succeed(ServerEnvironment.ServerEnvironment, {
31+ Layer.succeed(ServerEnvironment.ServerEnvironmentIdentity, {
3232 getEnvironmentId: Effect.succeed(environmentId),
33− getDescriptor: Effect.die(new Error("unused in SessionStore tests")),
3433 });
3534
3635 const makeSessionStoreLayer = (
apps/server/src/auth/SessionStore.ts+1−1
@@ -473,7 +473,7 @@ function toAuthClientSession(input: Omit<AuthClientSession, "current">): AuthCli · 472 unmodified lines
473473 export const make = Effect.gen(function* () {
474474 const crypto = yield* Crypto.Crypto;
475475 const serverConfig = yield* ServerConfig.ServerConfig;
476− const serverEnvironment = yield* ServerEnvironment.ServerEnvironment;
476+ const serverEnvironment = yield* ServerEnvironment.ServerEnvironmentIdentity;
477477 const secretStore = yield* ServerSecretStore.ServerSecretStore;
478478 const authSessions = yield* AuthSessions.AuthSessionRepository;
479479 const signingSecret = yield* secretStore.getOrCreateRandom(SIGNING_SECRET_NAME, 32);
apps/server/src/bin.test.ts+26−3
@@ -13,6 +13,7 @@ import { · 12 unmodified lines
1313 ThreadId,
1414 } from "@t3tools/contracts";
1515 import * as NetService from "@t3tools/shared/Net";
16+import { HostProcessEnvironment } from "@t3tools/shared/hostProcess";
1617 import { assert, it } from "@effect/vitest";
1718 import * as Effect from "effect/Effect";
1819 import * as DateTime from "effect/DateTime";
@@ -26,6 +27,11 @@ import * as TestConsole from "effect/testing/TestConsole"; · 7 unmodified lines
2627 import { Command } from "effect/unstable/cli";
2728
2829 import { cli, makeCli } from "./bin.ts";
30+import * as ServiceLauncherClient from "./cloud/serviceLauncherClient.ts";
31+import {
32+ SERVICE_LAUNCHER_CONTEXT_ENV,
33+ SERVICE_LAUNCHER_PROTOCOL,
34+} from "./cloud/serviceProtocol.ts";
2935 import * as ServerConfig from "./config.ts";
3036 import * as ServerEnvironment from "./environment/ServerEnvironment.ts";
3137 import * as ProjectionSnapshotQuery from "./orchestration/Services/ProjectionSnapshotQuery.ts";
@@ -43,7 +49,24 @@ import * as ServerSecretStore from "./auth/ServerSecretStore.ts"; · 11 unmodified lines
4349 import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts";
4450 import { environmentAuthenticatedAuthLayer } from "./auth/http.ts";
4551
52+import packageJson from "../package.json" with { type: "json" };
53+
4654 const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, NetService.layer);
55+const DisconnectedLauncherChildLayer = Layer.mergeAll(
56+ Layer.succeed(HostProcessEnvironment, {
57+ ...process.env,
58+ [SERVICE_LAUNCHER_CONTEXT_ENV]: JSON.stringify({
59+ protocol: SERVICE_LAUNCHER_PROTOCOL,
60+ childVersion: packageJson.version,
61+ }),
62+ }),
63+ Layer.succeed(ServiceLauncherClient.ServiceLauncherHostProcess, {
64+ connected: false,
65+ send: () => false,
66+ on: () => undefined,
67+ off: () => undefined,
68+ }),
69+);
4770 class ProjectCliHttpApi extends HttpApi.make("environment").add(EnvironmentOrchestrationHttpApi) {}
4871
4972 const connectCli = makeCli({ cloudEnabled: true });
@@ -128,7 +151,7 @@ const withLiveProjectCliServer = <A, E, R>(baseDir: string, run: () => Effect.Ef · 78 unmodified lines
128151 Layer.provideMerge(
129152 EnvironmentAuth.layer.pipe(
130153 Layer.provideMerge(SqlitePersistenceLayerLive),
131− Layer.provide(ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer))),
154+ Layer.provide(ServerEnvironment.identityLayer),
132155 Layer.provide(ServerSecretStore.layer),
133156 ),
134157 ),
@@ -239,7 +262,7 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { · 104 unmodified lines
239262 assert.equal(status.linked, false);
240263 assert.equal(status.cloudUserId, null);
241264 assert.equal(status.relayUrl, null);
242− }),
265+ }).pipe(Effect.provide(DisconnectedLauncherChildLayer)),
243266 );
244267
245268 it.effect("reports actionable human-readable headless connect state", () =>
@@ -410,7 +433,7 @@ it.layer(NodeServices.layer)("bin cli parsing", (it) => { · 164 unmodified lines
410433 "relay:write",
411434 ]);
412435 assert.equal("token" in (listed[0] ?? {}), false);
413− }),
436+ }).pipe(Effect.provide(DisconnectedLauncherChildLayer)),
414437 );
415438
416439 it.effect("rejects invalid ttl values before running auth commands", () =>
apps/server/src/cli/connect.ts+2−3
@@ -337,7 +337,7 @@ const unlinkRelayEnvironment = Effect.fn("cloud.cli.unlink_relay_environment")(f · 336 unmodified lines
337337 return { status: "not-authenticated" } satisfies RelayUnlinkResult;
338338 }
339339
340− const environment = yield* ServerEnvironment.ServerEnvironment;
340+ const environment = yield* ServerEnvironment.ServerEnvironmentIdentity;
341341 const environmentId = yield* environment.getEnvironmentId;
342342 const relayUrl = yield* relayUrlConfig;
343343 const httpClient = yield* HttpClient.HttpClient;
@@ -432,7 +432,7 @@ const runCloudCommand = Effect.fn("cloud.cli.run_cloud_command")(function* <A, E · 88 unmodified lines
432432 | HttpClient.HttpClient
433433 | Prompt.Environment
434434 | ServerConfig.ServerConfig
435− | ServerEnvironment.ServerEnvironment
435+ | ServerEnvironment.ServerEnvironmentIdentity
436436 >,
437437 options?: {
438438 readonly quietLogs?: boolean;
@@ -449,7 +449,6 @@ const runCloudCommand = Effect.fn("cloud.cli.run_cloud_command")(function* <A, E · 10 unmodified lines
449449 ),
450450 RelayClient.layerCloudflared({ baseDir: config.baseDir }),
451451 EnvironmentAuth.runtimeLayer,
452− ServerEnvironment.layer.pipe(Layer.provide(ServerSecretStore.layer)),
453452 bootServiceLayer(config),
454453 headlessRelayClientTracingLayer,
455454 ).pipe(
apps/server/src/cli/pair.test.ts+24−1
@@ -6,6 +6,7 @@ import * as NodePath from "node:path"; · 5 unmodified lines
66
77 import * as NodeServices from "@effect/platform-node/NodeServices";
88 import * as NetService from "@t3tools/shared/Net";
9+import { HostProcessEnvironment } from "@t3tools/shared/hostProcess";
910 import { assert, describe, expect, it } from "@effect/vitest";
1011 import * as Effect from "effect/Effect";
1112 import * as Layer from "effect/Layer";
@@ -13,6 +14,11 @@ import * as TestConsole from "effect/testing/TestConsole"; · 1 unmodified line
1314 import { Command } from "effect/unstable/cli";
1415
1516 import { cli } from "../bin.ts";
17+import {
18+ SERVICE_LAUNCHER_CONTEXT_ENV,
19+ SERVICE_LAUNCHER_PROTOCOL,
20+} from "../cloud/serviceProtocol.ts";
21+import * as ServiceLauncherClient from "../cloud/serviceLauncherClient.ts";
1622 import {
1723 makePersistedServerRuntimeState,
1824 persistServerRuntimeState,
@@ -24,6 +30,8 @@ import { · 5 unmodified lines
2430 resolveTailscaleLocalTarget,
2531 } from "./pair.ts";
2632
33+import packageJson from "../../package.json" with { type: "json" };
34+
2735 const CliRuntimeLayer = Layer.mergeAll(NodeServices.layer, NetService.layer);
2836
2937 const baseState = {
@@ -170,7 +178,22 @@ describe("t3 pair", () => { · 140 unmodified lines
170178 assert.equal(credentials.length, 1);
171179 assert.equal(credentials[0]?.label, "t3 pair");
172180 }),
173− ).pipe(Effect.provide(NodeServices.layer)),
181+ ).pipe(
182+ Effect.provide(NodeServices.layer),
183+ Effect.provideService(HostProcessEnvironment, {
184+ ...process.env,
185+ [SERVICE_LAUNCHER_CONTEXT_ENV]: JSON.stringify({
186+ protocol: SERVICE_LAUNCHER_PROTOCOL,
187+ childVersion: packageJson.version,
188+ }),
189+ }),
190+ Effect.provideService(ServiceLauncherClient.ServiceLauncherHostProcess, {
191+ connected: false,
192+ send: () => false,
193+ on: () => undefined,
194+ off: () => undefined,
195+ }),
196+ ),
174197 );
175198
176199 it.effect("pairs through the recorded dev web URL for dev servers", () =>
apps/server/src/environment/ServerEnvironment.ts+27−6
@@ -42,6 +42,13 @@ export class ServerEnvironment extends Context.Service< · 41 unmodified lines
4242 }
4343 >()("t3/environment/ServerEnvironment") {}
4444
45+export class ServerEnvironmentIdentity extends Context.Service<
46+ ServerEnvironmentIdentity,
47+ {
48+ readonly getEnvironmentId: Effect.Effect<EnvironmentId>;
49+ }
50+>()("t3/environment/ServerEnvironment/ServerEnvironmentIdentity") {}
51+
4552 function platformOs(platform: NodeJS.Platform): ExecutionEnvironmentDescriptor["platform"]["os"] {
4653 switch (platform) {
4754 case "darwin":
@@ -68,14 +75,10 @@ function platformArch( · 20 unmodified lines
6875 }
6976 }
7077
71−export const make = Effect.gen(function* () {
78+const makeIdentity = Effect.gen(function* () {
7279 const fileSystem = yield* FileSystem.FileSystem;
73− const path = yield* Path.Path;
7480 const serverConfig = yield* ServerConfig.ServerConfig;
75− const secrets = yield* ServerSecretStore.ServerSecretStore;
7681 const crypto = yield* Crypto.Crypto;
77− const hostPlatform = yield* HostProcessPlatform;
78− const hostArchitecture = yield* HostProcessArchitecture;
7982
8083 const readPersistedEnvironmentId = Effect.gen(function* () {
8184 const exists = yield* fileSystem.exists(serverConfig.environmentIdPath).pipe(
@@ -131,6 +134,19 @@ export const make = Effect.gen(function* () { · 49 unmodified lines
131134 });
132135
133136 const environmentId = EnvironmentId.make(environmentIdRaw);
137+ return ServerEnvironmentIdentity.of({
138+ getEnvironmentId: Effect.succeed(environmentId),
139+ });
140+});
141+
142+export const make = Effect.gen(function* () {
143+ const path = yield* Path.Path;
144+ const serverConfig = yield* ServerConfig.ServerConfig;
145+ const secrets = yield* ServerSecretStore.ServerSecretStore;
146+ const identity = yield* ServerEnvironmentIdentity;
147+ const hostPlatform = yield* HostProcessPlatform;
148+ const hostArchitecture = yield* HostProcessArchitecture;
149+ const environmentId = yield* identity.getEnvironmentId;
134150 const cwdBaseName = path.basename(serverConfig.cwd).trim();
135151 const label = yield* resolveServerEnvironmentLabel({ cwdBaseName });
136152 const launcher = yield* resolveServiceLauncherMode();
@@ -185,4 +201,9 @@ export const make = Effect.gen(function* () { · 48 unmodified lines
185201 * provide the external platform services, a ServerConfig, and the
186202 * ServerSecretStore backing the descriptor's publishing capability.
187203 */
188−export const layer = Layer.effect(ServerEnvironment, make).pipe(Layer.provide(ProcessRunner.layer));
204+export const identityLayer = Layer.effect(ServerEnvironmentIdentity, makeIdentity);
205+
206+export const layer = Layer.effect(ServerEnvironment, make).pipe(
207+ Layer.provideMerge(identityLayer),
208+ Layer.provide(ProcessRunner.layer),
209+);
apps/server/src/server.test.ts+1−2
@@ -292,9 +292,8 @@ const makeAuthTestLayer = () => · 291 unmodified lines
292292 Layer.provide(SqlitePersistenceMemory),
293293 Layer.provide(ServerSecretStore.layer),
294294 Layer.provide(
295− Layer.mock(ServerEnvironment.ServerEnvironment)({
295+ Layer.mock(ServerEnvironment.ServerEnvironmentIdentity)({
296296 getEnvironmentId: Effect.succeed(testEnvironmentDescriptor.environmentId),
297− getDescriptor: Effect.succeed(testEnvironmentDescriptor),
298297 }),
299298 ),
300299 );