Whisper (CT 311) and Ollama (CT 102) share one A2000. Before loading
Whisper on CUDA, ask Ollama to unload its models (GET /api/ps then POST
/api/generate keep_alive:0) and wait for the card to clear, so the GPU
load has headroom. Best-effort and stdlib-only; Ollama reloads
cooperatively, and the existing CUDA->CPU fallback covers any failure.
Toggle via OLLAMA_FREE_BEFORE_STT; endpoint via OLLAMA_URL.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cuda_available() only covers "no GPU present". On a shared card the GPU
can exist but fail to load the model (VRAM exhausted by another process
e.g. Ollama). Try CUDA first, fall back to a CPU model on any load
error instead of crashing the transcription job. Supports HA portability
(node without GPU) and a contended GPU. Adds GPU-path + fallback tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two real findings from the security reviewer:
1. urllib auto-follows 3xx redirects via the default HTTPRedirectHandler.
The previous code's hop loop never ran — urllib silently followed.
Replaced with http.client + a manual hop loop. Every hop re-runs
_validate_url, so an open-redirect to 127.0.0.1 / RFC1918 / metadata
gets caught on the second hop.
2. DNS TOCTOU — _resolve() validated but urllib.request re-resolved on
connect. Now the connection is pinned to the validated IP via a
PinnedHTTPConn / PinnedHTTPSConn subclass that overrides connect() to
bind socket.create_connection to (addr, port). For HTTPS, TLS
server_hostname is set to the original host so SNI + cert
verification still work against the named host while the TCP
destination is the pinned IP.
Tests added: redirect-to-loopback short-circuits at validation;
too-many-redirects exhausts max_hops; 2xx returns body; non-2xx raises.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fetches upstream URL via safe_fetch, sha256-diffs against the prior
body_sha stored in metadata, updates body_text + last_synced only when
content changed. Unchanged syncs just touch last_synced.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirrors lib/ingest/safe_fetch.js. Same scheme + IP-range checks and
VOID_INGEST_ALLOW_PRIVATE env gate. Used by sync.source_doc and any
future Python workers that fetch user-controlled URLs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The url passed to yt-dlp is user-controllable (via /api/capture). Any
string starting with '-' would be parsed as a flag (e.g.
--config-location=/etc/passwd). Mitigations:
1. Validate scheme is http(s) and hostname is present before subprocess.
2. Pass `--` to yt-dlp so it stops flag parsing before the positional
URL.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
yt-dlp pulls metadata (title, description, uploader, thumbnail) and
bestaudio (opus). faster-whisper transcribes; audio file removed after.
Creates a refs row with kind='video' and source_kind='youtube' for
YouTube URLs, generic 'video' otherwise. Idempotent on
sha256(space_id + url) via refs.external_id.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pdftotext first; falls back to per-page pdftoppm rasterization +
Tesseract OCR when the extracted text is < 200 chars. Updates
refs.body_text + metadata.extract.{method,chars} via the repo shim;
audit entry emitted with actor_kind='worker'.
born_digital.pdf fixture padded so pdftotext yields > 200 chars and
the test exercises the pdftotext path, not the OCR fallback.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the Boss class — SELECT … FOR UPDATE SKIP LOCKED to atomically
claim, UPDATE state on completion. Retry semantics match pg-boss:
exponential backoff via retry_count / retry_delay / retry_backoff.
Forces client_encoding=UTF8 on every connection. The void2-db cluster
was initialized as SQL_ASCII so psycopg refuses to decode text by
default; UTF8 client_encoding works because the data is already UTF-8.
Node's pg lib is more forgiving and didn't surface this.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Plan 4 Phase A scaffolding. void-workers package at /workers/, sibling
of /lib/. pyproject.toml pins Python 3.12 with separate extras for
pdf / image / video / test.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>