Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ jobs:
- name: pnpm audit (high + critical)
run: pnpm audit --audit-level=high

# QNBS-v3 (#60): vendored y-webrtc fork invariant guard (PBKDF2 600k, non-extractable keys,
# DataChannel encryption, no dangling upstream dep) — fails the build on fork drift.
- name: Vendor fork invariant guard
run: pnpm run verify:vendor

# QNBS-v3: osv-scanner.toml (in src-tauri/) suppresses accepted RUSTSEC advisories for Cargo.lock;
# scan both lockfiles so npm + Rust advisories are caught on every push
- name: OSV vulnerability scan
Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
- **Version:** `1.23.0`
- **License:** MIT

The app supports a multi-provider AI stack (Gemini, OpenAI, Claude, Grok, OpenRouter, Ollama, WebLLM, ONNX Runtime Web, Transformers.js), four AI execution modes (Hybrid / Cloud / Local / Eco), real-time collaboration with E2E encryption, a Plot Board v2 with swimlane/canvas/timeline modes, character/world management, manuscript export, voice dictation, and an 11-locale i18n layer.
The app supports a multi-provider AI stack (Gemini, OpenAI, Claude, Grok, OpenRouter, Ollama, WebLLM, ONNX Runtime Web, Transformers.js), four AI execution modes (Hybrid / Cloud / Local / Eco), real-time collaboration with E2E encryption, a Plot Board v2 with swimlane/canvas/timeline modes, character/world management, manuscript export, voice dictation, and a 19-locale i18n layer.

---

Expand Down Expand Up @@ -114,7 +114,7 @@ WorldScript-Studio/
│ ├── collab-transport/ # Vendor fork of y-webrtc 10.3.0 with E2E encryption patch
│ ├── ui/ # Tailwind preset + design tokens
│ └── worker-bus/ # Typed worker pool, circuit breakers, dead-letter queue
├── locales/ # i18n source JSON modules (11 locales)
├── locales/ # i18n source JSON modules (19 locales)
├── public/ # Static assets; runtime i18n bundles `public/locales/<lang>/bundle.json`
├── tests/
│ ├── unit/ # Vitest tests (co-located naming convention)
Expand Down Expand Up @@ -387,7 +387,7 @@ Edge builds run `scripts/build-edge.mjs` which sets `DEPLOY_TARGET=edge` and pat

### Feature Flags

- `features/featureFlags/featureFlagsSlice.ts` gates **23 flags**. New installs get the **full feature set**: all default **on** except five opt-in flags that default **off** — `enableRtlLayout`, `enableVoiceSupport`, `enableVoiceWasm`, `enableGlobalCopilot`, `enableLocalFirstSync`. (`enableCodexAutoTracking` + `enableCrossProjectSearch` were promoted to permanent core; `enablePlotBoardV2` + `enableCloudSync` were retired — none remain in the slice.) See `docs/FEATURE-PARITY.md` for the per-flag matrix.
- `features/featureFlags/featureFlagsSlice.ts` gates **23 flags**. New installs get the **full feature set**: all default **on** except six opt-in flags that default **off** — `enableRtlLayout`, `enableVoiceSupport`, `enableProForge`, `enableVoiceWasm`, `enableGlobalCopilot`, `enableLocalFirstSync`. (`enableCodexAutoTracking` + `enableCrossProjectSearch` were promoted to permanent core; `enablePlotBoardV2` + `enableCloudSync` were retired — none remain in the slice.) See `docs/FEATURE-PARITY.md` for the per-flag matrix.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- UI: Settings → Experimental flags.
- Do not use scattered `if (true)` hacks.

Expand Down Expand Up @@ -486,10 +486,10 @@ Central orchestration layer for all background worker tasks. Messages use short
## Internationalization (i18n)

- **Custom React Context** (`contexts/I18nContext.tsx`) — not i18next.
- Source modules: `locales/<lang>/*.json` for **11 locales**: `ar`, `de`, `el`, `en`, `es`, `fr`, `he`, `it`, `ja`, `pt`, `zh`.
- Source modules: `locales/<lang>/*.json` for **19 locales**: `ar`, `de`, `el`, `en`, `es`, `eu`, `fa`, `fi`, `fr`, `he`, `hu`, `is`, `it`, `ja`, `ko`, `pt`, `ru`, `sv`, `zh`.
- Runtime bundles: `public/locales/<lang>/bundle.json` (rebuilt by `pnpm run i18n:bundle` or automatically via `predev` / `prebuild`).
- Hook: `useTranslation()` returns `t('key.path')`. **No hardcoded text** in UI.
- Key parity is enforced in CI (`pnpm run i18n:check`). Add keys to **all eleven** locale trees.
- Key parity is enforced in CI (`pnpm run i18n:check`). Add keys to **all nineteen** locale trees.
- Repair scripts: `services/i18nBootstrap.ts` and `services/i18nRepair.ts` handle missing keys / bundle corruption.
- RTL Beta: Arabic (`ar`) and Hebrew (`he`) set `html[dir="rtl"]`. Layout mirroring uses logical properties and a global `[dir="rtl"]` CSS net. Canvas/SVG boards (Plot Board, Character Graph) stay LTR to keep pointer/geometry math correct. `enableRtlLayout` flag forces RTL for testing.

Expand Down
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **Settings hygiene.** Removed stale Experimental-category search hints (`plot board`, `codex`,
`cross project` — retired/promoted flags) in favor of current features; `ProForgeDashboard` now
uses the catalog-driven `MaturityBadge` instead of a hard-coded Experimental pill, keeping the
v1.24 maturity-label convention consistent.

- **Desktop settings "minimize to tray" is now a proper switch.** `DesktopSection` used the only raw
`<input type="checkbox">` left in Settings, with its hint not programmatically associated. It now
renders the design-system `ToggleSwitch` (`role="switch"`, `aria-labelledby` + `aria-describedby`
Expand Down Expand Up @@ -105,11 +110,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- **Ollama / LM Studio / vLLM discovery & connectivity on desktop, CORS noise in the PWA
(#266).** Root cause: all local-server traffic (`services/ollamaService.ts`,
`scanLocalOpenAiCompatibleEndpoints()`) used the WebView's `fetch`, so inside the Tauri shell the
cross-origin `localhost` requests died on CORS/PNA, and in the PWA the settings auto-effect probed
`localhost:11434` on every visit (loud CORS console errors). All local-server calls now go through
the new thin `services/localServerHttp.ts`, which routes via `@tauri-apps/plugin-http` (native,
CORS-free) on desktop and keeps browser `fetch` on the web, with shared URL normalization, timeout
composition and `unreachable`/`timeout` classification (user aborts still propagate unchanged).
The Settings → AI card no longer auto-probes localhost in the PWA — it shows a quiet "desktop app
required" banner with a download CTA instead — and the desktop scan gained per-endpoint status
badges (reachable / no response / timeout / HTTP error) plus a one-click **Use this URL** action.
All new strings localized in 19 locales. See
[ADR 0012](docs/adr/0012-local-server-connectivity-tauri-http.md) and
[`docs/LOCAL-AI.md`](docs/LOCAL-AI.md).

- **Feature-catalog / slice default drift made structurally impossible.** `features/featureCatalog.ts`
now covers all **23** flags (was 16) and **derives** each entry's `defaultOn` from the slice's
`defaultFeatureFlagsState` instead of hand-keying it — the class of bug where the catalog said
`false` while the slice said `true` for ~12 flags can no longer recur (guarded by the new
`tests/unit/featureCatalog.test.ts`). Added risk-level / desktop-requirement / dependency metadata.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### Security

- **Tauri HTTP-plugin capability scope pinned (#266).** `http:default` alone grants **no** URL
scope — every plugin-http call (including AI-SDK cloud calls on desktop) was silently denied by
the plugin's allow-list check. The capability now explicitly allows loopback any-port
(`http://localhost:*/*`, `http://127.0.0.1:*/*` — Ollama/LM Studio/vLLM + custom ports) and the
cloud endpoints mirroring the Tauri CSP `connect-src` (Gemini, OpenAI, x.ai, OpenRouter, Groq).
- **y-webrtc vendor-fork audit + CI invariant guard (#60).** Full-file diff of
`packages/collab-transport` against upstream `y-webrtc@10.3.0`: no deviations beyond the three
documented SC patches (PBKDF2 600k iterations, `extractable: false`, `return promise.reject`) plus
the DataChannel E2E encryption — recorded in `packages/collab-transport/AUDIT.md`, fork bumped to
`10.3.0-sc2`. The deprecated `patches/y-webrtc@10.3.0.patch` and the dead root `y-webrtc`
dependency (zero imports) are removed, and the previously-referenced-but-missing
`scripts/verify-vendor-fork.mjs` now exists and runs in the CI security job (`verify:vendor`).

### Removed

- **Dead `enableWebnnInference` feature flag removed.** The flag shipped default-on but **no runtime
Expand Down
19 changes: 13 additions & 6 deletions VENDOR-FORKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,36 @@ upstream monitoring and patch porting.
| Attribute | Value |
|-----------|-------|
| **Upstream** | `y-webrtc@10.3.0` |
| **Fork version** | `10.3.0-sc1` |
| **Fork version** | `10.3.0-sc2` |
| **Reason for fork** | E2E encryption (AES-256-GCM via Web Crypto) is not available upstream. The fork adds PBKDF2 key derivation, non-extractable CryptoKeys, and encrypted WebRTC DataChannels compatible with the y-webrtc signalling protocol. |
| **Critical invariants** | 1. PBKDF2 iterations must remain **600,000** (OWASP 2024 SHA-256 minimum).<br>2. All derived `CryptoKey` instances must have `extractable: false`.<br>3. `promise.reject()` in `decrypt()` must be prefixed with `return`. |
| **Update protocol** | 1. Watch upstream releases via GitHub API or Renovate (excluded in config).<br>2. On new upstream release, create a diff branch `vendor/y-webrtc-<version>`.<br>3. Port the three encryption patches manually; run `packages/collab-transport` test suite.<br>4. Update `VENDOR-DIFF.md` baseline and bump the `-scN` suffix. |
| **Update protocol** | 1. Watch upstream releases via GitHub API or Renovate (excluded in config).<br>2. On new upstream release, create a diff branch `vendor/y-webrtc-<version>`.<br>3. Port the three encryption patches manually; run `packages/collab-transport` test suite.<br>4. Update `packages/collab-transport/AUDIT.md` and bump the `-scN` suffix. |
| **Owner** | qnbs |
| **Issue tracker** | #60 |

## Audit log

| Date | Auditor | Result |
|------|---------|--------|
| 2026-07-25 | Kimi Code | Full-file diff vs upstream `10.3.0`: no deviations beyond the three SC patches. Deprecated `patches/y-webrtc@10.3.0.patch` + dead root dependency removed; `verify-vendor-fork.mjs` created and wired into CI (`verify:vendor`); fork bumped to `10.3.0-sc2`. Details: `packages/collab-transport/AUDIT.md`. |

## `patches/y-webrtc@10.3.0.patch`

| Attribute | Value |
|-----------|-------|
| **Status** | **DEPRECATED** — the vendor fork above supersedes this patch. |
| **Action** | Safe to remove once the fork is confirmed stable in production (v1.19.0+). |
| **Status** | **REMOVED** (2026-07-25, #60 audit) — the vendor fork above superseded this patch in v1.19.0. |

## Audit automation

`scripts/verify-vendor-fork.mjs` performs a static invariant guard:
- Asserts PBKDF2 iterations == 600k
- Asserts `extractable: false`
- Asserts `return promise.reject(...)`
- Asserts version string matches expected `-scN` suffix
- Asserts DataChannel encrypt/decrypt wiring, `-scN` version suffix, and no dangling upstream dep/patch

Wired into CI security job as `verify:vendor`.
Wired into the CI security job as `verify:vendor` (created 2026-07-25 during the #60 audit).
The per-release diff record lives in `packages/collab-transport/AUDIT.md` (replaces the formerly
planned `VENDOR-DIFF.md`).
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## CVE / advisory monitoring (audit finding F-7)

Expand Down
7 changes: 4 additions & 3 deletions components/proForge/ProForgeDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type React from 'react';
import { useCallback, useState } from 'react';
import { useProForgeViewContext } from '../../contexts/ProForgeViewContext';
import { PIPELINE_STAGES, type PipelineStage } from '../../features/proForge/types';
import { Badge } from '../ui/Badge';
import { MaturityBadge } from '../settings/MaturityBadge';
import { PipelineProgressPanel } from './PipelineProgressPanel';
import { PipelineReviewPanel } from './PipelineReviewPanel';

Expand Down Expand Up @@ -88,8 +88,9 @@ export const ProForgeDashboard: React.FC = () => {
<div>
<div className="flex items-center gap-2 flex-wrap">
<h2 className="text-base font-semibold">{t('proforge.pipeline.title')}</h2>
{/* QNBS-v3: ProForge is an on-by-default but experimental agentic pipeline — label it. */}
<Badge variant="experimental">{t('common.badge.experimental')}</Badge>
{/* QNBS-v3: ProForge is opt-in + experimental — label driven by FEATURE_CATALOG so it
can never drift from the catalog/slice (MaturityBadge convention, v1.24). */}
<MaturityBadge flagKey="enableProForge" />
</div>
{/* QNBS-v3: PR6 — make the human-in-the-loop expectation explicit, not just implied in help. */}
<p className="text-[11px] text-[var(--sc-warning-fg)] font-medium">
Expand Down
Loading
Loading