import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { DEFAULT_SCREEN_SHARE_ENCODING_POLICY, applyScreenShareEncodingPolicy, createRemoteOfficeMedia, type MediaAuthorizationDecision, type RemoteMediaTimers, type ScreenShareAccessGrant, type ScreenShareBinding, type ScreenShareParticipant, } from "../media/index.ts"; const BINDING: ScreenShareBinding = { officeId: "lumbridge-hq", levelId: "level-1", roomId: "commons", screenId: "wall-display", }; const NOW = 1_765_000_000_000; class FakeTrack { readonly kind = "video"; readyState: MediaStreamTrackState = "live"; stopped = 0; contentHint = ""; private readonly settings: MediaTrackSettings; constructor(settings: MediaTrackSettings = { width: 3_840, height: 2_160, frameRate: 60 }) { this.settings = settings; } getSettings(): MediaTrackSettings { return { ...this.settings }; } stop(): void { this.stopped += 1; this.readyState = "ended"; } } class FakeSender { readonly writes: RTCRtpSendParameters[] = []; private readonly rejectWrites: boolean; private readonly supported: boolean; constructor(rejectWrites = false, supported = true) { this.rejectWrites = rejectWrites; this.supported = supported; } getParameters(): RTCRtpSendParameters { if (!this.supported) throw new Error("getParameters unsupported"); return { encodings: [{}], degradationPreference: "balanced", headerExtensions: [], codecs: [], rtcp: { cname: "fake", reducedSize: false }, transactionId: "fake", }; } async setParameters(value: RTCRtpSendParameters): Promise { if (this.rejectWrites) throw new DOMException("unsupported", "NotSupportedError"); this.writes.push(structuredClone(value)); } } class FakeStream { readonly tracks: FakeTrack[]; constructor(tracks: FakeTrack[]) { this.tracks = tracks; } getTracks(): MediaStreamTrack[] { return this.tracks as unknown as MediaStreamTrack[]; } getVideoTracks(): MediaStreamTrack[] { return this.getTracks(); } } class FakeVideo { autoplay = true; muted = false; srcObject: MediaProvider | null = null; } class FakePeer { localDescription: RTCSessionDescription | null = null; remoteDescription: RTCSessionDescription | null = null; connectionState: RTCPeerConnectionState = "new"; onicecandidate: ((this: RTCPeerConnection, ev: RTCPeerConnectionIceEvent) => unknown) | null = null; ontrack: ((this: RTCPeerConnection, ev: RTCTrackEvent) => unknown) | null = null; onconnectionstatechange: ((this: RTCPeerConnection, ev: Event) => unknown) | null = null; readonly added: MediaStreamTrack[] = []; readonly remote: RTCSessionDescriptionInit[] = []; readonly ice: (RTCIceCandidateInit | null)[] = []; readonly transceivers: string[] = []; readonly senders: FakeSender[] = []; closed = 0; addTrack(track: MediaStreamTrack): RTCRtpSender { this.added.push(track); const sender = new FakeSender(); this.senders.push(sender); return sender as unknown as RTCRtpSender; } addTransceiver(kind: string): RTCRtpTransceiver { this.transceivers.push(kind); return {} as RTCRtpTransceiver; } async createOffer(): Promise { return { type: "offer", sdp: "presenter-offer" }; } async createAnswer(): Promise { return { type: "answer", sdp: "viewer-answer" }; } async setLocalDescription(value: RTCLocalSessionDescriptionInit): Promise { this.localDescription = value as RTCSessionDescription; } async setRemoteDescription(value: RTCSessionDescriptionInit): Promise { this.remote.push(value); this.remoteDescription = value as RTCSessionDescription; } async addIceCandidate(value?: RTCIceCandidateInit | null): Promise { this.ice.push(value ?? null); } close(): void { this.closed += 1; this.connectionState = "closed"; } connect(): void { this.connectionState = "connected"; this.onconnectionstatechange?.call(this as unknown as RTCPeerConnection, new Event("connectionstatechange")); } fail(): void { this.connectionState = "failed"; this.onconnectionstatechange?.call(this as unknown as RTCPeerConnection, new Event("connectionstatechange")); } receive(track: FakeTrack, stream: FakeStream): void { this.ontrack?.call(this as unknown as RTCPeerConnection, { track: track as unknown as MediaStreamTrack, streams: [stream as unknown as MediaStream], } as unknown as RTCTrackEvent); } } class FakeTimers implements RemoteMediaTimers { pending = new Map void>(); next = 1; setTimeout(callback: () => void): ReturnType { const id = this.next++; this.pending.set(id, callback); return id as unknown as ReturnType; } clearTimeout(handle: ReturnType): void { this.pending.delete(handle as unknown as number); } run(): void { const first = this.pending.entries().next().value as [number, () => void] | undefined; if (!first) throw new Error("no reconnect timer"); this.pending.delete(first[0]); first[1](); } } function access(role: "presenter" | "viewer", participantId: string, sessionId = "share-session"): ScreenShareAccessGrant { return { credential: { sessionId, participantId, role, grantToken: `private-${role}-grant` }, issuedAtMs: NOW - 1_000, expiresAtMs: NOW + 60_000, }; } function grant( type: "screen-share-create-grant" | "screen-share-join-grant" | "screen-share-resume-grant", requestId: string, role: "presenter" | "viewer", participants: readonly ScreenShareParticipant[], ) { return { type, protocolVersion: 1, sequence: 1, timestampMs: NOW, requestId, binding: BINDING, grant: access(role, `${role}-opaque`), nextClientSequence: 2, participants, ...(type === "screen-share-resume-grant" ? { continuous: true } : {}), }; } function iceGrant(requestId: string) { return { type: "ice-config-grant", protocolVersion: 1, requestId, issuedAtMs: NOW - 1_000, expiresAtMs: NOW + 50_000, iceServers: [{ urls: ["turns:relay.example.test:5349"], username: "temporary-user-01", credential: "temporary-password-01", credentialType: "password", }], }; } function iceResponse(input: RequestInfo | URL, body: Record): Response | null { return String(input).endsWith("/ice") ? Response.json(iceGrant(String(body.requestId))) : null; } function sse(messages: readonly unknown[]): Response { const bytes = new TextEncoder().encode(messages.map((message) => `data: ${JSON.stringify(message)}\n\n`).join("")); return new Response(new ReadableStream({ start(controller) { controller.enqueue(bytes); /* held open like the real fetch stream */ }, }), { status: 200, headers: { "Content-Type": "text/event-stream" } }); } function decision(optedIn = true): MediaAuthorizationDecision { return { authorized: true, optedIn, canView: optedIn, reason: optedIn ? "ready" : "opt_in_required", surface: { ...BINDING, status: "presenting", source: optedIn ? { kind: "live-stream", locator: "opaque", privacy: "private", hasAudio: false, autoplay: false, muted: true, } : null, playback: { autoplay: false, muted: true }, }, }; } async function settle(): Promise { await Promise.resolve(); await Promise.resolve(); await new Promise((resolve) => setTimeout(resolve, 0)); } describe("screen share sender policy", () => { it("caps a 4K sender at the explicit 720p/15 bandwidth contract", async () => { const track = new FakeTrack(); const sender = new FakeSender(); const result = await applyScreenShareEncodingPolicy( track as unknown as MediaStreamTrack, sender as unknown as RTCRtpSender, ); assert.deepEqual(result, { contentHintApplied: true, senderParametersApplied: true }); assert.equal(track.contentHint, "detail"); assert.equal(DEFAULT_SCREEN_SHARE_ENCODING_POLICY.maximumWidth, 1_280); assert.equal(DEFAULT_SCREEN_SHARE_ENCODING_POLICY.maximumHeight, 720); assert.deepEqual(sender.writes[0]?.encodings, [{ maxBitrate: 1_500_000, maxFramerate: 15, scaleResolutionDownBy: 3, }]); assert.equal(sender.writes[0]?.degradationPreference, "maintain-resolution"); }); it("falls back without throwing when a browser rejects sender parameters", async () => { const track = new FakeTrack(); const rejected = await applyScreenShareEncodingPolicy( track as unknown as MediaStreamTrack, new FakeSender(true) as unknown as RTCRtpSender, ); assert.deepEqual(rejected, { contentHintApplied: true, senderParametersApplied: false }); const unsupported = await applyScreenShareEncodingPolicy( track as unknown as MediaStreamTrack, {} as RTCRtpSender, ); assert.deepEqual(unsupported, { contentHintApplied: true, senderParametersApplied: false }); }); }); describe("remote office media presenter", () => { it("requires opt-in, publishes one caller track, and keeps credentials in authenticated POST bodies", async () => { const presenterTrack = new FakeTrack(); const stream = new FakeStream([presenterTrack]); const video = new FakeVideo(); const peers: FakePeer[] = []; const calls: Array<{ url: string; init: RequestInit; body: Record }> = []; const viewerPeer = { participantId: "viewer-opaque", role: "viewer" as const }; const fetcher: typeof fetch = async (input, init = {}) => { const body = JSON.parse(String(init.body)) as Record; calls.push({ url: String(input), init, body }); const ice = iceResponse(input, body); if (ice) return ice; if (String(input).endsWith("/join")) { return Response.json(grant("screen-share-create-grant", String(body.requestId), "presenter", []), { status: 201 }); } if (String(input).endsWith("/events")) { return sse([grant("screen-share-resume-grant", String(body.requestId), "presenter", [viewerPeer])]); } return new Response(null, { status: 204 }); }; const media = createRemoteOfficeMedia({ role: "presenter", binding: BINDING, authenticatedFetch: fetcher, now: () => NOW, peerConnectionFactory: () => { const peer = new FakePeer(); peers.push(peer); return peer as unknown as RTCPeerConnection; }, }); await assert.rejects( media.startPresenter({ consent: { authorized: true, optedIn: false }, stream: stream as unknown as MediaStream, video: video as unknown as HTMLVideoElement }), /explicit opt-in/, ); await media.startPresenter({ consent: { authorized: true, optedIn: true }, stream: stream as unknown as MediaStream, video: video as unknown as HTMLVideoElement }); assert.equal(media.state().status, "live", "an empty hosted room is ready for its first viewer"); await settle(); assert.equal(peers.length, 1); assert.equal(peers[0]?.added[0], presenterTrack as unknown as MediaStreamTrack); assert.equal(peers[0]?.senders[0]?.writes[0]?.encodings[0]?.maxFramerate, 15); assert.equal(video.autoplay, false); assert.equal(video.muted, true); const signal = calls.find((call) => call.url.endsWith("/signal")); const joinIndex = calls.findIndex((call) => call.url.endsWith("/join")); const iceIndex = calls.findIndex((call) => call.url.endsWith("/ice")); assert.ok(joinIndex >= 0 && iceIndex > joinIndex, "signaling authorization precedes TURN exchange"); assert.deepEqual(calls[iceIndex]?.body, { type: "ice-config-request", protocolVersion: 1, requestId: calls[iceIndex]?.body.requestId, binding: BINDING, credential: access("presenter", "presenter-opaque").credential, }); assert.equal((signal?.body.signal as { kind?: string }).kind, "sdp"); assert.equal(signal?.init.credentials, "same-origin"); assert.equal(calls.some((call) => call.url.includes("private-presenter-grant")), false); assert.equal(JSON.stringify(media.state()).includes("private-presenter-grant"), false); await media.stop(); assert.equal(presenterTrack.stopped, 0, "caller-owned capture track survives stop"); assert.equal(presenterTrack.contentHint, "", "caller-owned track metadata is restored on stop"); assert.equal(peers[0]?.closed, 1); }); it("applies the bounded sender policy independently to late-joining viewers", async () => { const first = { participantId: "viewer-first", role: "viewer" as const }; const late = { participantId: "viewer-late", role: "viewer" as const }; const peers: FakePeer[] = []; const fetcher: typeof fetch = async (input, init = {}) => { const body = JSON.parse(String(init.body)) as Record; const ice = iceResponse(input, body); if (ice) return ice; if (String(input).endsWith("/join")) { return Response.json(grant("screen-share-create-grant", String(body.requestId), "presenter", [first]), { status: 201 }); } if (String(input).endsWith("/events")) { return sse([ grant("screen-share-resume-grant", String(body.requestId), "presenter", [first]), { type: "screen-share-participants", protocolVersion: 1, sequence: 2, timestampMs: NOW + 1, sessionId: "share-session", binding: BINDING, participants: [first, late], leaseExpiresAtMs: NOW + 60_000, }, ]); } return new Response(null, { status: 204 }); }; const track = new FakeTrack(); const media = createRemoteOfficeMedia({ role: "presenter", binding: BINDING, authenticatedFetch: fetcher, now: () => NOW, peerConnectionFactory: () => { const peer = new FakePeer(); peers.push(peer); return peer as unknown as RTCPeerConnection; }, }); await media.startPresenter({ consent: { authorized: true, optedIn: true }, stream: new FakeStream([track]) as unknown as MediaStream, video: new FakeVideo() as unknown as HTMLVideoElement, }); await settle(); assert.equal(peers.length, 2); for (const peer of peers) { assert.equal(peer.added[0], track as unknown as MediaStreamTrack); assert.equal(peer.senders[0]?.writes.length, 1); assert.equal(peer.senders[0]?.writes[0]?.encodings[0]?.maxBitrate, 1_500_000); assert.equal(peer.senders[0]?.writes[0]?.encodings[0]?.scaleResolutionDownBy, 3); } await media.stop(); assert.equal(track.stopped, 0); assert.equal(track.contentHint, ""); }); it("tears down failed peers and rebuilds them from a resumed participant snapshot", async () => { const timers = new FakeTimers(); const peers: FakePeer[] = []; const iceTokens: string[] = []; let eventCalls = 0; const participant = { participantId: "viewer-opaque", role: "viewer" as const }; const fetcher: typeof fetch = async (input, init = {}) => { const body = JSON.parse(String(init.body)) as Record; const ice = iceResponse(input, body); if (ice) { iceTokens.push((body.credential as { grantToken: string }).grantToken); return ice; } if (String(input).endsWith("/join")) return Response.json(grant("screen-share-create-grant", String(body.requestId), "presenter", [participant]), { status: 201 }); if (String(input).endsWith("/events")) { eventCalls += 1; const resumed = grant("screen-share-resume-grant", String(body.requestId), "presenter", [participant]); resumed.grant.credential.grantToken = `rotated-reconnect-${eventCalls}`; return sse([resumed]); } return new Response(null, { status: 204 }); }; const media = createRemoteOfficeMedia({ role: "presenter", binding: BINDING, fetch: fetcher, timers, now: () => NOW, peerConnectionFactory: () => { const peer = new FakePeer(); peers.push(peer); return peer as unknown as RTCPeerConnection; }, }); const track = new FakeTrack(); await media.startPresenter({ consent: { authorized: true, optedIn: true }, stream: new FakeStream([track]) as unknown as MediaStream, video: new FakeVideo() as unknown as HTMLVideoElement }); await settle(); assert.deepEqual(iceTokens, ["private-presenter-grant"], "connected peer keeps its active TURN allocation across token rotation"); peers[0]?.fail(); assert.equal(media.state().status, "reconnecting"); assert.equal(peers[0]?.closed, 1); timers.run(); await settle(); assert.equal(peers.length, 2); assert.equal(peers[1]?.added[0], track as unknown as MediaStreamTrack); assert.deepEqual(iceTokens, ["private-presenter-grant", "rotated-reconnect-2"], "rebuilt peer refreshes TURN under the current rotated grant"); await media.dispose(); assert.equal(track.stopped, 0); }); it("does not resurrect a session when stop overtakes a slow join", async () => { let resolveJoin: ((response: Response) => void) | null = null; let joinRequestId = ""; const fetcher: typeof fetch = async (input, init = {}) => { const body = JSON.parse(String(init.body)) as Record; const ice = iceResponse(input, body); if (ice) return ice; if (String(input).endsWith("/join")) { joinRequestId = String(body.requestId); return new Promise((resolve) => { resolveJoin = resolve; }); } return new Response(null, { status: 204 }); }; const track = new FakeTrack(); const media = createRemoteOfficeMedia({ role: "presenter", binding: BINDING, fetch: fetcher, now: () => NOW }); const starting = media.startPresenter({ consent: { authorized: true, optedIn: true }, stream: new FakeStream([track]) as unknown as MediaStream, video: new FakeVideo() as unknown as HTMLVideoElement, }); await media.stop(); assert.ok(resolveJoin); (resolveJoin as (response: Response) => void)(Response.json( grant("screen-share-create-grant", joinRequestId, "presenter", []), { status: 201 }, )); await assert.rejects(starting, /superseded/); assert.equal(media.state().status, "stopped"); assert.equal(media.state().sessionId, null); assert.equal(track.stopped, 0); }); it("renews its lease before expiry and uses only the rotated in-memory grant", async () => { const timers = new FakeTimers(); const eventTokens: string[] = []; const iceTokens: string[] = []; let eventCalls = 0; const fetcher: typeof fetch = async (input, init = {}) => { const body = JSON.parse(String(init.body)) as Record; const ice = iceResponse(input, body); if (ice) { iceTokens.push((body.credential as { grantToken: string }).grantToken); return ice; } if (String(input).endsWith("/join")) { return Response.json(grant("screen-share-create-grant", String(body.requestId), "presenter", []), { status: 201 }); } if (String(input).endsWith("/events")) { eventCalls += 1; const credential = body.credential as { grantToken: string }; eventTokens.push(credential.grantToken); const resumed = grant("screen-share-resume-grant", String(body.requestId), "presenter", []); resumed.grant.credential.grantToken = `rotated-${eventCalls}`; return sse([resumed]); } return new Response(null, { status: 204 }); }; const media = createRemoteOfficeMedia({ role: "presenter", binding: BINDING, fetch: fetcher, timers, now: () => NOW, peerConnectionFactory: () => new FakePeer() as unknown as RTCPeerConnection, }); await media.startPresenter({ consent: { authorized: true, optedIn: true }, stream: new FakeStream([new FakeTrack()]) as unknown as MediaStream, video: new FakeVideo() as unknown as HTMLVideoElement, }); await settle(); assert.deepEqual(eventTokens, ["private-presenter-grant"]); assert.deepEqual(iceTokens, ["private-presenter-grant"], "initial resume reuses active TURN configuration"); assert.equal(timers.pending.size, 1, "one pre-expiry renewal is scheduled"); timers.run(); await settle(); assert.deepEqual(eventTokens, ["private-presenter-grant", "rotated-1"]); assert.deepEqual(iceTokens, ["private-presenter-grant"], "lease renewal does not issue unused TURN credentials"); assert.equal(JSON.stringify(media.state()).includes("rotated-2"), false); await media.dispose(); }); it("creates no peer before grant-bound ICE succeeds and cleans up a failed hosted join", async () => { let peerCount = 0; const urls: string[] = []; const fetcher: typeof fetch = async (input, init = {}) => { const url = String(input); urls.push(url); const body = JSON.parse(String(init.body)) as Record; if (url.endsWith("/join")) { return Response.json(grant("screen-share-create-grant", String(body.requestId), "presenter", [ { participantId: "viewer-opaque", role: "viewer" }, ]), { status: 201 }); } if (url.endsWith("/ice")) return Response.json({ type: "ice-config-unavailable", protocolVersion: 1, requestId: body.requestId, retryAfterMs: 10_000, }, { status: 503 }); return new Response(null, { status: 204 }); }; const track = new FakeTrack(); const media = createRemoteOfficeMedia({ role: "presenter", binding: BINDING, fetch: fetcher, now: () => NOW, peerConnectionFactory: () => { peerCount += 1; return new FakePeer() as unknown as RTCPeerConnection; }, }); await assert.rejects(media.startPresenter({ consent: { authorized: true, optedIn: true }, stream: new FakeStream([track]) as unknown as MediaStream, video: new FakeVideo() as unknown as HTMLVideoElement, }), /temporarily unavailable/); assert.equal(peerCount, 0); assert.deepEqual(urls.map((url) => url.split("/").at(-1)), ["join", "ice", "leave"]); assert.equal(track.stopped, 0); assert.equal(media.state().status, "idle"); }); }); describe("remote office media viewer", () => { it("accepts a ready decision only, answers offers, and stops receiver tracks on revoke", async () => { const presenterPeer = { participantId: "presenter-opaque", role: "presenter" as const }; const peers: FakePeer[] = []; const calls: Array<{ url: string; body: Record }> = []; const fetcher: typeof fetch = async (input, init = {}) => { const body = JSON.parse(String(init.body)) as Record; calls.push({ url: String(input), body }); const iceConfig = iceResponse(input, body); if (iceConfig) return iceConfig; if (String(input).endsWith("/join")) return Response.json(grant("screen-share-join-grant", String(body.requestId), "viewer", [presenterPeer]), { status: 201 }); if (String(input).endsWith("/events")) { const resume = grant("screen-share-resume-grant", String(body.requestId), "viewer", [presenterPeer]); const ice = { type: "screen-share-signal-relay", protocolVersion: 1, sequence: 2, timestampMs: NOW + 1, sessionId: "share-session", binding: BINDING, fromParticipantId: "presenter-opaque", targetParticipantId: "viewer-opaque", signal: { kind: "ice-candidate", candidate: "candidate:remote", sdpMid: "0", sdpMLineIndex: 0, usernameFragment: null }, }; const relay = { type: "screen-share-signal-relay", protocolVersion: 1, sequence: 3, timestampMs: NOW + 2, sessionId: "share-session", binding: BINDING, fromParticipantId: "presenter-opaque", targetParticipantId: "viewer-opaque", signal: { kind: "sdp", descriptionType: "offer", sdp: "presenter-offer" }, }; return sse([resume, ice, relay]); } return new Response(null, { status: 204 }); }; const media = createRemoteOfficeMedia({ role: "viewer", binding: BINDING, authenticatedFetch: fetcher, now: () => NOW, peerConnectionFactory: () => { const peer = new FakePeer(); peers.push(peer); return peer as unknown as RTCPeerConnection; }, }); const video = new FakeVideo(); await assert.rejects(media.startViewer({ decision: decision(false), video: video as unknown as HTMLVideoElement }), /ready live-stream/); await media.startViewer({ viewerOptIn: true, video: video as unknown as HTMLVideoElement }); await settle(); assert.equal(peers[0]?.transceivers[0], "video"); assert.deepEqual(peers[0]?.remote[0], { type: "offer", sdp: "presenter-offer" }); assert.equal(peers[0]?.ice[0]?.candidate, "candidate:remote", "early ICE waits for remote SDP"); assert.ok(calls.some((call) => call.url.endsWith("/signal") && (call.body.signal as { descriptionType?: string }).descriptionType === "answer")); const remoteTrack = new FakeTrack(); const remoteStream = new FakeStream([remoteTrack]); peers[0]?.receive(remoteTrack, remoteStream); assert.equal(video.srcObject, remoteStream as unknown as MediaStream); await media.revoke(); assert.equal(remoteTrack.stopped, 1); assert.equal(video.srcObject, null); assert.equal(media.state().status, "revoked"); }); it("forgets its grant when the server has already stopped the session", async () => { const presenterPeer = { participantId: "presenter-opaque", role: "presenter" as const }; let emitEvent: (value: Uint8Array) => void = () => { throw new Error("event stream is not open"); }; let leaveCalls = 0; const fetcher: typeof fetch = async (input, init = {}) => { const body = JSON.parse(String(init.body)) as Record; const ice = iceResponse(input, body); if (ice) return ice; if (String(input).endsWith("/join")) { return Response.json(grant("screen-share-join-grant", String(body.requestId), "viewer", [presenterPeer]), { status: 201 }); } if (String(input).endsWith("/events")) { const stream = new ReadableStream({ start(controller) { emitEvent = (value) => controller.enqueue(value); } }); const resume = grant("screen-share-resume-grant", String(body.requestId), "viewer", [presenterPeer]); emitEvent(new TextEncoder().encode(`data: ${JSON.stringify(resume)}\n\n`)); return new Response(stream, { status: 200, headers: { "Content-Type": "text/event-stream" } }); } if (String(input).endsWith("/leave")) leaveCalls += 1; return new Response(null, { status: 204 }); }; const media = createRemoteOfficeMedia({ role: "viewer", binding: BINDING, authenticatedFetch: fetcher, now: () => NOW, peerConnectionFactory: () => new FakePeer() as unknown as RTCPeerConnection, }); await media.startViewer({ viewerOptIn: true, video: new FakeVideo() as unknown as HTMLVideoElement }); await settle(); emitEvent(new TextEncoder().encode(`data: ${JSON.stringify({ type: "screen-share-stopped", protocolVersion: 1, sequence: 2, timestampMs: NOW + 1, sessionId: "share-session", binding: BINDING, reason: "presenter-stopped", })}\n\n`)); await settle(); assert.equal(media.state().status, "stopped"); assert.equal(media.state().sessionId, null); await media.dispose(); assert.equal(leaveCalls, 0, "cleanup does not reuse a server-invalidated capability"); }); });