Security hardening, retry unification, model allowlists, image-text-to-text, and Kubernetes deployment#14
Merged
eduardoworrel merged 9 commits intoFeb 25, 2026
Conversation
Add InputSanitizer with URL validation (blocks private/reserved IPs), filename sanitization (Path.GetFileName), and file size limits (100MB). Apply to SpeechToTextTaskHandler and configure Kestrel/FormOptions limits. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
S8: Validate base64 data-URL MIME type and audio magic bytes before
writing decoded content to disk (InputSanitizer).
S9: Replace permissive CORS (AllowCredentials + any origin) with
AllowAnyOrigin without credentials to prevent CSRF.
S11: Remove full payload logging from TaskBusinessLogic (result messages),
sanitize exception logs across TasksEndPoints, TaskSockets, and
AudioValidation to avoid leaking sensitive data.
S12: Add UseHttpsRedirection to WebSocket/Program.cs.
S13: Add rate limiter (50 req/min) to WebSocket service and apply
RequireRateLimiting to the ws/{id} route.
S14: Replace unbounded string concatenation with StringBuilder and
enforce 10 MB max message size per WebSocket frame accumulation,
closing with MessageTooBig on violation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…kQueue Both systems now share retry_count via PrivateArgs in Redis instead of independent in-memory counters, with a combined ceiling of 3 retries. Fixes: SessionTrackQueue registered in DI (was dead code), message deserialization uses Guid.TryParse matching DistributeQueue format, AddOrUpdate replaces TryAdd for retried task timers, task_completion published on success, Redis key deleted after completion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r detection
- Extract TaskRequest god file into separate handler classes per task type
(SpeechToTextTaskHandler, TextToSpeechTaskHandler, TranslationTaskHandler,
TextGenerationTaskHandler) with FieldsConfig, ITaskHandler, TaskHandlerFactory
- Extract StreamBuffer, SpeechToTextFormProcessor, TaskRequestFactory
- Add model/dtype allowlist validation in FieldsConfig and IsValidFields(),
rejecting arbitrary model strings at the API boundary (E2)
- Replace fragile string-matching error detection
(response.Contains("\"Status\":\"Error\"")) with JSON parsing via
JsonDocument, preventing false 500s on valid responses containing
"error" in their content (E1)
- Add CLAUDE.md, rules, agents, and skills for project documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…chunk buffers
Replace fire-and-forget Redis Pub/Sub with Redis Streams (XADD/XREADGROUP/XACK)
for all 7 pipeline channels, providing message durability and crash recovery.
Replace static ConcurrentDictionary chunk buffers in STT/TTS logic with
Redis Stream-based RedisChunkBuffer<T>, enabling horizontal scaling and
preventing memory leaks from incomplete tasks.
result_queue_{taskId} channels remain on Pub/Sub (ephemeral per-request).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…sult timeout D5: Replace broken ClaimPendingAsync (StreamPendingMessagesAsync with null consumer) with StreamAutoClaimAsync for correct crash recovery of orphaned messages in Redis Stream consumer groups. D6: Fix SessionTrackQueue dual-consumer restart — use linked CancellationToken with Task.WhenAny so that if either consumer crashes, both restart together instead of leaving one stream unprocessed indefinitely. D7: Add 3-minute safety-net timeout to AwaitTaskResultAsync via linked CancellationToken. Propagate HTTP cancellationToken from endpoint caller. Return HTTP 504 on TimeoutException instead of hanging indefinitely. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s (P9-P20) Remove 7 unused NuGet packages (FluentValidation, MediatR, NRedisStack, EF Core SqlServer/Design, JwtBearer, Serilog), delete dead files (TemplateWorker, Entity, TextGenerationRequestContract, TextGenerationSchemaFilter), remove unused model classes from AIModels.cs, extract duplicate retry_count parsing into PrivateArgsHelper, add 30s timeout to DistributeQueue node acquisition, update GitHub Actions to v4, add Docker resource limits, fix log message inconsistency, and remove unnecessary Task.Delay(1). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add k8s deployment manifests for all services (core-api, core-websocket, core-background, client, redis) with ingress routing: - woolball.xyz → landing page - open.woolball.xyz → client app + /api, /swagger, /ws - api.woolball.xyz → core-api - ws.woolball.xyz → core-websocket TLS via cert-manager for all domains. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The generic gemma3-1b-it-int4.task causes "func is not a function" in MediaPipe on the browser. Switch to gemma3-1b-it-int4-web.task which is the web-converted format required by tasks-genai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PrivateArgs["retry_count"]stored in Redis, with a shared ceiling of 3 retries; register SessionTrackQueue in DI (was dead code), fix message deserialization, fix timer replacement for retried tasks, publishtask_completionon success, and clean up Redis keysresponse.Contains("\"Status\":\"Error\"")string matching with properJsonDocument.Parse— prevents false HTTP 500 on valid responses that happen to contain the word "error" in their contentAllowedModelsandAllowedDtypestoFieldsConfig, validated inIsValidFields()at the API boundary — rejects arbitrary model strings before they reach worker nodes/api/v1/image-text-to-text) with full pipeline support (preprocessing, distribution, post-processing) for SmolVLM and similar modelswoolball.xyz→ landing pageopen.woolball.xyz→ client app + API/WebSocket/Swaggerapi.woolball.xyz→ core-api (dedicated)ws.woolball.xyz→ core-websocket (dedicated)SpeechToTextTaskHandler,TextToSpeechTaskHandler,TranslationTaskHandler,TextGenerationTaskHandler,ImageTextToTextTaskHandler), plusFieldsConfig,ITaskHandler,TaskHandlerFactory,StreamBuffer<T>,TaskRequestFactory,SpeechToTextFormProcessorBugs fixed
AddHostedService<SessionTrackQueue>()Deserialize<TaskRequest>Guid.TryParsetask_completionnever published — timers never cancelled on successtask_completion+ deletes Redis key_taskAttempts.GetOrAdd(taskId, 1)never increments — infinite retriesretry_countTryAddignores re-dispatched tasks — no new timerAddOrUpdatethat cancels previous timerPrivateArgs["retry_count"]in Redistask:{id}never deleted after successJsonDocumentIsValidFields()Test plan
dotnet buildpasses with 0 errorsgrep -rn "_taskAttempts" src/returns empty (in-memory counter fully removed)grep -rn "task_completion" src/returns 2+ results (publisher + subscriber)grep -rn "AddHostedService<SessionTrackQueue>" src/returns 1 resultkubectl apply -f k8s/deploys all services successfullywoolball.xyz,open.woolball.xyz,api.woolball.xyz,ws.woolball.xyzcorrectly🤖 Generated with Claude Code