History

[codex] Redact agent awareness relay diagnostics (#3410)

Julius Marminge committed 4 months ago

Commit 025fd6f1 parent 52701262 files · +91 −7Browse files

Unified · Split

2 files
  1. apps/server/src/relay/AgentAwarenessRelay.test.ts +45 −0
  2. apps/server/src/relay/AgentAwarenessRelay.ts +46 −7
apps/server/src/relay/AgentAwarenessRelay.test.ts+45−0
@@ -20,6 +20,7 @@ import { CommandId, ProviderInstanceId } from "@t3tools/contracts"; · 19 unmodified lines
2020 import { RelayClientTracer } from "@t3tools/shared/relayTracing";
2121 import { RELAY_ACTIVITY_PUBLISH_TYP, verifyRelayJwt } from "@t3tools/shared/relayJwt";
2222 import { describe, expect, it } from "@effect/vitest";
23+import * as Cause from "effect/Cause";
2324 import * as Deferred from "effect/Deferred";
2425 import * as Effect from "effect/Effect";
2526 import * as Layer from "effect/Layer";
@@ -98,6 +99,50 @@ function makeMemorySecretStore() { · 72 unmodified lines
9899 }
99100
100101 describe.sequential("signRelayAgentActivityPublishProof", () => {
102+ it("redacts relay URL secrets from log attributes", () => {
103+ const relayUrl =
104+ "https://relay-user:relay-password@relay.example.test/private/path?token=relay-secret#fragment";
105+ const attributes = AgentAwarenessRelay.relayUrlLogAttributes(relayUrl);
106+
107+ expect(attributes).toEqual({
108+ relayUrlConfigured: true,
109+ relayUrlInputLength: relayUrl.length,
110+ relayUrlProtocol: "https:",
111+ relayUrlHostname: "relay.example.test",
112+ });
113+ const renderedAttributes = Object.values(attributes).join(" ");
114+ expect(renderedAttributes).not.toContain("relay-user");
115+ expect(renderedAttributes).not.toContain("relay-password");
116+ expect(renderedAttributes).not.toContain("private/path");
117+ expect(renderedAttributes).not.toContain("relay-secret");
118+ expect(renderedAttributes).not.toContain("fragment");
119+ });
120+
121+ it("summarizes publish causes without serializing nested failures or defects", () => {
122+ const privateFailureDetail = "private relay response body";
123+ const privateDefectDetail = "private relay defect detail";
124+ const cause = Cause.combine(
125+ Cause.fail({
126+ _tag: "RelayPublishError",
127+ cause: new Error(privateFailureDetail),
128+ detail: privateFailureDetail,
129+ }),
130+ Cause.die(new Error(privateDefectDetail)),
131+ );
132+ const attributes = AgentAwarenessRelay.relayPublishCauseLogAttributes(cause);
133+
134+ expect(attributes).toEqual({
135+ causeReasonCount: 2,
136+ causeFailureCount: 1,
137+ causeDefectCount: 1,
138+ causeInterruptionCount: 0,
139+ causeFailureTags: ["RelayPublishError"],
140+ });
141+ const renderedAttributes = Object.values(attributes).join(" ");
142+ expect(renderedAttributes).not.toContain(privateFailureDetail);
143+ expect(renderedAttributes).not.toContain(privateDefectDetail);
144+ });
145+
101146 it("distinguishes pending link credentials from disabled publication", () => {
102147 expect(
103148 AgentAwarenessRelay.resolveAgentActivityPublishingStartupState({
apps/server/src/relay/AgentAwarenessRelay.ts+46−7
@@ -18,6 +18,7 @@ import { · 17 unmodified lines
1818 RELAY_ACTIVITY_PUBLISH_TYP,
1919 signRelayJwt,
2020 } from "@t3tools/shared/relayJwt";
21+import { getUrlDiagnostics } from "@t3tools/shared/urlDiagnostics";
2122 import * as Cause from "effect/Cause";
2223 import * as Context from "effect/Context";
2324 import * as Crypto from "effect/Crypto";
@@ -98,6 +99,44 @@ export function isAgentActivityPublishingEnabled(value: string | null): boolean · 74 unmodified lines
9899 return value === "true";
99100 }
100101
102+export function relayUrlLogAttributes(relayUrl: string | undefined) {
103+ if (relayUrl === undefined) {
104+ return { relayUrlConfigured: false };
105+ }
106+ const diagnostics = getUrlDiagnostics(relayUrl);
107+ return {
108+ relayUrlConfigured: true,
109+ relayUrlInputLength: diagnostics.inputLength,
110+ ...(diagnostics.protocol === undefined ? {} : { relayUrlProtocol: diagnostics.protocol }),
111+ ...(diagnostics.hostname === undefined ? {} : { relayUrlHostname: diagnostics.hostname }),
112+ };
113+}
114+
115+export function relayPublishCauseLogAttributes(cause: Cause.Cause<unknown>) {
116+ const failureTags = cause.reasons.flatMap((reason) => {
117+ if (!Cause.isFailReason(reason)) {
118+ return [];
119+ }
120+ const error = reason.error;
121+ if (
122+ typeof error !== "object" ||
123+ error === null ||
124+ !("_tag" in error) ||
125+ typeof error._tag !== "string"
126+ ) {
127+ return [];
128+ }
129+ return [error._tag];
130+ });
131+ return {
132+ causeReasonCount: cause.reasons.length,
133+ causeFailureCount: cause.reasons.filter(Cause.isFailReason).length,
134+ causeDefectCount: cause.reasons.filter(Cause.isDieReason).length,
135+ causeInterruptionCount: cause.reasons.filter(Cause.isInterruptReason).length,
136+ causeFailureTags: [...new Set(failureTags)],
137+ };
138+}
139+
101140 export function resolveAgentActivityPublishingStartupState(input: {
102141 readonly relayConfigured: boolean;
103142 readonly publishEnabled: boolean;
@@ -417,12 +456,12 @@ export const make = Effect.gen(function* () { · 313 unmodified lines
417456
418457 const publishThread: AgentAwarenessRelay["Service"]["publishThread"] = (threadId) =>
419458 publishThreadUnsafe(threadId).pipe(
420− Effect.catchCause((cause) => {
421− return Effect.logWarning("agent activity publish failed", {
459+ Effect.catchCause((cause) =>
460+ Effect.logWarning("agent activity publish failed", {
422461 threadId,
423− cause: Cause.pretty(cause),
424− });
425− }),
462+ ...relayPublishCauseLogAttributes(cause),
463+ }),
464+ ),
426465 Effect.withSpan("AgentAwarenessRelay.publishThread"),
427466 withRelayClientTracing,
428467 );
@@ -467,7 +506,7 @@ export const make = Effect.gen(function* () { · 38 unmodified lines
467506 if (logEnabledWhenReady) {
468507 const relayConfig = yield* readRelayConfig.pipe(Effect.orElseSucceed(() => null));
469508 yield* Effect.logInfo("agent activity publishing enabled after link reconciliation", {
470− relayUrl: relayConfig?.url,
509+ ...relayUrlLogAttributes(relayConfig?.url),
471510 });
472511 }
473512 return;
@@ -499,7 +538,7 @@ export const make = Effect.gen(function* () { · 25 unmodified lines
499538 break;
500539 case "enabled":
501540 yield* Effect.logInfo("agent activity publishing enabled", {
502− relayUrl: relayConfig?.url,
541+ ...relayUrlLogAttributes(relayConfig?.url),
503542 });
504543 break;
505544 }