1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, it } from "@effect/vitest";
3import * as DateTime from "effect/DateTime";
4import * as Duration from "effect/Duration";
5import * as Effect from "effect/Effect";
6import * as FileSystem from "effect/FileSystem";
7import * as Layer from "effect/Layer";
8import * as Path from "effect/Path";
9import * as TestClock from "effect/testing/TestClock";
10
11import * as ServerConfig from "../config.ts";
12import { pruneExpiredReplayMarkers, REPLAY_MARKER_MAX_AGE } from "./replayMarkers.ts";
13import * as ServerSecretStore from "./ServerSecretStore.ts";
14
15// Every secret name the server stores today. The last three stand for names
16// built from an id at runtime.
17const REAL_SECRET_NAMES = [
18 "server-signing-key",
19 "asset-access-signing-key",
20 "cloud-cli-oauth-token",
21 "cloud-cli-desired-link",
22 "cloud-link-ed25519-key-pair",
23 "cloud-link-ed25519-private-key",
24 "cloud-link-ed25519-public-key",
25 "cloud-mint-ed25519-public-key",
26 "cloud-endpoint-runtime-config",
27 "cloud-endpoint-confirmed-origin",
28 "cloud-linked-user-id",
29 "cloud-relay-url",
30 "cloud-relay-issuer",
31 "cloud-relay-environment-credential",
32 "cloud-publish-agent-activity",
33 "provider-env-Y29kZXg-T1BFTkFJX0FQSV9LRVk",
34 "provider-auth-0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0",
35 "usage-limit-source-aHVi",
36];
37
38it.layer(NodeServices.layer)("replayMarkers", (it) => {
39 it.effect("prunes only replay markers older than the max age", () =>
40 Effect.gen(function* () {
41 const secretStore = yield* ServerSecretStore.ServerSecretStore;
42 const { secretsDir } = yield* ServerConfig.ServerConfig;
43 const fileSystem = yield* FileSystem.FileSystem;
44 const path = yield* Path.Path;
45 const now = DateTime.makeUnsafe("2026-01-01T00:00:00Z");
46 const setAge = (fileName: string, age: Duration.Duration) => {
47 const mtime = DateTime.toDateUtc(DateTime.subtractDuration(now, age));
48 return fileSystem.utimes(path.join(secretsDir, fileName), mtime, mtime);
49 };
50 const writeAged = (name: string, age: Duration.Duration) =>
51 secretStore
52 .create(name, Uint8Array.from([1]))
53 .pipe(Effect.andThen(setAge(`${name}.bin`, age)));
54
55 const justExpired = Duration.sum(REPLAY_MARKER_MAX_AGE, Duration.seconds(1));
56 const expiredMarkers = [
57 "dpop-proof-old",
58 "cloud-mint-jti-old",
59 "cloud-mint-nonce-old",
60 "cloud-health-jti-old",
61 "cloud-health-nonce-old",
62 ];
63 for (const name of expiredMarkers) yield* writeAged(name, justExpired);
64 yield* writeAged("dpop-proof-at-max-age", REPLAY_MARKER_MAX_AGE);
65 for (const name of REAL_SECRET_NAMES) yield* writeAged(name, Duration.days(30));
66 const pendingSetFile = "dpop-proof-pending.bin.0000.tmp";
67 yield* fileSystem.writeFile(path.join(secretsDir, pendingSetFile), Uint8Array.from([1]));
68 yield* setAge(pendingSetFile, Duration.days(30));
69 yield* TestClock.setTime(DateTime.toEpochMillis(now));
70
71 yield* pruneExpiredReplayMarkers();
72
73 const remaining = yield* fileSystem.readDirectory(secretsDir);
74 assert.deepStrictEqual(
75 remaining.toSorted(),
76 [
77 ...REAL_SECRET_NAMES.map((name) => `${name}.bin`),
78 "dpop-proof-at-max-age.bin",
79 pendingSetFile,
80 ].toSorted(),
81 );
82 }).pipe(
83 Effect.provide(
84 ServerSecretStore.layer.pipe(
85 Layer.provideMerge(
86 ServerConfig.layerTest(process.cwd(), { prefix: "t3-replay-markers-test-" }),
87 ),
88 ),
89 ),
90 ),
91 );
92});