Skip to content

Releases: yakden/ai-media-stack

v2.0.0 — plan → glTF scene → SDXL shots, 360° panoramas, photo declutter, MCP

Choose a tag to compare

@yakden yakden released this 24 Sep 14:43

Floor plan → 3D scene → photoreal, now agent-ready.

Added

  • Floor plans read like an architect reads them. plan_knowledge.py encodes drawing conventions
    (ГОСТ 21.501 / 21.201, ISO 4157/7519): room labels vs. level marks (+13,50), ceiling heights (h=3000),
    opening sizes (11x14), window/door marks, fire ratings, section marks, dimension chains. Multilingual room
    abbreviation dictionary (FI/PL/RU/UK/EN/DE). Mis-read OCR numbers that are really level marks no longer
    become room areas.
  • Room partitioning by neural walls. A VLM grounds every room label on the image; seeds are refined by
    OCR area labels; cubicasa-service /partition floods rooms between the neural walls (doors closed, exterior
    masked, terraces from Outdoor polygons, unseeded pockets auto-filled). Seeds are cross-checked by printed
    area vs polygon area (bad seeds rejected, up to 3 rounds). 13/13 rooms on the test plan, areas within ±0.5 m².
  • Whole-floor 2.5D scene for rendering (interior-render/app/floorscene.py): walls, openings, rooms and
    typical furniture become vertical prisms; a column ray-caster produces depth/material maps from any camera —
    you see the next room through a doorway and the outside through a window. Equirectangular 360° variant.
  • Shot planner (shots.py): wide_corner, toward_window, from_door, through_door (enfilade views
    from the neighbouring room), along, pano. Light is described from the real window positions relative to
    the camera; neighbours behind doors are named. Style presets ×7, mood presets ×6, two SDXL prompts
    (scene / style) to stay within CLIP limits.
  • SDXL engine: RealVisXL 4.0 + ControlNet-depth (/shot, /restyle), seamless 360° panoramas via
    circular convolution padding, panorama viewer, per-shot camera map drawn on the plan.
  • glTF 2.0 export (glb_export.py, GET /api/jobs/{id}/scene.glb): walls with cut openings, door leaves,
    window glass and sills, per-room floors/ceilings, furniture, node extras with room metadata — opens in
    Blender/Unreal/Unity/Three.js. Built-in GLB viewer at /glb/{id}/{floor}.
  • Photo tools (type=photo): a VLM lists personal items and clutter with boxes (two passes, merged),
    SAM2 refines masks, LaMa removes them; then tidy (img2img cleanup, same room), restyle (MiDaS depth →
    SDXL redesign with the same layout), declutter or enhance. Removal overlay + full room inventory for
    handover acts.
  • Render-only jobs on an analysed project (POST /api/jobs/{id}/shots) — no re-analysis.
  • MCP service (services/interior-mcp): streamable-HTTP MCP server exposing the whole pipeline
    (submit_plan, job_status, get_analysis, get_scene_3d, get_scene_glb, render_shots,
    list_images, get_image, submit_photo, get_photo_report, my_usage) with API keys shared with the
    gateway, per-key ownership and daily limits, HMAC-signed result links. Client guide included.

Changed

  • Broker /api/project accepts mood, engine, scenarios, max_shots, pano, user_style; the queue
    console has selectors for all of them plus the photo modes.
  • Per-room crops for VLM reading fade everything outside the room polygon so neighbours don't leak into the reading.
  • Category resolution counts kitchens/bathrooms from partitioned rooms; a single-kitchen dwelling is never an
    "apartment-building floor".

Fixed

  • Scene JSON now stores the source image and analysed rooms per floor, so later render jobs and GLB export
    work after a broker restart.
  • release-time regressions guarded: syntax checks for the broker and render server before restart.

Docs: INTERIOR3D.md · MCP.md · full CHANGELOG.

v1.8.0 — mobile touch gestures + orientation-aware fullscreen

Choose a tag to compare

@yakden yakden released this 12 Jun 22:17

Makes the VMS viewers feel great on phones, and adaptive on both mobile and desktop.

  • Touch gestures: two-finger pinch-to-zoom, one-finger drag-to-pan (while zoomed), double-tap to toggle 2× — on both the live monitor and the recorded-clip viewer. Desktop keeps wheel / drag / double-click. One shared attachZoomPan handler drives both for identical feel, with touch-action tuned per surface so the browser's own pinch-zoom never fights it and native <video> controls keep working.
  • Orientation-aware fullscreen: fullscreen fills the screen and follows the device — rotate the phone and the picture re-lays-out to fill in landscape, letterboxes cleanly in portrait (no forced orientation lock). Zoom re-centers on rotation.
  • Responsive polish: larger touch targets on the toolbar / record button under 820 px; landscape phones get up to 82 vh of media.

Reload the app (cache-bust ?v=137) to pick it up.

v1.7.2 — delete solo event clips on identity delete

Choose a tag to compare

@yakden yakden released this 12 Jun 22:08

Refines the recursive-delete policy (from 1.7.1) per the chosen rule: delete the event clip only if the person was the sole subject.

When you delete an identity, any event where it was the only associated identity is now deleted along with its clip + thumbnail files. Events that also involve other people are preserved (shared footage) — only the deleted person's link is nulled. Association is computed across both the denormalized Event.identity_id and the event's sightings, and applied per delete-set so single / bulk / clear-all all behave correctly.

Verified live: deleting an identity removed its 8 solo events + clips, while a (synthetically) shared event survived with its clip intact and the surviving identity untouched.

v1.7.1 — recursive identity deletion (files + rows)

Choose a tag to compare

@yakden yakden released this 12 Jun 22:03

Deleting an identity (single / bulk / clear-all) now also purges its on-disk artifacts, not just the cascaded DB rows:

  • the per-identity sighting-crop directory (data/identities/<id>/), and
  • every linked face-sample crop + row (FaceSample has no DB-level FK cascade, so it's purged explicitly).

Event clips are intentionally preserved (shared footage) — only the denormalized identity link is nulled.

Also a one-time cleanup of the backlog from pre-fix deletes: 308 orphaned face-sample rows + crops and 216 orphaned crop directories removed, so on-disk state matches the live identities exactly.

Events and people already removed their own files (clips/thumbnails, enrollment photos); this closes the last gap so every delete is fully recursive. Verified live.

v1.7.0 — Live Monitoring: monitor view, fullscreen, zoom, manual record

Choose a tag to compare

@yakden yakden released this 12 Jun 21:56

VMS Live Monitoring upgrades.

  • Low-latency monitor: clicking a camera opens an MJPEG monitor (not the ~6–12 s HLS) with fullscreen (⛶) and zoom/pan (scroll to zoom toward the cursor, drag to pan, double-click to toggle 2×).
  • Manual record button (● Record / ■ Stop with a live timer): stateless start/stop, the clip is assembled from the warm on-disk segment buffer and saved as a manual event with clip + thumbnail (POST /api/live/{id}/record/start · /stop).
  • Sound on demand: 🔊 switches to the HLS player.
  • Smoother grid: tiles stream at a low frame rate (?fps=4) so many cameras stay fluid at once; the focused monitor uses the full rate — the main fix for grid stutter.

Verified live: manual record produced a 9.8 s playable clip + thumbnail and appears as manual in Events; the ?fps= param works; production VM healthy.

v1.6.0 — non-root container + capability-dropped deployment

Choose a tag to compare

@yakden yakden released this 12 Jun 09:43

Fourth and final VMS security-hardening batch — deployment / blast-radius reduction. Compose-only, no image rebuild, fully reversible.

  • Container runs unprivileged (user: "1000:1000") instead of root, so an in-container write/RCE primitive can't tamper with host-owned files or the bind-mounted source. GPU inference is unaffected (NVIDIA device nodes are world-accessible) — verified still running on the T4, not CPU fallback.
  • All Linux capabilities dropped (cap_drop: [ALL]) + no-new-privileges.
  • App source mounted read-only (./app:ro).
  • Data directory tightened to 0700, owned by the unprivileged uid.

Verified live: uid 1000, GPU active, recording + thumbnail/face writes succeed, ffmpeg runs under dropped caps, auth still fail-closed, production VM unaffected.

Completes the four-batch defensive hardening pass (1.5.0 hygiene · 1.5.1 credential/SSRF · 1.5.2 fail-closed auth · 1.6.0 deployment) that followed a full multi-agent security audit.

v1.5.2 — fail-closed authentication + anti-spoof header injection

Choose a tag to compare

@yakden yakden released this 12 Jun 09:38

Third VMS security batch — closes the HIGH findings. The app previously failed open: auth_required defaulted to false and the gateway didn't inject the identity header, so any process reaching the loopback port (SSH tunnel, SSRF, co-located container) had full unauthenticated read/write/delete, and the SSO email was spoofable.

  • auth_required now defaults to true (fail-closed). Behind the SSO gateway the header is always present, so only direct/loopback callers without a key are rejected.
  • Gateway injects the identity from the auth subrequest and overrides any client-supplied header (auth_request_set $sso_email $upstream_http_x_auth_email; proxy_set_header X-Email $sso_email;) — the trusted email can no longer be spoofed. Documented in .env.example.
  • Strong, constant-time API key for CLI/tunnel access.

Verified live: loopback no-creds → 401, injected SSO header → 200, valid bearer → 200, bad key → 401, browser without cookie → 302 (no lockout), production VM unaffected.

v1.5.1 — RTSP credential masking + ffmpeg SSRF hardening

Choose a tag to compare

@yakden yakden released this 12 Jun 09:13

Second VMS security-hardening batch — credential exposure & SSRF. Server-side only; no change to the auth/SSO flow.

  • RTSP credentials no longer leave the server. Camera API responses mask the URL userinfo (rtsp://***@host:port/path); the raw URL with credentials stays in the DB (workers read it directly, so capture is unaffected). Previously GET /api/cameras returned admin:password in clear text.
  • Edit-safe. A blank or masked rtsp_url on update means 'keep current', so the UI can echo the redacted value without overwriting the secret.
  • SSRF / local-file-read hardening. The segmenter and HLS spawner pass -protocol_whitelist rtsp,rtsps,rtp,rtcp,udp,tcp,tls,crypto, and rtsp_url is restricted to the rtsp/rtsps schemes — the recorder can't be pointed at file:///… or internal HTTP.

Verified live: responses masked, raw DB URL intact, masked PUT keeps the real URL, file:// → 422, RTSP still connects, production VM healthy.

v1.5.0 — VMS security hardening (batch 1)

Choose a tag to compare

@yakden yakden released this 12 Jun 08:51

First batch of a defensive security-hardening pass over the VMS service, from a full audit (multi-agent workflow with adversarial verification of every finding). This batch is the low-risk hygiene set — no change to the auth/SSO flow:

  • Timing-safe API-key comparison (hmac.compare_digest).
  • Decompression-bomb guard on enrollment uploads (reject >50 MP; byte cap 32→8 MiB).
  • Path-traversal containment in the stored-image resolver (commonpath; absolute stored paths no longer trusted).
  • Bounded request bodies — bulk id-lists capped (max_length) + free-text max_length (DoS).
  • Face-group clustering cap 20000 → 2000 (bounds the O(n²) matrix).
  • Build hardening — setuptools/wheel upgrade (CVE-2022-40897 / CVE-2024-6345, build-time).

Verified live: routes register cleanly, the id-list cap returns 422 over the limit and 200 under it, the clustering cap is enforced, and the co-hosted production VM stays healthy.

Higher-impact items (redact RTSP credentials from API responses, ffmpeg protocol whitelist, enabling fail-closed auth, non-root container) follow in subsequent versions.

v1.4.2 — drop-to-latest decode thread

Choose a tag to compare

@yakden yakden released this 12 Jun 07:13

Continues the in-place VMS performance work.

Each camera worker now reads its RTSP stream on a dedicated producer thread that publishes only the newest frame; the detection loop consumes the latest frame instead of calling cap.read() inline. Under load (or when an analysis pass is slow) stale frames are dropped rather than queued, so the pipeline stays at real time instead of accumulating latency. A stuck or reconnecting stream no longer blocks the loop — the consumer waits with a timeout and stays responsive while the producer handles reconnects.

Verified live: last_seen advances in real time on a healthy camera while a flaky second stream reconnects independently; teardown stays clean (bounded thread join, ~2s).