Compare commits

...

2 Commits

Author SHA1 Message Date
Markus Hartung
235235f31c fix(video): register the drill-down consumer test with stryker and document the principal-digest hash (#11655) 2026-08-29 07:18:00 -03:00
Markus Hartung
806280ec2c feat(video): connect tenant-bound drill-down lifecycle and multiresolution variants (#11655)
Add a lifecycle layer on top of the existing Video Bridge drill-down cache
substrate: opaque hashed handles bound to (principal, handle) with no
existence oracle, preview/standard/detail multiresolution variants resampled
on read, response pagination capped at 8 frames and 32 MiB, TTL/quota/delete/
cleanup, and a new authenticated /api/v1/video-bridge/drilldown consumer
route that stays disabled for remote access by default.

Refs #11655
2026-08-29 06:44:22 -03:00
6 changed files with 1048 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache
substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail
multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB,
and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for
remote access by default and denies cross-key access with the same response as a nonexistent
handle (no existence oracle).

View File

@@ -0,0 +1,155 @@
import { z } from "zod";
import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
import { enforceApiKeyPolicy } from "@/shared/utils/apiKeyPolicy";
import { extractApiKey, isValidApiKey } from "@/sse/services/auth";
import { buildErrorBody } from "@omniroute/open-sse/utils/error";
import {
isVideoBridgeDrilldownRemoteAccessEnabled,
VIDEO_DRILLDOWN_VARIANTS,
VideoDrilldownLifecycle,
type VideoDrilldownVariant,
} from "@/lib/guardrails/videoBridgeDrilldownLifecycle";
import { VideoDrilldownCache } from "@/lib/guardrails/videoBridgeDrilldown";
export const dynamic = "force-dynamic";
export const revalidate = 0;
// A single process-wide lifecycle backs both this authenticated consumer route and any
// future in-process producer (the video bridge guardrail, once it threads a per-request
// opt-in flag through). It is intentionally a *different* cache instance from the internal
// loopback broker route's — this route never accepts raw frames from a remote caller, only
// opaque handles minted by an in-process producer, so there is no reason to share state with
// the loopback-only extraction broker surface.
const sharedLifecycle = new VideoDrilldownLifecycle({
cache: new VideoDrilldownCache({
maxEntries: 64,
maxEntriesPerPrincipal: 16,
maxBytesPerPrincipal: 64 * 1024 * 1024,
maxTotalBytes: 256 * 1024 * 1024,
ttlMs: 10 * 60 * 1000,
}),
});
const HandleSchema = z
.string()
.regex(/^[0-9a-f]{64}$/, "handle must be an opaque 64-character hex value");
const VariantSchema = z.enum(VIDEO_DRILLDOWN_VARIANTS as [VideoDrilldownVariant, ...VideoDrilldownVariant[]]);
const BoundedIntSchema = z
.string()
.regex(/^\d{1,9}$/)
.transform(Number);
const NonNegativeNumberSchema = z
.string()
.refine((value) => value.length > 0 && value.length <= 64 && Number.isFinite(Number(value)))
.transform(Number)
.refine((value) => value >= 0);
const ReadQuerySchema = z
.object({
end: NonNegativeNumberSchema.optional(),
frames: BoundedIntSchema.pipe(z.number().int().min(1).max(8)).optional(),
handle: HandleSchema,
page: BoundedIntSchema.pipe(z.number().int().min(0)).optional(),
start: NonNegativeNumberSchema.optional(),
variant: VariantSchema.optional(),
})
.strict();
const DeleteQuerySchema = z.object({ handle: HandleSchema }).strict();
function queryRecord(searchParams: URLSearchParams): Record<string, string> {
const values: Record<string, string> = {};
for (const [key, value] of searchParams) values[key] = value;
return values;
}
function corsJson(status: number, body: unknown): Response {
return new Response(JSON.stringify(body), {
status,
headers: { ...CORS_HEADERS, "Content-Type": "application/json", "Cache-Control": "no-store" },
});
}
function corsError(status: number, message: string, type: string): Response {
return corsJson(status, buildErrorBody(status, message, null, { type }));
}
export interface VideoBridgeDrilldownRouteDependencies {
isRemoteAccessEnabled?: () => boolean;
lifecycle?: VideoDrilldownLifecycle;
}
async function resolvePrincipal(
request: Request
): Promise<{ error?: Response; principalId?: string }> {
const apiKey = extractApiKey(request);
if (!apiKey) {
return { error: corsError(401, "Authentication is required", "authentication_required") };
}
if (!(await isValidApiKey(apiKey))) {
return { error: corsError(401, "The provided API key is invalid", "authentication_required") };
}
const policy = await enforceApiKeyPolicy(request, null);
if (policy.rejection) return { error: policy.rejection };
const principalId = policy.apiKeyInfo?.id;
if (!principalId) {
return { error: corsError(401, "Authentication is required", "authentication_required") };
}
return { principalId };
}
export const OPTIONS = async (): Promise<Response> => handleCorsOptions();
export async function handleVideoBridgeDrilldownConsumerRequest(
request: Request,
dependencies: VideoBridgeDrilldownRouteDependencies = {}
): Promise<Response> {
const isRemoteAccessEnabled =
dependencies.isRemoteAccessEnabled ?? isVideoBridgeDrilldownRemoteAccessEnabled;
if (!isRemoteAccessEnabled()) {
return corsError(
403,
"Video Bridge drill-down remote access is disabled",
"feature_disabled"
);
}
if (request.method !== "GET" && request.method !== "DELETE") {
return corsError(405, "Method not allowed", "invalid_request");
}
const resolved = await resolvePrincipal(request);
if (resolved.error) return resolved.error;
const principalId = resolved.principalId!;
const lifecycle = dependencies.lifecycle ?? sharedLifecycle;
const url = new URL(request.url);
const query = queryRecord(url.searchParams);
if (request.method === "DELETE") {
const parsed = DeleteQuerySchema.safeParse(query);
if (!parsed.success) return corsError(400, "A valid drill-down handle is required", "invalid_request");
const removed = lifecycle.deleteHandle(principalId, parsed.data.handle);
return corsJson(200, { removed });
}
const parsed = ReadQuerySchema.safeParse(query);
if (!parsed.success) return corsError(400, "Invalid Video Bridge drill-down query", "invalid_request");
const page = await lifecycle.resolve(principalId, parsed.data.handle, {
endSeconds: parsed.data.end,
frameCount: parsed.data.frames,
page: parsed.data.page,
startSeconds: parsed.data.start,
variant: parsed.data.variant,
});
if (!page) {
return corsError(404, "Video Bridge drill-down result was not found", "not_found");
}
return corsJson(200, page);
}
export async function GET(request: Request): Promise<Response> {
return handleVideoBridgeDrilldownConsumerRequest(request);
}
export async function DELETE(request: Request): Promise<Response> {
return handleVideoBridgeDrilldownConsumerRequest(request);
}

View File

@@ -0,0 +1,401 @@
// Video Bridge drill-down production/consumption lifecycle (FU-08, #11655).
//
// `videoBridgeDrilldown.ts` is the secure cache substrate: it stores frames keyed by
// (principalId, sessionId, videoRef) and already refuses cross-principal reads/deletes
// without revealing whether an entry exists. This module adds the missing lifecycle on
// top of it, without touching the frozen-shape substrate file:
// - opaque hashed handles, so a consumer never needs (and never sees) the raw
// sessionId/videoRef the substrate indexes by — both are minted server-side here;
// - preview/standard/detail multiresolution variants, resampled on read (never stored
// more than once, so producing stays "zero overhead" beyond the opt-in call itself);
// - response pagination capped at 8 frames and a bounded response-byte budget;
// - a small handle registry with its own TTL/quota, cleaned up alongside the cache.
import { createHash, randomUUID } from "node:crypto";
import sharp from "sharp";
import {
VIDEO_DRILLDOWN_MAX_ENTRY_BYTES,
VideoDrilldownCache,
type VideoDrilldownCacheOptions,
type VideoDrilldownFrame,
type VideoDrilldownPutValue,
type VideoDrilldownResult,
} from "./videoBridgeDrilldown";
export type VideoDrilldownVariant = "preview" | "standard" | "detail";
export const VIDEO_DRILLDOWN_VARIANTS: readonly VideoDrilldownVariant[] = [
"preview",
"standard",
"detail",
];
export interface VideoDrilldownVariantPreset {
/** Frames are only ever shrunk toward this ceiling, never upscaled. */
maxDimension: number;
/** Default page size for this variant when the caller does not ask for a specific count. */
defaultPageFrames: number;
}
export const VIDEO_DRILLDOWN_VARIANT_PRESETS: Record<
VideoDrilldownVariant,
VideoDrilldownVariantPreset
> = {
preview: { maxDimension: 320, defaultPageFrames: 3 },
standard: { maxDimension: 640, defaultPageFrames: 6 },
detail: { maxDimension: 1280, defaultPageFrames: 8 },
};
export const VIDEO_DRILLDOWN_MAX_PAGE_FRAMES = 8;
export const VIDEO_DRILLDOWN_MAX_PAGE_BYTES = VIDEO_DRILLDOWN_MAX_ENTRY_BYTES;
const HANDLE_PATTERN = /^[0-9a-f]{64}$/;
function truthyFlag(value: string | undefined): boolean {
if (!value) return false;
const normalized = value.trim().toLowerCase();
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
}
/** Producing drill-down artifacts stays off unless an operator opts the deployment in. */
export function isVideoBridgeDrilldownProductionEnabled(
env: Record<string, string | undefined> = process.env
): boolean {
return truthyFlag(env.OMNIROUTE_VIDEO_BRIDGE_DRILLDOWN_ENABLED);
}
/** Remote (authenticated API-key) consumption stays off unless an operator opts in. */
export function isVideoBridgeDrilldownRemoteAccessEnabled(
env: Record<string, string | undefined> = process.env
): boolean {
return truthyFlag(env.OMNIROUTE_VIDEO_BRIDGE_DRILLDOWN_REMOTE_ENABLED);
}
interface HandleEntry {
createdAt: number;
expiresAt: number;
principalId: string;
principalKey: string;
sessionId: string;
videoRef: string;
}
export interface VideoDrilldownProducePayload {
derivation: VideoDrilldownPutValue["derivation"];
durationSeconds: number;
frames: readonly VideoDrilldownPutValue["frames"][number][];
}
export interface VideoDrilldownProduceOptions {
signal?: AbortSignal;
}
export interface VideoDrilldownProduceResult {
expiresAt: number;
handle: string;
}
export interface VideoDrilldownResolveQuery {
endSeconds?: number;
frameCount?: number;
/** Test/defense-in-depth override; production callers use the exported constant. */
maxPageBytes?: number;
page?: number;
startSeconds?: number;
variant?: VideoDrilldownVariant;
}
export interface VideoDrilldownPage {
derivation: VideoDrilldownResult["derivation"];
durationSeconds: number;
focusWindow?: VideoDrilldownResult["focusWindow"];
frames: VideoDrilldownFrame[];
hasMore: boolean;
page: number;
variant: VideoDrilldownVariant;
}
export interface VideoDrilldownUsage {
bytes: number;
entries: number;
totalBytes: number;
totalEntries: number;
}
export interface VideoDrilldownLifecycleOptions {
cache: VideoDrilldownCache;
maxHandles?: number;
maxHandlesPerPrincipal?: number;
now?: () => number;
/** Handle bookkeeping TTL. Defaults to the cache's own TTL when it is exposed via `ttlMs`. */
ttlMs?: number;
}
/**
* Derives the per-principal bookkeeping key from an API-key RECORD ID (`apiKeyInfo.id`),
* never from the secret itself. SHA-256 is the correct primitive here and a password KDF
* would be wrong: this value is a deterministic index key that must be recomputable on
* every lookup, not a stored credential verifier. Same construction and rationale as
* `src/lib/db/apiKeys.ts` and `videoBridgeDrilldown.ts`; CodeQL/semgrep flag the
* `sha256(<something reached from an apiKey-shaped source>)` shape generically and cannot
* see that the input is an opaque row id (issue #11655, Hard Rule #14).
*/
function principalDigest(principalId: string): string {
// nosemgrep: insufficient-password-hash
return createHash("sha256").update(principalId, "utf8").digest("hex");
}
function frameDataUriBytes(frame: VideoDrilldownFrame): number {
const commaIndex = frame.dataUri.indexOf(",");
const encoded = commaIndex >= 0 ? frame.dataUri.slice(commaIndex + 1) : "";
return Math.floor((encoded.length * 3) / 4);
}
async function shrinkFrameForVariant(
frame: VideoDrilldownFrame,
preset: VideoDrilldownVariantPreset
): Promise<VideoDrilldownFrame> {
if (frame.width <= preset.maxDimension && frame.height <= preset.maxDimension) return frame;
const commaIndex = frame.dataUri.indexOf(",");
const source = Buffer.from(frame.dataUri.slice(commaIndex + 1), "base64");
const resized = await sharp(source)
.resize({
fit: "inside",
height: preset.maxDimension,
width: preset.maxDimension,
withoutEnlargement: true,
})
.jpeg({ progressive: false })
.toBuffer();
const metadata = await sharp(resized).metadata();
return {
dataUri: `data:image/jpeg;base64,${resized.toString("base64")}`,
height: metadata.height ?? frame.height,
timestampSeconds: frame.timestampSeconds,
width: metadata.width ?? frame.width,
};
}
function pageFrameSlice(
frames: readonly VideoDrilldownFrame[],
page: number,
requestedFrameCount: number | undefined,
variant: VideoDrilldownVariant
): { hasMore: boolean; slice: readonly VideoDrilldownFrame[] } {
const pageSize = Math.max(
1,
Math.min(
requestedFrameCount ?? VIDEO_DRILLDOWN_VARIANT_PRESETS[variant].defaultPageFrames,
VIDEO_DRILLDOWN_MAX_PAGE_FRAMES
)
);
const start = Math.max(0, page) * pageSize;
const slice = frames.slice(start, start + pageSize);
return { hasMore: start + slice.length < frames.length, slice };
}
function trimToByteBudget(
frames: readonly VideoDrilldownFrame[],
maxBytes: number
): { frames: VideoDrilldownFrame[]; trimmed: boolean } {
const kept: VideoDrilldownFrame[] = [];
let total = 0;
for (const frame of frames) {
const bytes = frameDataUriBytes(frame);
if (kept.length > 0 && total + bytes > maxBytes) break;
kept.push(frame);
total += bytes;
}
// A single oversized frame is still returned alone rather than producing an empty page —
// the underlying cache already enforces the 32 MiB per-entry ceiling, so this only trims
// multi-frame pages down.
return { frames: kept.length > 0 ? kept : frames.slice(0, 1), trimmed: kept.length < frames.length };
}
/**
* Lifecycle wrapper around {@link VideoDrilldownCache}: mints opaque handles at produce
* time, resolves/deletes strictly by (principal, handle), and applies multiresolution
* variants + pagination at read time.
*/
export class VideoDrilldownLifecycle {
private readonly cache: VideoDrilldownCache;
private readonly handles = new Map<string, HandleEntry>();
private readonly handleCountByPrincipal = new Map<string, number>();
private readonly maxHandles: number;
private readonly maxHandlesPerPrincipal: number;
private readonly now: () => number;
private readonly ttlMs: number;
constructor(options: VideoDrilldownLifecycleOptions) {
this.cache = options.cache;
this.now = options.now ?? Date.now;
this.ttlMs = options.ttlMs ?? 10 * 60 * 1000;
this.maxHandles = options.maxHandles ?? 4096;
this.maxHandlesPerPrincipal = options.maxHandlesPerPrincipal ?? 64;
}
private sweepExpiredHandles(): number {
const now = this.now();
let removed = 0;
for (const [handle, entry] of this.handles) {
if (entry.expiresAt <= now) {
this.dropHandle(handle, entry);
removed += 1;
}
}
return removed;
}
private dropHandle(handle: string, entry: HandleEntry): void {
this.handles.delete(handle);
const count = this.handleCountByPrincipal.get(entry.principalKey) ?? 0;
if (count <= 1) this.handleCountByPrincipal.delete(entry.principalKey);
else this.handleCountByPrincipal.set(entry.principalKey, count - 1);
}
/**
* A handle evicted for quota is unreachable forever (its digest can never be re-derived
* without the minted sessionId/videoRef), so its cache entry must be released here too —
* otherwise it would sit as unreclaimable, invisible quota usage until TTL expiry.
*/
private releaseEvictedHandle(handle: string, entry: HandleEntry): void {
this.cache.clearSession(entry.principalId, entry.sessionId);
this.dropHandle(handle, entry);
}
private evictOldestHandleForPrincipal(principalKey: string): void {
for (const [handle, entry] of this.handles) {
if (entry.principalKey === principalKey) {
this.releaseEvictedHandle(handle, entry);
return;
}
}
}
private evictOldestHandleGlobally(): void {
const oldest = this.handles.keys().next().value;
if (!oldest) return;
const entry = this.handles.get(oldest);
if (entry) this.releaseEvictedHandle(oldest, entry);
}
private registerHandle(principalId: string, sessionId: string, videoRef: string): string {
const principalKey = principalDigest(principalId);
const createdAt = this.now();
const handle = createHash("sha256")
.update(principalKey, "utf8")
.update(":", "utf8")
.update(sessionId, "utf8")
.update(":", "utf8")
.update(videoRef, "utf8")
.digest("hex");
this.handles.set(handle, {
createdAt,
expiresAt: createdAt + this.ttlMs,
principalId,
principalKey,
sessionId,
videoRef,
});
const count = (this.handleCountByPrincipal.get(principalKey) ?? 0) + 1;
this.handleCountByPrincipal.set(principalKey, count);
while ((this.handleCountByPrincipal.get(principalKey) ?? 0) > this.maxHandlesPerPrincipal) {
const before = this.handles.size;
this.evictOldestHandleForPrincipal(principalKey);
if (this.handles.size === before) break;
}
while (this.handles.size > this.maxHandles) {
const before = this.handles.size;
this.evictOldestHandleGlobally();
if (this.handles.size === before) break;
}
return handle;
}
private resolveHandleEntry(principalId: string, handle: string): HandleEntry | null {
if (!HANDLE_PATTERN.test(handle)) return null;
this.sweepExpiredHandles();
const entry = this.handles.get(handle);
if (!entry || entry.principalKey !== principalDigest(principalId)) return null;
return entry;
}
async produce(
principalId: string,
value: VideoDrilldownProducePayload,
options: VideoDrilldownProduceOptions = {}
): Promise<VideoDrilldownProduceResult> {
this.sweepExpiredHandles();
const sessionId = randomUUID();
const videoRef = randomUUID();
await this.cache.put(principalId, sessionId, videoRef, value as VideoDrilldownPutValue, {
signal: options.signal,
});
const handle = this.registerHandle(principalId, sessionId, videoRef);
const entry = this.handles.get(handle);
return { expiresAt: entry?.expiresAt ?? this.now() + this.ttlMs, handle };
}
async resolve(
principalId: string,
handle: string,
query: VideoDrilldownResolveQuery
): Promise<VideoDrilldownPage | null> {
const entry = this.resolveHandleEntry(principalId, handle);
if (!entry) return null;
const stored = this.cache.get(principalId, entry.sessionId, entry.videoRef, {
endSeconds: query.endSeconds,
frameCount: 16,
startSeconds: query.startSeconds,
});
if (!stored) {
this.dropHandle(handle, entry);
return null;
}
const variant = query.variant ?? "detail";
const preset = VIDEO_DRILLDOWN_VARIANT_PRESETS[variant];
const { slice, hasMore } = pageFrameSlice(
stored.frames,
query.page ?? 0,
query.frameCount,
variant
);
const shrunk = await Promise.all(slice.map((frame) => shrinkFrameForVariant(frame, preset)));
const { frames, trimmed } = trimToByteBudget(shrunk, query.maxPageBytes ?? VIDEO_DRILLDOWN_MAX_PAGE_BYTES);
return {
derivation: stored.derivation,
durationSeconds: stored.durationSeconds,
...(stored.focusWindow ? { focusWindow: stored.focusWindow } : {}),
frames,
hasMore: hasMore || trimmed,
page: Math.max(0, query.page ?? 0),
variant,
};
}
deleteHandle(principalId: string, handle: string): number {
const entry = this.resolveHandleEntry(principalId, handle);
if (!entry) return 0;
const removed = this.cache.clearSession(principalId, entry.sessionId);
this.dropHandle(handle, entry);
return removed;
}
/** Explicit sweep for scheduled cleanup; returns the number of stale handles reclaimed. */
cleanup(): number {
return this.sweepExpiredHandles();
}
getUsage(principalId: string): VideoDrilldownUsage {
return this.cache.getUsage(principalId);
}
clearAll(): void {
this.cache.clearAll();
this.handles.clear();
this.handleCountByPrincipal.clear();
}
}
export type { VideoDrilldownCacheOptions };

View File

@@ -393,6 +393,7 @@
"tests/unit/usage-service-hardening.test.ts",
"tests/unit/validate-response-quality.test.ts",
"tests/unit/vertex-passthrough-model-lockout.test.ts",
"tests/unit/video-bridge-drilldown-consumer-route.test.ts",
"tests/unit/video-bridge-drilldown-route.test.ts",
"tests/unit/video-bridge-route-security.test.ts",
"tests/unit/xai-agent-tools-passthrough.test.ts",

View File

@@ -0,0 +1,271 @@
import assert from "node:assert/strict";
import test from "node:test";
import sharp from "sharp";
import { VideoDrilldownCache } from "../../../src/lib/guardrails/videoBridgeDrilldown";
import {
isVideoBridgeDrilldownProductionEnabled,
isVideoBridgeDrilldownRemoteAccessEnabled,
VIDEO_DRILLDOWN_MAX_PAGE_FRAMES,
VIDEO_DRILLDOWN_VARIANT_PRESETS,
VideoDrilldownLifecycle,
} from "../../../src/lib/guardrails/videoBridgeDrilldownLifecycle";
const derivation = {
parentContentHash: `sha256:${"a".repeat(64)}`,
policy: "focused-window",
version: "video-drilldown/v1",
} as const;
const jpegFixtures = new Map<string, Buffer>();
async function jpeg(width: number, height: number, fill: number): Promise<Buffer> {
const key = `${width}x${height}x${fill}`;
const cached = jpegFixtures.get(key);
if (cached) return cached;
const buffer = await sharp({
create: { width, height, channels: 3, background: { r: fill, g: fill, b: fill } },
})
.jpeg({ progressive: false })
.toBuffer();
jpegFixtures.set(key, buffer);
return buffer;
}
async function jpegDataUri(width: number, height: number, fill = 1): Promise<string> {
const buffer = await jpeg(width, height, fill);
return `data:image/jpeg;base64,${buffer.toString("base64")}`;
}
function framesInput(
entries: ReadonlyArray<{ dataUri: string; timestampSeconds: number }>
): { derivation: typeof derivation; durationSeconds: number; frames: typeof entries } {
return { derivation, durationSeconds: 20, frames: entries };
}
test("video bridge drill-down feature flags default to opt-in / disabled", () => {
assert.equal(isVideoBridgeDrilldownProductionEnabled({}), false);
assert.equal(isVideoBridgeDrilldownProductionEnabled({ OMNIROUTE_VIDEO_BRIDGE_DRILLDOWN_ENABLED: "true" }), true);
assert.equal(isVideoBridgeDrilldownRemoteAccessEnabled({}), false);
assert.equal(
isVideoBridgeDrilldownRemoteAccessEnabled({
OMNIROUTE_VIDEO_BRIDGE_DRILLDOWN_REMOTE_ENABLED: "1",
}),
true
);
});
test("produce mints an opaque hashed handle that reveals no session/video identity", async () => {
const lifecycle = new VideoDrilldownLifecycle({ cache: new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 }) });
const { handle, expiresAt } = await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(640, 360), timestampSeconds: 1 }])
);
assert.match(handle, /^[0-9a-f]{64}$/);
assert.ok(expiresAt > Date.now());
});
test("resolve returns stored frames to the owning principal by handle only", async () => {
const lifecycle = new VideoDrilldownLifecycle({ cache: new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 }) });
const { handle } = await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(640, 360), timestampSeconds: 3 }])
);
const page = await lifecycle.resolve("principal-a", handle, {});
assert.ok(page);
assert.equal(page?.frames.length, 1);
assert.equal(page?.variant, "detail");
assert.equal(page?.hasMore, false);
});
test("resolve denies a wrong principal and a nonexistent handle identically (no oracle)", async () => {
const lifecycle = new VideoDrilldownLifecycle({ cache: new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 }) });
const { handle } = await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(640, 360), timestampSeconds: 3 }])
);
const wrongPrincipal = await lifecycle.resolve("principal-b", handle, {});
const madeUpHandle = await lifecycle.resolve("principal-b", "f".repeat(64), {});
assert.equal(wrongPrincipal, null);
assert.equal(madeUpHandle, null);
const deniedDelete = lifecycle.deleteHandle("principal-b", handle);
const madeUpDelete = lifecycle.deleteHandle("principal-b", "f".repeat(64));
assert.equal(deniedDelete, 0);
assert.equal(madeUpDelete, 0);
const ownerStillWorks = await lifecycle.resolve("principal-a", handle, {});
assert.ok(ownerStillWorks);
});
test("delete removes the artifact for its own principal and is idempotent", async () => {
const lifecycle = new VideoDrilldownLifecycle({ cache: new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 }) });
const { handle } = await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(640, 360), timestampSeconds: 3 }])
);
assert.equal(lifecycle.deleteHandle("principal-a", handle), 1);
assert.equal(await lifecycle.resolve("principal-a", handle, {}), null);
assert.equal(lifecycle.deleteHandle("principal-a", handle), 0);
});
test("preview and standard variants shrink frames without ever upscaling", async () => {
const lifecycle = new VideoDrilldownLifecycle({ cache: new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 }) });
const big = await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(1280, 720), timestampSeconds: 1 }])
);
const small = await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(200, 100), timestampSeconds: 2 }])
);
const preview = await lifecycle.resolve("principal-a", big.handle, { variant: "preview" });
assert.ok(preview);
for (const frame of preview?.frames ?? []) {
assert.ok(frame.width <= VIDEO_DRILLDOWN_VARIANT_PRESETS.preview.maxDimension);
assert.ok(frame.height <= VIDEO_DRILLDOWN_VARIANT_PRESETS.preview.maxDimension);
}
// The already-small 200x100 frame must not be upscaled toward the preview ceiling.
const smallPreview = await lifecycle.resolve("principal-a", small.handle, { variant: "preview" });
const smallFrame = smallPreview?.frames[0];
assert.deepEqual(
smallFrame && { height: smallFrame.height, width: smallFrame.width },
{ height: 100, width: 200 }
);
const standard = await lifecycle.resolve("principal-a", big.handle, { variant: "standard" });
for (const frame of standard?.frames ?? []) {
assert.ok(frame.width <= VIDEO_DRILLDOWN_VARIANT_PRESETS.standard.maxDimension);
assert.ok(frame.height <= VIDEO_DRILLDOWN_VARIANT_PRESETS.standard.maxDimension);
}
const detail = await lifecycle.resolve("principal-a", big.handle, { variant: "detail" });
const detailBig = detail?.frames[0];
assert.deepEqual(
detailBig && { height: detailBig.height, width: detailBig.width },
{ height: 720, width: 1280 }
);
});
test("pagination never exceeds the 8-frame page cap and reports hasMore across pages", async () => {
const lifecycle = new VideoDrilldownLifecycle({ cache: new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 }) });
const entries = [] as Array<{ dataUri: string; timestampSeconds: number }>;
for (let index = 0; index < 12; index += 1) {
entries.push({ dataUri: await jpegDataUri(64, 64, (index % 250) + 1), timestampSeconds: index });
}
const { handle } = await lifecycle.produce("principal-a", framesInput(entries.slice(0, 16)));
const firstPage = await lifecycle.resolve("principal-a", handle, { frameCount: 100 });
assert.ok(firstPage);
assert.ok((firstPage?.frames.length ?? 0) <= VIDEO_DRILLDOWN_MAX_PAGE_FRAMES);
assert.equal(firstPage?.hasMore, entries.length > VIDEO_DRILLDOWN_MAX_PAGE_FRAMES);
const secondPage = await lifecycle.resolve("principal-a", handle, { page: 1 });
assert.ok(secondPage);
const seenTimestamps = new Set([
...(firstPage?.frames.map((frame) => frame.timestampSeconds) ?? []),
...(secondPage?.frames.map((frame) => frame.timestampSeconds) ?? []),
]);
assert.equal(seenTimestamps.size, entries.length);
});
test("a tight page byte budget trims frames instead of exceeding it", async () => {
const lifecycle = new VideoDrilldownLifecycle({
cache: new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 }),
});
const frameBytes = (await jpeg(320, 180, 5)).byteLength;
const { handle } = await lifecycle.produce(
"principal-a",
framesInput([
{ dataUri: await jpegDataUri(320, 180, 5), timestampSeconds: 1 },
{ dataUri: await jpegDataUri(320, 180, 5), timestampSeconds: 2 },
{ dataUri: await jpegDataUri(320, 180, 5), timestampSeconds: 3 },
])
);
const tight = await lifecycle.resolve("principal-a", handle, {
maxPageBytes: Math.floor(frameBytes * 1.5),
});
assert.ok(tight);
assert.equal(tight?.frames.length, 1);
assert.equal(tight?.hasMore, true);
});
test("TTL expiry hides the artifact and cleanup reclaims the handle", async () => {
let now = 1_000;
const lifecycle = new VideoDrilldownLifecycle({
cache: new VideoDrilldownCache({ maxEntries: 8, now: () => now, ttlMs: 5_000 }),
now: () => now,
ttlMs: 5_000,
});
const { handle } = await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(320, 180), timestampSeconds: 1 }])
);
now += 10_000;
// cleanup() alone (no prior resolve/delete call) must reclaim the stale handle.
const removed = lifecycle.cleanup();
assert.ok(removed >= 1);
assert.equal(await lifecycle.resolve("principal-a", handle, {}), null);
assert.equal(lifecycle.deleteHandle("principal-a", handle), 0);
});
test("per-principal and global usage accounting is delegated to the cache substrate", async () => {
const cache = new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 });
const lifecycle = new VideoDrilldownLifecycle({ cache });
assert.deepEqual(lifecycle.getUsage("principal-a"), {
bytes: 0,
entries: 0,
totalBytes: 0,
totalEntries: 0,
});
await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(320, 180), timestampSeconds: 1 }])
);
const usage = lifecycle.getUsage("principal-a");
assert.equal(usage.entries, 1);
assert.ok(usage.bytes > 0);
assert.equal(usage.totalEntries, 1);
});
test("aborting mid-production leaves no orphaned handle or cache usage", async () => {
const cache = new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 });
const lifecycle = new VideoDrilldownLifecycle({ cache });
const controller = new AbortController();
controller.abort();
const dataUri = await jpegDataUri(320, 180);
await assert.rejects(() =>
lifecycle.produce("principal-a", framesInput([{ dataUri, timestampSeconds: 1 }]), {
signal: controller.signal,
})
);
assert.deepEqual(lifecycle.getUsage("principal-a"), {
bytes: 0,
entries: 0,
totalBytes: 0,
totalEntries: 0,
});
});
test("per-principal handle quota evicts the oldest handle instead of growing without bound", async () => {
const lifecycle = new VideoDrilldownLifecycle({
cache: new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 }),
maxHandlesPerPrincipal: 2,
});
const first = await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(320, 180), timestampSeconds: 1 }])
);
await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(320, 180), timestampSeconds: 2 }])
);
await lifecycle.produce(
"principal-a",
framesInput([{ dataUri: await jpegDataUri(320, 180), timestampSeconds: 3 }])
);
assert.equal(await lifecycle.resolve("principal-a", first.handle, {}), null);
assert.equal(lifecycle.getUsage("principal-a").entries, 2);
});

View File

@@ -0,0 +1,214 @@
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import sharp from "sharp";
const TEST_DATA_DIR = fs.mkdtempSync(
path.join(os.tmpdir(), "omniroute-video-drilldown-consumer-route-")
);
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "video-drilldown-consumer-route-test-secret";
process.env.DISABLE_SQLITE_AUTO_BACKUP = "true";
const core = await import("../../src/lib/db/core.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const route = await import("../../src/app/api/v1/video-bridge/drilldown/route.ts");
const { VideoDrilldownLifecycle } = await import(
"../../src/lib/guardrails/videoBridgeDrilldownLifecycle.ts"
);
const { VideoDrilldownCache } = await import("../../src/lib/guardrails/videoBridgeDrilldown.ts");
const { isLocalOnlyPath } = await import("../../src/server/authz/routeGuard.ts");
async function resetStorage(): Promise<void> {
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.beforeEach(resetStorage);
test.after(() => {
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
async function seedKey(): Promise<{ key: string }> {
return apiKeysDb.createApiKey("video-drilldown-route-key", "test", []);
}
function newLifecycle() {
return new VideoDrilldownLifecycle({
cache: new VideoDrilldownCache({ maxEntries: 8, ttlMs: 60_000 }),
});
}
async function jpegDataUri(width: number, height: number, fill = 1): Promise<string> {
const buffer = await sharp({
create: { width, height, channels: 3, background: { r: fill, g: fill, b: fill } },
})
.jpeg({ progressive: false })
.toBuffer();
return `data:image/jpeg;base64,${buffer.toString("base64")}`;
}
const derivation = {
parentContentHash: `sha256:${"a".repeat(64)}`,
policy: "focused-window",
version: "video-drilldown/v1",
} as const;
function get(url: string, key?: string): Request {
return new Request(`http://omniroute.local${url}`, {
headers: key ? { Authorization: `Bearer ${key}` } : {},
});
}
function del(url: string, key?: string): Request {
return new Request(`http://omniroute.local${url}`, {
headers: key ? { Authorization: `Bearer ${key}` } : {},
method: "DELETE",
});
}
test("consumer route requires remote access to be explicitly enabled", async () => {
const { key } = await seedKey();
const response = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${"a".repeat(64)}`, key),
{ isRemoteAccessEnabled: () => false }
);
assert.equal(response.status, 403);
});
test("consumer route requires an authenticated API key", async () => {
const noKey = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${"a".repeat(64)}`),
{ isRemoteAccessEnabled: () => true }
);
assert.equal(noKey.status, 401);
const invalidKey = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${"a".repeat(64)}`, "not-a-real-key"),
{ isRemoteAccessEnabled: () => true }
);
assert.equal(invalidKey.status, 401);
});
test("consumer route resolves a produced handle for its own API key and paginates/variant-shapes it", async () => {
const { key } = await seedKey();
const lifecycle = newLifecycle();
const policyModule = await import("../../src/shared/utils/apiKeyPolicy.ts");
const policy = await policyModule.enforceApiKeyPolicy(get("/x", key), null);
const principalId = policy.apiKeyInfo!.id;
const { handle } = await lifecycle.produce(principalId, {
derivation,
durationSeconds: 10,
frames: [
{ dataUri: await jpegDataUri(640, 360), timestampSeconds: 1 },
{ dataUri: await jpegDataUri(640, 360), timestampSeconds: 2 },
],
});
const response = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${handle}&variant=preview`, key),
{ isRemoteAccessEnabled: () => true, lifecycle }
);
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.variant, "preview");
assert.equal(body.frames.length, 2);
for (const frame of body.frames) {
assert.ok(frame.width <= 320);
assert.ok(frame.height <= 320);
}
});
test("consumer route denies a different API key's handle with the same 404 as a made-up handle", async () => {
const { key: ownerKey } = await seedKey();
const { key: strangerKey } = await seedKey();
const lifecycle = newLifecycle();
const policyModule = await import("../../src/shared/utils/apiKeyPolicy.ts");
const ownerPolicy = await policyModule.enforceApiKeyPolicy(get("/x", ownerKey), null);
const { handle } = await lifecycle.produce(ownerPolicy.apiKeyInfo!.id, {
derivation,
durationSeconds: 10,
frames: [{ dataUri: await jpegDataUri(320, 180), timestampSeconds: 1 }],
});
const strangerRead = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${handle}`, strangerKey),
{ isRemoteAccessEnabled: () => true, lifecycle }
);
const madeUpRead = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${"0".repeat(64)}`, strangerKey),
{ isRemoteAccessEnabled: () => true, lifecycle }
);
assert.equal(strangerRead.status, 404);
assert.equal(madeUpRead.status, 404);
assert.deepEqual(await strangerRead.json(), await madeUpRead.json());
const strangerDelete = await route.handleVideoBridgeDrilldownConsumerRequest(
del(`/api/v1/video-bridge/drilldown?handle=${handle}`, strangerKey),
{ isRemoteAccessEnabled: () => true, lifecycle }
);
assert.equal(strangerDelete.status, 200);
assert.deepEqual(await strangerDelete.json(), { removed: 0 });
const ownerRead = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${handle}`, ownerKey),
{ isRemoteAccessEnabled: () => true, lifecycle }
);
assert.equal(ownerRead.status, 200);
});
test("consumer route deletes an owner's handle and it becomes unresolvable afterward", async () => {
const { key } = await seedKey();
const lifecycle = newLifecycle();
const policyModule = await import("../../src/shared/utils/apiKeyPolicy.ts");
const policy = await policyModule.enforceApiKeyPolicy(get("/x", key), null);
const { handle } = await lifecycle.produce(policy.apiKeyInfo!.id, {
derivation,
durationSeconds: 10,
frames: [{ dataUri: await jpegDataUri(320, 180), timestampSeconds: 1 }],
});
const deleted = await route.handleVideoBridgeDrilldownConsumerRequest(
del(`/api/v1/video-bridge/drilldown?handle=${handle}`, key),
{ isRemoteAccessEnabled: () => true, lifecycle }
);
assert.deepEqual(await deleted.json(), { removed: 1 });
const afterDelete = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${handle}`, key),
{ isRemoteAccessEnabled: () => true, lifecycle }
);
assert.equal(afterDelete.status, 404);
});
test("consumer route rejects malformed handles and out-of-range pagination before touching the lifecycle", async () => {
const { key } = await seedKey();
const badHandle = await route.handleVideoBridgeDrilldownConsumerRequest(
get("/api/v1/video-bridge/drilldown?handle=not-hex", key),
{ isRemoteAccessEnabled: () => true }
);
assert.equal(badHandle.status, 400);
const tooManyFrames = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${"a".repeat(64)}&frames=9`, key),
{ isRemoteAccessEnabled: () => true }
);
assert.equal(tooManyFrames.status, 400);
const badVariant = await route.handleVideoBridgeDrilldownConsumerRequest(
get(`/api/v1/video-bridge/drilldown?handle=${"a".repeat(64)}&variant=ultra`, key),
{ isRemoteAccessEnabled: () => true }
);
assert.equal(badVariant.status, 400);
});
test("consumer route is not classified local-only — it is a real remote-authenticated surface, gated by settings instead", () => {
assert.equal(isLocalOnlyPath("/api/v1/video-bridge/drilldown", "GET"), false);
});