Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions .s2s/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -683,9 +683,11 @@ As a result, `conversation.j2` and `TemplateRenderer.render_conversation()` are

### DEBT-018: Scope widget `--vektra-primary` override and dedupe style node

**Status**: planned | **Priority**: low | **Created**: 2026-04-21
**Status**: completed | **Priority**: low | **Created**: 2026-04-21 | **Completed**: 2026-04-27 (v0.5.0)
**Origin**: CodeRabbit review on PR #66 (v0.5.0), `vektra-learn/widget/src/chat-ui.js:138-144`

**Resolution**: `_injectStyles()` now writes the `--vektra-primary` override scoped to `.vektra-chat-btn, .vektra-chat-panel` (no longer to `:root`) and reuses a single `<style id="vektra-primary-override">` element across instantiations.

**Context**: `ChatUI._injectStyles()` appends a new `<style>` element setting `:root { --vektra-primary: <color> }` on every instantiation. Two consequences:

1. Repeated construction (hot reload, multi-instance embedding, host-page re-init) accumulates style nodes.
Expand Down Expand Up @@ -722,9 +724,11 @@ In today's deploy there is one widget per page and init fires once, so the impac

### DEBT-020: Audit log fallback when `request_id` is missing on sensitive endpoints

**Status**: planned | **Priority**: medium | **Created**: 2026-04-26
**Status**: completed | **Priority**: medium | **Created**: 2026-04-26 | **Completed**: 2026-04-27 (v0.5.0)
**Origin**: Gemini review on PR #71 (v0.5.0 release), `vektra-admin/src/vektra_admin/api.py:768-783`. Same pattern applies to sensitive reads, e.g. `get_conversation_turns` in `vektra-admin/src/vektra_admin/api.py:491-539`.

**Resolution**: each of `vektra-admin/api.py`, `vektra-learn/api.py`, and `vektra-ingest/api.py` declares a private `_resolve_request_id(request) -> UUID` helper that synthesizes `uuid4()` when `request.state.request_id` is missing and emits a `request_id_middleware_missing_fallback` structlog warning. All known sensitive endpoints (admin api-keys CRUD, namespace config PATCH, admin/learn conversation turns reads, ingest async + direct audit writers) now audit unconditionally. Helper duplication across three modules is intentional for v0.5.0 — extraction to `vektra_shared` is tracked separately if a fourth caller appears.

**Context**: `patch_namespace_config` writes the audit log only when `request.state.request_id` is truthy:

```python
Expand Down Expand Up @@ -781,6 +785,51 @@ Path A is sufficient for the immediate goal (one fewer round-trip). Path B is a

---

### DEBT-022: Widget multi-instance primary-color and base-style support

**Status**: planned | **Priority**: low | **Created**: 2026-04-27
**Origin**: Gemini review on PR #73 (v0.5.0 hardening), `vektra-learn/widget/src/chat-ui.js:152`

**Context**: DEBT-018 closed the host-page leak (`:root` → widget-roots scoped) and deduped the `--vektra-primary` override style node. It does **not** support multiple widgets on the same page with different primary colors: the override is keyed by a global `id="vektra-primary-override"` and writes class selectors shared by every instance, so the last constructor to fire wins for all instances. Separately, the main `<style>` block (the theme stylesheet) still appends a fresh element on every `ChatUI` construction — fine for the current 1-widget-per-page assumption, but accumulates under hot-reload or future multi-instance embedding.

Today's deploy is single-instance, so the gap is theoretical. Filing for the moment we ship multi-instance embedding (e.g. an instructor-side admin widget alongside a student widget on the same LMS page).

**Proposed approach**:
1. Apply per-instance primary color via `style.setProperty("--vektra-primary", color)` on the widget's root elements (`._btn` and `._panel`) inside `_createElements`, instead of writing a global stylesheet override. Drop the `vektra-primary-override` style node.
2. Dedupe the main style block too: look up `document.getElementById("vektra-chat-ui-styles")` and reuse if present; otherwise create. Note: instances must agree on theme — or theme should also become per-instance.
3. Decide whether `--vektra-primary` lookups inside descendants (`.vektra-chat-msg.user`, etc.) still resolve correctly via cascade after the per-instance set.

**Acceptance criteria**:
- [ ] Two widgets on the same page with different `data-primary-color` render with their own respective colors
- [ ] Re-initialising a widget (hot reload / SPA navigation) does not accumulate `<style>` elements in `document.head`
- [ ] No regression on single-instance deploys (dominant case today)
- [ ] Visual smoke test in Moodle host page

---

### DEBT-023: Hoist `_resolve_request_id` audit-fallback helper into `vektra_shared`

**Status**: planned | **Priority**: low | **Created**: 2026-04-27
**Origin**: CodeRabbit review on PR #73 (v0.5.0 hardening), nitpick on `vektra-learn/src/vektra_learn/api.py:60-76`

**Context**: DEBT-020 introduced a `_resolve_request_id(request) -> UUID` helper that synthesizes `uuid4()` when `request.state.request_id` is missing and emits a structlog warning. The helper is duplicated almost verbatim across `vektra-admin/src/vektra_admin/api.py`, `vektra-learn/src/vektra_learn/api.py`, and `vektra-ingest/src/vektra_ingest/api.py`. Logger usage is also subtly inconsistent: admin/ingest call `log.warning(...)` against a module-level `log`, while learn calls `structlog.get_logger(__name__).warning(...)` inline.

Three near-duplicates is the threshold where extraction starts to pay off (a fourth caller is plausible if the platform grows new audited endpoints; behavioural drift between modules is the real risk).

**Proposed approach**:
1. Add `vektra_shared.audit.resolve_request_id(request: Request) -> UUID` (or a new `vektra_shared.request_id` module if `audit.py` should stay narrow).
2. Standardize the logger call shape so all three modules emit the same `request_id_middleware_missing_fallback` event with identical keys.
3. Replace the three local helpers with imports.
4. Verify import-linter contracts still pass (`vektra_shared` is the only module the components are allowed to import from, so this is well within the existing contract).

**Acceptance criteria**:
- [ ] `_resolve_request_id` removed from `vektra-admin/api.py`, `vektra-learn/api.py`, `vektra-ingest/api.py`
- [ ] Single shared helper in `vektra_shared` with the same signature and behaviour
- [ ] Same structlog event name and keys regardless of caller
- [ ] All existing audit-fallback tests still pass; helper unit-tested in `vektra-shared/tests`

---

### INFRA-005: Docker log persistence across container restarts

**Status**: planned | **Priority**: medium | **Created**: 2026-03-23
Expand Down
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Convention (Keep a Changelog 1.1.0):

<!-- Add entries under: Added, Changed, Deprecated, Removed, Fixed, Security -->

## [0.5.0] - 2026-04-25
## [0.5.0] - 2026-04-27

Widget production-ready + instructor configuration.

Expand All @@ -41,6 +41,18 @@ Widget production-ready + instructor configuration.
- **User-facing branding**: README, top-level docs h1s, and external surfaces now use **Vektra RAG** as the product name. Coordinated with [vektralabs/vektra-moodle#14](https://github.com/vektralabs/vektra-moodle/pull/14) which renames its README h1 to "Vektra RAG for Moodle". Repo name (`vektra-stack`), Python package names (`vektra_*`), Docker images, container names, CLI commands, and internal code identifiers are unchanged.
- **widget footer label**: default "Powered by" link text changed from "Vektra" to "VektraLabs" — clearer maintainer-credit pattern (the link points to vektralabs.github.io, the org landing page) and avoids the residual short form. Integrators using the default branding will see the new label after upgrading the bundle; custom `data-powered-by-text` overrides are unaffected.
- **widget styles**: button and accent colors now use `var(--vektra-primary, …)` so `data-primary-color` takes effect without rebuilding the bundle. Hover states use `filter: brightness()` so custom colors still feel interactive.
- **widget primary-color scope (DEBT-018)**: the `--vektra-primary` override is now scoped to widget roots (`.vektra-chat-btn, .vektra-chat-panel`) instead of `:root`, so it no longer clobbers any unrelated `--vektra-primary` declarations on the host page. Repeated widget initialization reuses a single `<style id="vektra-primary-override">` node instead of accumulating duplicates (multi-instance / hot-reload safety).

### Fixed

- **audit log fallback (DEBT-020, NFR-007)**: sensitive admin, learn, and ingest endpoints now always write an audit row, even if the request-id middleware doesn't populate `request.state.request_id`. Each component declares a private `_resolve_request_id` helper that synthesizes a `uuid4()` fallback and emits a structlog warning so middleware misconfiguration is observable. Applied to `POST /api-keys`, `DELETE /api-keys/{id}`, `PATCH /admin/namespaces/{id}/config`, `GET /admin/conversations/{id}/turns`, `GET /api/v1/learn/conversations/{id}/turns`, and the vektra-ingest async + direct audit writers (used by `POST /api/v1/ingest`, batch ingest, deletion). Previous code skipped the audit silently when `request_id` was missing.
- **test fakes**: renamed `self_inner` to `self` in nested `_FakeResult` / `_FakeSession` helpers inside `test_fetch_document_names_marks_archived` (style consistency with PEP 8; no behaviour change).

### Security

- **deps**: bumped `litellm` from `>=1.40,<1.82.7` to `>=1.83.10,<1.83.11` — closes 2 critical CVEs (authentication bypass via OIDC userinfo cache key collision) and several high-severity findings (authenticated command execution via MCP stdio test endpoints, SSTI in `/prompts/test`).
- **deps-dev**: bumped `pytest` from `>=8.0` to `>=9.0.3` (full test suite green on the new major version).
- **deps-dev**: bumped widget `esbuild` from `^0.24` to `^0.25`.

## [0.4.0] - 2026-04-11

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dev = [
"alembic>=1.13",
"pyyaml>=6.0",
"testcontainers[postgres]>=4.8",
"pytest>=8.0",
"pytest>=9.0.3",
"pytest-asyncio>=0.24",
"pytest-cov>=5.0",
"ruff>=0.15",
Expand Down
Loading
Loading