Tags: hoophq/hoop
Tags
DEP-138: move expired-credential session cleanup off the read paths (#… …1708) * DEP-138: move expired-credential session cleanup off the read paths GET /api/sessions opened with models.CloseExpiredCredentialSessions() before parsing a single query param. That made a read mutate, and the query it ran was not scoped to the caller's org: a read for one tenant closed sessions for every tenant on the deployment, as 1+2N serial statements with no bound on N and every per-row error discarded. The work now runs in gateway/jobs/credentialsweeper, a 15s ticker started from main.go, and the model function is a single set-based statement: FOR UPDATE SKIP LOCKED so replicas take disjoint slices, and a LIMIT so a backlog drains over several ticks instead of in one long transaction that pins the xmin horizon. Migration 000109 adds a partial index matching the sweep predicate. Removed from all four call sites. The two credential handlers lose nothing: CreateConnectionCredentials already closes the prior session in issueOrRefreshCredential, and ResumeConnectionCredentials' 410 gate is backstopped by its own expire_at check. On the GETs the only delta is timing — a session reports done within 15s of expiry rather than whenever anyone next hit one of those endpoints. * fix: Sweeper runs uncancelled * chore: renumber the credential sweep index migration to 000110
fix(rdp): bound onnxruntime threads in the OCR sidecar (+ benchmark t… …ooling) (#1658) * fix(rdp): bound onnxruntime thread pools in the OCR sidecar Left at ONNX Runtime's default, every InferenceSession sizes its intra-op thread pool to the host's logical core count. The sidecar creates many sessions per uvicorn worker — the RapidOCR engine (det/rec/cls) plus one fixed-shape session per recognition and detection bucket, ~19 in total — and runs WEB_CONCURRENCY workers. That product is fine on a small node but explodes on a high-core host: on a 192-core GPU box it demands tens of thousands of threads and the sidecar dies during warmup with "pthread_create failed, error code: 11" before serving a single request. Bound both pools and apply them at every session the server creates: the engine (via EngineConfig.onnxruntime.*), the per-bucket detection sessions, and the fixed-shape fp16 recognition sessions. The GPU does the heavy compute and serving throughput comes from concurrent workers, not intra-op parallelism inside one inference, so a small pool is sufficient as well as necessary. Defaults (intra 4 / inter 1) are overridable per deployment via OCR_INTRA_OP_THREADS / OCR_INTER_OP_THREADS. Also make the CUDA arena growth strategy configurable in the same config block (OCR_CUDA_ARENA_STRATEGY, default unchanged at kNextPowerOfTwo). The default rounds each arena up to a power of two, which inflates per-worker VRAM (~4.8GB on an 80GB card) and limits how many workers fit; kSameAsRequested trades allocation frequency for a smaller footprint on VRAM-constrained GPUs. It shares the session-construction paths touched above, so it is not separable into its own commit. 🤖 Generated with Mister Maluco Co-Authored-By: MisterMal <teskeslab@lucasteske.dev> * feat(rdpbench): add -concurrency and -compare-url to ocrbench ocrbench could only drive the OCR server serially, which measures per-request latency but not what the gateway analyzer actually does: it issues up to 8 concurrent chunk requests, so sustained throughput under concurrency is the number that decides capacity. -concurrency N dispatches the sampled band states across N workers and reports aggregate throughput at that level. Band extraction stays serial (compositing mutates a shared framebuffer in event order); each band is copied out so the parallel phase reads immutable buffers, and the latency stats are mutex-guarded. -compare-url cross-checks a second OCR server against the first and reports tokens the primary DROPPED relative to the reference, flagging PII-shaped ones (>=4 digits or '@') separately from UI-chrome noise. Word order and whitespace are ignored — a token-based PII scan does not care, but a lost token is a potential leak. This is what verified an fp16 recognition runtime does not lose PII that fp32 catches. 🤖 Generated with Mister Maluco Co-Authored-By: MisterMal <teskeslab@lucasteske.dev> * chore(rdp): add aarch64/CUDA13 OCR sidecar image for Blackwell (sm_121) Dockerfile.rapidocr-gpu-aarch64 builds the OCR PoC sidecar for aarch64 + CUDA 13 + GB10 (Grace-Blackwell, sm_121) — the DGX Spark. It is a separate file rather than a base-image swap because the x86 GPU image is fundamentally incompatible with that platform: - No aarch64 + CUDA 13 onnxruntime-gpu wheel is published on PyPI, so the wheel must be built from source for this arch. - The ORT 1.20 line the x86 images pin does not compile against CUDA 13 at all (CCCL removed thrust::unary_function); GB10 needs >= 1.24.4, so this image cannot reuse that pin. - GB10 is compute capability 12.1, so the build needs CMAKE_CUDA_ARCHITECTURES=121 — a stock sm_80/86/90 wheel lacks the kernel images and silently falls back to CPU. Because it installs a source-built wheel rather than resolving onnxruntime-gpu from PyPI, this image is unaffected by the 1.20.1 yank handled in #1657. The header documents producing the wheel. CUDA 13 + cuDNN 9 come from the base image, so unlike the x86 file there are no explicit nvidia-*-cu12 pins. fp16 recognition models are converted at build time, mirroring Dockerfile.agent-ocr-gpu. 🤖 Generated with Mister Maluco Co-Authored-By: MisterMal <teskeslab@lucasteske.dev> * chore: ignore bare cmd binaries dropped in the repo root `go build ./gateway/cmd/rdpbench/` (without -o) writes the binary to the current directory, so a routine build from the repo root leaves a ~44MB executable sitting next to the source — easy to stage by accident. Ignore the four root-level names our main packages produce. They are listed explicitly rather than matched by pattern because such a pattern would be indiscriminate: ./gateway is deliberately absent, since ignoring it would shadow the gateway module directory. (That build is harmless anyway — Go refuses to overwrite an existing directory.) 🤖 Generated with Mister Maluco Co-Authored-By: MisterMal <teskeslab@lucasteske.dev> * fix(rdpbench): keep '@' through token normalization in compare mode normalizeToken stripped every non-alphanumeric character, so "a@b.com" and "ab.com" both normalized to "abcom". A candidate engine that dropped the '@' out of an email therefore compared equal to the reference, was never reported as a dropped token, and never reached the PII-shaped classifier — silently missing exactly the kind of loss the cross-check exists to catch. The underlying invariant is that any character the classifier keys on must survive normalization. Express it directly: piiMarkerRune now defines that set, normalizeToken preserves it alongside alphanumerics, and the classifier tests membership with the same predicate instead of its own literal '@', so the two cannot drift apart. Adds ocrbench_test.go covering normalizeToken (case, separator punctuation, digits, empty/non-ascii) and droppedTokens (multiset semantics, order and whitespace insensitivity, reporting in original form), including a regression case for the email above and a guard asserting no marker rune is ever stripped. Also route both /healthz probes through a probeHealth helper that surfaces read errors, rather than discarding them and benchmarking on against a banner that silently came back empty. 🤖 Generated with Mister Maluco Co-Authored-By: MisterMal <teskeslab@lucasteske.dev> * fix(rdp): apply the CUDA arena strategy to the engine's sessions too OCR_CUDA_ARENA_STRATEGY was documented as covering every session's GPU memory pool but was only passed to the manually built bucket det/rec sessions. The RapidOCR engine constructs its own det/rec/cls CUDA sessions, which kept the built-in default, making the knob quietly partial: it changed the bucket sessions' footprint but not the engine's. rapidocr exposes the setting at EngineConfig.onnxruntime.cuda_ep_cfg .arena_extend_strategy, so plumb it there rather than weakening the documented contract. The engine's sessions hold VRAM even in fp16 mode — where BucketDet/BucketRec serve the actual inferences — so they count toward the per-worker footprint the knob exists to control. Document the new runtime knobs where the sidecar's others already live: the Dockerfile.agent-ocr-gpu env reference block and the gcp-gpu-agent env sample. They are consumed by the OCR container, not the gateway, so they do not belong in the root .env.sample (which carries only the gateway-side RDP_OCR_SERVER_URL). 🤖 Generated with Mister Maluco Co-Authored-By: MisterMal <teskeslab@lucasteske.dev> * chore: never commit rdpbench framebuffer snapshots `rdpbench run -dump <dir>` writes each analyzed frame to snapshot-NNNN.png — full-canvas decodes of the recorded RDP session, i.e. the same sensitive screen content .gitignore already refuses to accept in /recording.json, except directly viewable rather than encoded in a fixture. Only the fixture was protected, so a dump left in the tree could be picked up by a wide `git add`. The dump directory is supplied by the caller and can be created anywhere in the repo, so match the tool's own filename rather than guessing directory names. Verified the pattern catches dumps at the root and nested, and does not shadow the tracked README assets (backedby.png, github.png). 🤖 Generated with Mister Maluco Co-Authored-By: MisterMal <teskeslab@lucasteske.dev> --------- Co-authored-by: MisterMal <teskeslab@lucasteske.dev>
chore(deps): alcatraz v0.16.0 for the Brazilian identifiers (#1700) The six recognizers this branch carried locally -- CNS, título eleitoral, RENAVAM, CEP, plate and PIX key -- are upstream as of hoophq/alcatraz#29, so the local copy goes and the pin moves v0.7.0 -> v0.16.0. Upstream also owns the checksum helpers the local copy had duplicated, and picked up two fixes from review there: the PIX gate no longer accepts a bare "chave" (Portuguese for "key", which matched ordinary primary-key columns), and both context-gated patterns score above a realistic caller threshold instead of being dropped despite their gate passing. Verified on the bumped module: 54 entity types, all six BR types detected, and the false-positive guards hold -- "chave primaria <uuid>" and a plain eight-digit order id stay unmasked. Generated with Mister Maluco Co-authored-by: MisterMal <teskeslab@lucasteske.dev>
feat(hoopinspect): defer rule verdicts to OPA, rewrite the SQL scanner ( #1704) * add: lexer sql for improvements and make the ai session analyzer use opa * docs: improvements * fix some docs * SQL server support on hoop proxy (17,19,22, latest...) (#1705) * add: mssql encrypt_off flag for tls * add: fix docs * add: fix docs * add: mssql 2019 support and run envoy stack overy 2017, 2019, 2022 and latest * add: fix operations hoopinspector
fix(mcp): add tag_selector filter to connections_list so tags are fil… …terable (#1706) * add: fix(mcp): filter connections_list by tag_selector instead of legacy tags array * fix(mcp): add tag_selector filter to connections_list, deprecate legacy tags * fix(models): keep every tag_selector constraint and tolerate blank segments
fix: bound terminal output rendering and handle draft quota (EVL-121) (… …#1703) * fix(webclient): bound terminal output rendering and handle draft quota (EVL-121) The web terminal froze on large command output. The gateway returns up to sessionwal.DefaultMaxRead (110MB) and the log area rendered all of it into a single unvirtualized `whitespace-pre` node, producing an element wide enough to exceed browser layout limits: blank viewport, multi-million-pixel scroll width and a pinned main thread on any scroll. Reproduced by holding the input constant and varying only the output. The same 15,728,646-byte input against `bash -c 'wc -c'` (small output) renders fine; against plain `bash`, which echoes the payload back as "command not found" on stderr, it froze the tab. Output size is independent of input size, so an input cap does not address this. - log_area/logs.cljs: cap what reaches the DOM at 512KB and surface a callout pointing at the output menu, which already routes payloads above its own threshold to the backend download flow. The untruncated string stays in the app db, so downloads and copy are unaffected. The screen reader label now derives from the bounded string instead of running split-lines over the whole payload on every render. - log_area/main.cljs: only run papaparse + js->clj when the connection type can actually surface the Tabular tab or the CSV/JSON downloads that consume the matrix. Non-database connections were paying a full-payload scan on every render for a result nothing reads. - panel.cljs: catch the QuotaExceededError that localStorage raises above ~5MB. The uncaught throw skipped the status reset and left the footer spinner running forever, reading as a hang. Adds a :not-saved state that says so, and warns that a reload restores the previous draft, since setItem is atomic and the stale value survives a rejected write. * fix(webclient): lower output render cap to 512KB (EVL-121) Interactive testing showed 1MB still degrades scroll on the pathological single-unwrapped-line shape. Layout is only half the cost — repaint of a very wide node slows down well before Chrome's 2^25 clamp, which the headless layout benchmark does not capture. 512KB stays responsive. * style(webclient): make output truncation callout full width (EVL-121)
EVL-185: onboarding signup origin survey (#1701) Adds the onboarding "How did you hear about Hoop?" survey: two nullable columns on private.users, POST /users/self/signup-origin with a first-answer-wins conditional update, show_origin_survey on /userinfo derived from users.created_at, and a floating survey widget in webapp_v2. The answer is also emitted to Segment as hoop-onboarding-origin-answered, without the free text.
PreviousNext