Files
SnapOtter/docs/superpowers/specs/2026-04-04-http-compatibility-design.md
T
Siddharth Kumar Sah 04c2461390 docs: add design spec for HTTP/non-secure context compatibility
Addresses issues #4 and #5 - crypto.randomUUID() and
navigator.clipboard.writeText() fail over plain HTTP on
non-localhost addresses, breaking all tool operations.
2026-04-04 16:14:41 +08:00

3.6 KiB

Fix HTTP/Non-Secure Context Compatibility

Date: 2026-04-04 Issues: #4, #5

Problem

crypto.randomUUID() and navigator.clipboard.writeText() are secure-context-only Web APIs. They throw or are undefined when the app is accessed over plain HTTP on non-localhost addresses (e.g., http://192.168.1.x:1349). This breaks all tool operations and copy-to-clipboard functionality.

Stirling-Image is a self-hosted tool where many users deploy on NAS/homelab devices over plain HTTP. This must be a first-class supported deployment mode.

Solution

Part 1: generateId() utility

Add to apps/web/src/lib/utils.ts:

export function generateId(): string {
  const bytes = new Uint8Array(16);
  crypto.getRandomValues(bytes);
  bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
  bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10
  const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}

Produces a standard UUID v4 string using crypto.getRandomValues(), which is available in all modern browsers regardless of secure context.

6 call sites replaced (crypto.randomUUID() -> generateId()):

File Line Purpose
apps/web/src/hooks/use-tool-processor.ts 91 Single-file job correlation ID
apps/web/src/hooks/use-tool-processor.ts 260 Batch job correlation ID
apps/web/src/components/tools/ocr-settings.tsx 52 OCR job ID
apps/web/src/components/tools/erase-object-settings.tsx 52 Erase object job ID
apps/web/src/components/tools/pipeline-builder.tsx 104 Pipeline step ID
apps/web/src/pages/automate-page.tsx 147 Automation step ID

Part 2: copyToClipboard() utility

Add to apps/web/src/lib/utils.ts:

export async function copyToClipboard(text: string): Promise<boolean> {
  try {
    await navigator.clipboard.writeText(text);
    return true;
  } catch {
    return false;
  }
}

Returns true/false so callers can decide whether to show a "Copied!" confirmation. On HTTP, the call fails silently and the UI does not show the confirmation. No behavior change for HTTPS/localhost users.

4 call sites replaced (navigator.clipboard.writeText() -> copyToClipboard()):

File Line Current error handling
apps/web/src/components/settings/settings-dialog.tsx 1148 None - needs wrapping
apps/web/src/components/tools/color-palette-settings.tsx 47 Has try-catch, simplify
apps/web/src/components/tools/ocr-settings.tsx 120 None - needs wrapping
apps/web/src/components/tools/barcode-read-settings.tsx 48 Has try-catch, simplify

What does NOT change

  • Server-side code: Node's crypto.randomUUID() works fine outside browsers
  • vitest.config.ts: Runs in Node, not a browser
  • No new dependencies added
  • No config file changes

Testing

  • pnpm typecheck - verify all imports resolve
  • pnpm lint - verify Biome formatting
  • pnpm test - catch regressions
  • Manual: access over HTTP on a non-localhost address, confirm tools work

Alternatives considered

  1. uuid npm package - rejected, adds a dependency for 5 lines of code that does the same thing internally
  2. Try-catch fallback wrapper (try native randomUUID(), fall back to getRandomValues()) - rejected, two code paths for zero meaningful performance benefit