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