diff --git a/README.md b/README.md index d2c924e..cd09e9e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Vire is a local-only macOS desktop app for project time tracking, AI usage evidence, and billing review. It imports AI traces (pi, Claude Code) from a **local Docker self-hosted Langfuse stack** as the primary AI time/usage/cost evidence source and requires human approval before any billable or profitability total is computed. -Current version: v0.8.0. Includes manual time entries, projects, reports (with Last 7/14/30/90 day quick-range presets), and CSV export; a local Docker Langfuse AI trace importer with configurable range, backfill, and diagnostics; and an AI time-entry suggestion engine that proposes time blocks from imported Langfuse evidence for human review and explicit acceptance — nothing is auto-posted. Accepted suggestions carry AI cost (where available), visible in Reports summary cards and in the CSV export as `cost_total`/`cost_currency` columns. The Suggestions view provides actionable notices for unmapped environments, untimed entries, and a disabled Langfuse source (TASK-034). +Current version: v0.8.2. Includes manual time entries, projects, reports (with Last 7/14/30/90 day quick-range presets), and CSV export; a local Docker Langfuse AI trace importer with configurable range, backfill, and diagnostics; and an AI time-entry suggestion engine that proposes time blocks from imported Langfuse evidence for human review and explicit acceptance — nothing is auto-posted. Accepted suggestions carry AI cost (where available), visible in Reports summary cards and in the CSV export as `cost_total`/`cost_currency` columns. The Suggestions view provides actionable notices for unmapped environments, untimed entries, and a disabled Langfuse source (TASK-034). **New in v0.8.0:** an opt-in zero-permission active-app and idle capture loop (default OFF). **Settings → Active-window capture** provides the enable toggle, sample-interval/idle/retention controls, a live capture @@ -69,7 +69,7 @@ The quickest way to run Vire is to install the prebuilt macOS app — no toolcha Building from source (below) is the alternative for developers, not the primary install path. 1. Open the [latest release](https://github.com/kaikkone4/vire/releases/latest) and download the - DMG asset **`Vire_0.8.1_aarch64.dmg`** (Apple Silicon / `aarch64` only — Intel Macs are not + DMG asset **`Vire_0.8.2_aarch64.dmg`** (Apple Silicon / `aarch64` only — Intel Macs are not supported by this build). 2. Double-click the downloaded `.dmg` to mount it, then drag **only `Vire.app`** onto the `Applications` shortcut in the DMG window. Drag just the app — do **not** Select-All (⌘A) and @@ -121,7 +121,7 @@ npm run tauri:build | Artifact | Path | |---|---| | App bundle | `src-tauri/target/release/bundle/macos/Vire.app` | -| Disk image (where the toolchain supports it) | `src-tauri/target/release/bundle/dmg/Vire__.dmg` (e.g. `Vire_0.8.1_aarch64.dmg`) | +| Disk image (where the toolchain supports it) | `src-tauri/target/release/bundle/dmg/Vire__.dmg` (e.g. `Vire_0.8.2_aarch64.dmg`) | ### Install and run diff --git a/RELEASE.md b/RELEASE.md index f2f3b3b..47125d9 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,42 @@ # Vire — Release Notes +## v0.8.2 — Publish the TASK-056 Settings UI so an older build detects the update (TASK-057) + +**Branch:** `feat/task-057-publish-v082-release` +**PR:** #44 + +### What changed + +This release makes the **TASK-056 in-app Active-window capture Settings panel discoverable** to anyone running the distributed `0.8.1` build. TASK-056 (PR #43, merge `df79423`) shipped the **Settings → Active-window capture** panel — the enable toggle, sample-interval/idle/retention controls, a live capture status/health readout, and a Captured-vs-Never-captured privacy table with truthful status/privacy copy — but carried **no version bump**, so `main` still read `0.8.1` and the latest Release was still `v0.8.1`. A distributed `0.8.1` build ran "Check for updates" and resolved **UpToDate**, so the new UI was invisible to anyone on the released binary. This release reconciles the version metadata to `0.8.2` and publishes a `v0.8.2` Release for the check to point at. + +- **Version metadata reconciled `0.8.1` → `0.8.2`.** `src-tauri/Cargo.toml`, `src-tauri/tauri.conf.json`, and the `vire` package entry in `src-tauri/Cargo.lock` (regenerated by `cargo build`) now all read `0.8.2`, so the Tauri/Cargo metadata and the in-app current-version string (`CARGO_PKG_VERSION`) agree. +- **Published GitHub Release.** A single **full** (non-draft, non-prerelease) Release tagged `v0.8.2` is published against the merged `main` commit that includes TASK-056 (published by the Release Manager in SW-6, after this change merges), so `/releases/latest` returns `v0.8.2`. Once published, the distributed `0.8.1` build resolves "Check for updates" to **UpdateAvailable (0.8.1 → 0.8.2)** with a real `release_url`; a rebuilt `0.8.2` build resolves to **UpToDate**; "Open GitHub Releases" reaches a real release page. + +**No source/schema/CSP/dependency change beyond the version strings.** `src-tauri/src/update_check/` (comparison logic, IPC commands, bounded fail-soft client, scoped opener capability, canonical-owner guards) is untouched. No `tauri-plugin-updater`, no download/install/auto-updater, no signing/notarization, and no schema/migration/IPC-storage/CSP/capture/Langfuse change — the only delta is the three version strings, this changelog entry, and the README version/artifact references. + +### DMG asset + unsigned first-launch remedy (Plan B) + +The published `v0.8.2` Release attaches the built **`Vire_0.8.2_aarch64.dmg`** (Apple Silicon / `aarch64` only, matching the `v0.8.1` single-arch precedent), built from the merged `main` head that includes TASK-056. The build is **unsigned and unnotarized**: a browser-downloaded copy carries `com.apple.quarantine`, so Apple Silicon Gatekeeper reports **"Vire is damaged and can't be opened"** — a policy verdict, not corruption, and right-click → Open does not clear it. The one-time, per-app remedy is to run, after copying the app to `/Applications`: + +```sh +xattr -dr com.apple.quarantine /Applications/Vire.app +``` + +Only run this for a DMG downloaded from the official [Vire releases page](https://github.com/kaikkone4/vire/releases) that you trust. The quarantine attribute is stamped by Gatekeeper **on the user's Mac at download time** — it is not present in the shipped artifact, so nothing in the DMG pre-clears it and no quarantine-bypass is (or can be) embedded in the artifact. The only fix that removes the user step is a valid Apple Developer ID signature plus notarization, tracked as **TASK-028**. This is the same Plan B validated on a physical Mac in TASK-054; the notes never instruct `sudo` or a global Gatekeeper disable. + +### Compatibility and rollback + +Version-string + docs change only; no runtime, schema, IPC, capability, or dependency delta from v0.8.1. The shipped `.app` behaves identically to the TASK-056 `main` build. Rollback: reinstall the prior `Vire_0.8.1_aarch64.dmg` — no cleanup step, no data migration. Deployment size: **patch**. Rollback: **automated**. + +### Verification (Part A) + +- `cargo build` succeeds; `Cargo.lock` `vire` = `0.8.2`; the version triple agrees at `0.8.2`. +- `cargo test update_check` green (comparison logic + canonical-owner guards unchanged). +- `updateCheckUi.test.mjs` green; `npm run build` and `cargo fmt --all -- --check` clean. +- `openspec validate task-057-publish-v082-release --strict` passes. + +Live release verification (0.8.1 build → **UpdateAvailable**; 0.8.2 build → **UpToDate**; DMG asset present and downloadable; `/releases/latest` = `v0.8.2`) is performed in SW-6 (Part B), after this change merges and the Release is published. + ## v0.8.1 — Make the update-check usable: release URL hotfix + first GitHub Release (TASK-052) **Branch:** `feat/task-052-publish-v081-release` diff --git a/openspec/changes/task-057-publish-v082-release/RELEASE.md b/openspec/changes/task-057-publish-v082-release/RELEASE.md new file mode 100644 index 0000000..b907f92 --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/RELEASE.md @@ -0,0 +1,168 @@ +# Release — TASK-057 · Vire v0.8.2 (Part A) + +**Gate:** SW-6 Release · **Tier:** L2 · **Date:** 2026-07-03 +**Branch / PR:** `feat/task-057-publish-v082-release` · PR #44 (base `main`) +**Commit:** `aa4ec09` (merge base `origin/main` = `df79423`, the TASK-056 merge) +**Tag:** `task-057/v0.8.2` (signed, SSH format) + +--- + +## Summary + +Metadata-only patch: version strings reconciled `0.8.1 → 0.8.2` across `src-tauri/Cargo.toml`, +`src-tauri/tauri.conf.json`, and `src-tauri/Cargo.lock` (`vire` package entry only — zero dep +drift). `RELEASE.md` (repo root) prepended with a `## v0.8.2` entry; `README.md` version header +and DMG-filename examples updated. No source, schema, CSP, IPC, dependency, or capability change. +Publishes the version bump the TASK-056 Settings UI needed so an already-distributed `0.8.1` +build's "Check for updates" resolves to **UpdateAvailable** instead of **UpToDate**, once the +Release itself is published in Part B. + +**Part A** (this PR): land `0.8.2` metadata + docs on `main`. +**Part B** (SW-6, post-merge): build `Vire_0.8.2_aarch64.dmg`, publish full non-draft/non-prerelease +GitHub Release `v0.8.2` on merged `main`, live-smoke the update check. Not performed in this gate — +explicitly out of scope per task instruction. + +--- + +## Gate results + +| Gate | Verdict | Evidence | +|------|---------|----------| +| SW-3 QA | **PASS** | `qa.md` — change scope 1:1 with `proposal.md`/`tasks.md`; `cargo test update_check` 11/11, `updateCheckUi.test.mjs` 12/12, `cargo fmt` clean, `npm run build` clean, `openspec validate --strict` passes | +| SW-4 Code Review | **PASS** | `review.md` — version triple consistent at `0.8.2`; `Cargo.lock` numstat `1 1`; no blocking issues, no suggestions | +| SW-5 Security | **PASS** | `sec.md` — gitleaks 0 leaks; semgrep 0 ERROR; Trivy 0/0; OSV advisories all pre-existing byte-identical to base, zero dep drift introduced | +| Documentation | **PASS (complete)** | `docs.md` — README/RELEASE independently re-verified: version, TASK-056 inclusion, update-check wording, manual-install (no auto-updater claim), Plan B xattr remedy + trust caveat + TASK-028 pointer all accurate; no doc edits required | + +--- + +## Declaration 1 — Deployment size: **PATCH** + +Semantic version change: `0.8.1 → 0.8.2` — no breaking change, no new user-facing feature in this +Part A diff itself (the underlying TASK-056 Settings UI was already merged to `main` at `df79423` +without a version bump). The only code delta here is three version-string lines, one `RELEASE.md` +changelog entry, and two `README.md` doc edits. This corrects a metadata inconsistency (the +TASK-056 UI shipped on `main` but `main` still reported `0.8.1`, so a distributed `0.8.1` build's +update check resolved `UpToDate` and the new UI was undiscoverable) and prepares the version for +the Part B Release publish. Patch-tier by SemVer, consistent with the TASK-052/v0.8.1 precedent. + +--- + +## Declaration 2 — Rollback strategy: **automated** + +Single-surface rollback for this Part A change: `git revert ` on `main`, reviewed and +merged as a normal PR. The diff is fully deterministic (three version-string lines + one changelog +block + two doc lines) — no data migration, no schema/IPC/capability change to reconcile, no +credential or Keychain state to restore. Verify: `src-tauri/Cargo.toml`/`tauri.conf.json` read +`0.8.1` again and `cargo test update_check` still passes (comparison logic itself is untouched by +either the forward change or the revert). + +For an end user who already installed the `v0.8.2` DMG (Part B), rollback is simpler still: +reinstall the prior `Vire_0.8.1_aarch64.dmg` — no cleanup step, no data migration, matching the +root `RELEASE.md` v0.8.2 entry's declared end-user rollback. + +**L2 note:** rollback path validated by inspection only (deterministic single-file-class diff); +no staging rebuild was performed this gate since Part A ships no runtime behavior change to +regress — the identical `update_check` test suite (11/11) passes both before and after the +version-string diff. + +--- + +## Declaration 3 — Component compatibility matrix + +No dependency, toolchain, or platform-minimum change from the `0.8.1` baseline (`task-052`). This +change is version-string + docs only. + +### Runtime and toolchain + +| Component | Min version | Tested on | Notes | +|-----------|-------------|-----------|-------| +| Vire | 0.8.2 | 0.8.2 | this release | +| macOS | 10.13 (High Sierra) | — (Part B live smoke pending, physical-Mac step) | Tauri 2.x platform minimum; macOS-only binary | +| Rust toolchain | 1.56 (edition 2021) | per CI/session toolchain | edition 2021 minimum, unchanged | +| Cargo | ≥1.56 | matches Rust toolchain | unchanged | + +### Tauri framework (Cargo) — unchanged, zero lockfile diff beyond `vire` self-version + +| Crate | Spec version | Resolved version | Notes | +|-------|-------------|-----------------|-------| +| `tauri` | `2.2` | `2.11.2` | no feature flags | +| `tauri-build` | `2.0` | resolved in lock | build-only | + +### Frontend / tooling — unchanged + +| Package | Spec version | Notes | +|---------|-------------|-------| +| `@tauri-apps/api` | `^2.2.0` | Tauri JS bridge | +| `@tauri-apps/plugin-dialog` | `^2.2.0` | dialog plugin | +| `@tauri-apps/cli` | `^2.2.0` | dev CLI, build-time only | +| TypeScript | `^5.7.2` | strict mode | +| Vite | `^6.4.3` | bundler | +| tsx | `^4.22.0` | test runner | + +### Upstream/downstream services + +| Service | Version / endpoint | Notes | +|---------|--------------------|-------| +| GitHub Releases API | `/repos/kaikkone4/vire/releases/latest` | polled by in-app update-check; published in Part B | +| GitHub Releases UI | `github.com/kaikkone4/vire/releases` | "Open GitHub Releases" target; no version lock | + +**Carry-forward advisories (pre-existing baseline, not introduced by this change, byte-identical +to base `df79423`):** `quick-xml 0.39.4` (RUSTSEC-2026-0194/0195, 7.5 HIGH each — reachable only via +`plist`/Tauri's own build-time config parsing, no attacker-controlled input); `quinn-proto 0.11.14` +(RUSTSEC-2026-0185, 7.5 HIGH — non-enabled `reqwest` QUIC feature, not compiled/linked on macOS +target); `glib 0.18.5` (RUSTSEC-2024-0429, 6.9, below the ≥7 line) plus the Linux GTK3 cluster — not +compiled on this macOS-only app. Governed by the `task-047` dependency-hygiene lineage, independent +of this release gate. + +--- + +## Part B — Post-merge instruction (SW-6 handoff) + +**Precondition:** PR #44 merged to `main`; confirm with: + +```bash +git fetch origin +git show origin/main:src-tauri/Cargo.toml | grep '^version' # must read 0.8.2 +``` + +**Steps:** +1. Build `Vire_0.8.2_aarch64.dmg` from the merged `main` head (`npm run tauri:build`). +2. `gh release create v0.8.2 --repo kaikkone4/vire --target --title "Vire v0.8.2" --notes-file ` — **no** `--draft`, **no** `--prerelease`. Attach the DMG asset. +3. Backfill DMG name/size/sha256 into the root `RELEASE.md` v0.8.2 entry (v0.8.1 precedent). +4. Live-smoke: `0.8.1` build → **UpdateAvailable**; rebuilt `0.8.2` build → **UpToDate**; DMG asset downloadable; `/releases/latest` returns `v0.8.2`. + +--- + +## Changed paths (this gate) + +``` +openspec/changes/task-057-publish-v082-release/RELEASE.md (new, this gate) +openspec/changes/task-057-publish-v082-release/qa.md (SW-3 gate artifact, committed this gate) +openspec/changes/task-057-publish-v082-release/review.md (SW-4 gate artifact, committed this gate) +openspec/changes/task-057-publish-v082-release/sec.md (SW-5 gate artifact, committed this gate) +openspec/changes/task-057-publish-v082-release/docs.md (Documentation gate artifact, committed this gate) +openspec/changes/task-057-publish-v082-release/handoff.md (updated, this gate) +``` + +Full PR #44 diff vs `main` (product surface, unchanged from prior gates): +`README.md`, `RELEASE.md`, `src-tauri/Cargo.lock`, `src-tauri/Cargo.toml`, `src-tauri/tauri.conf.json`, +plus the `task-057-publish-v082-release` change directory. + +--- + +## Gate checklist + +- [x] SW-3 QA PASS +- [x] SW-4 Code Review PASS +- [x] SW-5 Security PASS +- [x] Documentation gate PASS +- [x] RELEASE.md written with all three required declarations +- [x] Tag `task-057/v0.8.2` — signed (SSH format), created this gate +- [x] PR #44 promoted from draft to ready-for-review (this gate) + +## Non-blocking follow-ups + +1. Part B: build DMG, publish full `v0.8.2` GitHub Release, live-smoke update check, backfill + asset name/size/sha256 into root `RELEASE.md` — after Janne merges PR #44. +2. `quick-xml`/`quinn-proto`/`glib` pre-existing advisories — continue in the dependency-hygiene + lane (`task-047` lineage), independent of this release gate. diff --git a/openspec/changes/task-057-publish-v082-release/arch-review.md b/openspec/changes/task-057-publish-v082-release/arch-review.md new file mode 100644 index 0000000..866a397 --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/arch-review.md @@ -0,0 +1,92 @@ +# Architecture Review — TASK-057 publish v0.8.2 release + +- **Tier:** L2 · **Component:** Vire desktop app + release-ops · **Verdict:** **PASS** (no split, no + blocking BA escalation) +- **Reviewed against:** `artifacts/ba/03_architecture_plan.md` (update-check / release path), DEC-029 + (U-lite update check, Phase-1), TASK-052 arch-review (ownership/ordering precedent, proposed + DEC-031), TASK-053/054 (DMG asset + unsigned-launch guidance), and the TASK-056 merge state + (PR #43 → `main` @ `df79423`). + +## 1. Scope alignment — proportional, single boundary + +TASK-057 moves **version strings** + a **changelog/README doc update**, then performs one +**release-ops publication** (build aarch64 DMG from `main`, publish full `v0.8.2` Release with the +asset + unsigned-launch notes). No source logic, schema, IPC, CSP, capability, or dependency change; +`src-tauri/src/update_check/` is untouched. This crosses **no** component boundary in +`03_architecture_plan.md`. The update-check capability is already correct on `main`; TASK-057 only +supplies the two real-world inputs a *new* detectable version needs — matching compiled metadata and +a published Release newer than `v0.8.1`. + +## 2. Split decision — DO NOT split (keep focused) + +For `v0.8.1`, the equivalent work fragmented reactively across three tasks: TASK-052 published +notes-only (assets deferred), TASK-053 later attached the DMG, TASK-054 fixed the unsigned-launch +docs. That fragmentation was **discovery-driven**, not inherent: the DMG build path, the +`Vire__aarch64.dmg` naming, the single-arch (aarch64-only) choice, and the validated Plan-B xattr +guidance are now all **known and settled**. Folding them into one task removes the 052→053 window +where a published version had no downloadable binary. The architectural separation that *does* matter +— "code change through gates" vs "outward-facing publication" — is preserved **inside** this task as +the Part A / Part B ownership+ordering boundary (below), which is sufficient. A separate +code-bump-vs-release-asset task split would add coordination cost with **no** boundary or risk +benefit. **One task, no split.** + +## 3. Ownership & ordering (the key architectural constraint) — inherited from DEC-031 (proposed) + +**The public GitHub Release + DMG asset is owned by the Release Manager (SW-6) and published only +*after* the Part-A code change merges to `main`. The version-metadata change is owned by SW-2 and +clears the normal code gates first.** Sequenced, not concurrent. Rationale (same as TASK-052 §3): +publishing a public Release + binary is an outward-facing, hard-to-reverse action that must reference +the exact merged `main` SHA; the tag is authoritative for `/releases/latest`; and the server-side tag +via `gh release create --target ` avoids the recurring local SSH-key passphrase blocker that +deferred every prior signed task-tag. The DMG **must be built from the merged `main` head that +contains TASK-056** — otherwise the shipped binary omits the feature this release exists to surface. + +## 4. Correctness of the check against `v0.8.2` (logic unchanged) + +`compare_release` (`src-tauri/src/update_check/mod.rs`) strips a leading `v`, parses both sides with +`semver`, returns `UpdateAvailable` iff `latest > current`, else `UpToDate`: + +- `current 0.8.1` vs `tag v0.8.2` → `0.8.2 > 0.8.1` → **UpdateAvailable** (real `html_url`). ✓ +- `current 0.8.2` vs `tag v0.8.2` → equal → **UpToDate** (never downgrade). ✓ + +Tag MUST be parseable semver after `v`-stripping (`v0.8.2` → `0.8.2`); a non-semver tag fail-softs to +`Unknown`. No logic change — both arms exercised by the two builds. + +## 5. xattr / quarantine — reaffirming Plan B stays user-run (Janne's question) + +Confirmed architecturally sound to **not** embed any quarantine bypass in the DMG/installer: +`com.apple.quarantine` is stamped by Gatekeeper on the user's filesystem at download time, is absent +from the uploaded artifact, and cannot be pre-cleared from it; a drag-install DMG runs no script, and +any embedded `xattr`/`spctl` bypass would be both circular (blocked by the same Gatekeeper) and an +anti-pattern (globally weakening the user's Gatekeeper). The **only** artifact-side removal of the +user step is a valid Developer ID signature + Apple notarization. Absent that (TASK-028), Plan B +remains the documented, user-run, per-app remedy — the exact posture validated on a physical Mac in +TASK-054. Fully detailed in `design.md` §xattr. + +## 6. Version-tier note (non-blocking) + +Bump is `0.8.1 → 0.8.2` (**patch**) per the task directive. Observation for BA/product awareness: +TASK-056 added a user-facing feature (Settings UI), which strict SemVer would treat as minor +(→`0.9.0`), and TASK-056's RELEASE.md suggested a future `0.9.0`. A patch bump is a valid pre-1.0 +(`0.x`) choice and fully satisfies the release's goal (any increment `> 0.8.1` triggers +`UpdateAvailable`). Not a blocker; surfaced in the handoff so Janne can choose `0.9.0` instead if +preferred — that would change only the three version strings + tag/notes, no design impact. + +## 7. feedback_to_ba (non-blocking) + +- **DEC-031 (still proposed, carry-forward):** formalize release-publication ownership/ordering (code + gates → merge → SW-6 publishes a full Release; server-side tag via `gh release create --target`; + `/releases/latest` requires non-draft/non-prerelease). TASK-057 relies on it operationally; worth + pinning in `07_decision_log.md`. +- **FB-054-1 (open):** BA to decide TASK-028 Developer ID signing + notarization vs. accept the + unsigned/xattr interim. TASK-057 continues the unsigned interim; it does not resolve FB-054-1. + +## 8. Verdict + +**PASS.** Single component, proportional scope, no boundary crossing, no redesign, no split. +Architecture constraints for implementation: (1) keep `update_check/` untouched, no +`tauri-plugin-updater`/auto-install; (2) version triple (`Cargo.toml` / `tauri.conf.json` / +`Cargo.lock`) must agree at `0.8.2`; (3) Release is non-draft, non-prerelease, `v`-prefixed semver +tag on the merged `main` SHA that includes TASK-056, published by SW-6 after gates; (4) DMG built +from that `main` head, attached, unsigned-launch notes with user-run xattr Plan B and no embedded +bypass. diff --git a/openspec/changes/task-057-publish-v082-release/design.md b/openspec/changes/task-057-publish-v082-release/design.md new file mode 100644 index 0000000..4bd08bb --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/design.md @@ -0,0 +1,92 @@ +# Design — TASK-057 publish v0.8.2 release + +## Goal + +Publish a discoverable `v0.8.2` GitHub Release (with a real installable macOS aarch64 DMG built from +`main` incl. TASK-056) so an existing `0.8.1` build reports **UpdateAvailable**, without touching any +update-check logic and without building an in-app auto-updater. + +## Current state (verified locally) + +- `main` metadata triple = `0.8.1` (`src-tauri/Cargo.toml`, `src-tauri/tauri.conf.json`, `Cargo.lock` + `vire` entry all read `0.8.1`). +- TASK-056 merged to `main` at `df79423` (PR #43) — but the **local `origin/main` ref is stale** + (its recorded head is the TASK-054 merge `71719b9`, PR #42). **Precondition:** SW-2/SW-6 MUST + `git fetch origin` and branch/build off the *actual* current `main` that contains `df79423`, or the + DMG will omit TASK-056. This is a required step, not a blocker. +- Latest Release = `v0.8.1`, full, with asset `Vire_0.8.1_aarch64.dmg` + (`sha256 e77d15cf…`, 5,787,708 B) and TASK-054 unsigned-launch notes. +- `update_check/mod.rs`: current version = `env!("CARGO_PKG_VERSION")`; `compare_release` strips a + leading `v` from `tag_name`, parses both with `semver`, returns `UpdateAvailable` iff + `latest > current`, else `UpToDate` (equal or lower → up-to-date, never downgrade). **Unchanged.** + +## Version decision — 0.8.1 → 0.8.2 (patch) + +Bump instructed by the task. Note (non-blocking): TASK-056 added a user-facing feature (Settings UI), +which by strict SemVer would be minor (→0.9.0), and TASK-056's own RELEASE.md suggested folding into a +future `0.9.0`. A **patch** bump is nonetheless a deliberate, valid pre-1.0 choice here: the release's +*purpose* is to make the update check detect *a* newer version, and any increment `> 0.8.1` satisfies +`latest > current`. Pre-1.0 (`0.x`) SemVer does not reserve minor for features. The design proceeds +with `0.8.2` as directed; if the product prefers `0.9.0` semantics, only the three version strings and +the tag/notes change — no design impact. Flagged in the handoff for Janne's awareness. + +## Version triple + docs (Part A) + +Change exactly these, keep them in agreement: + +| File | From | To | +|------|------|----| +| `src-tauri/Cargo.toml` `version` | `0.8.1` | `0.8.2` | +| `src-tauri/tauri.conf.json` `version` | `0.8.1` | `0.8.2` | +| `src-tauri/Cargo.lock` `vire` entry | `0.8.1` | `0.8.2` (via `cargo build`, commit only that line) | +| `RELEASE.md` | — | prepend `## v0.8.2` entry (TASK-056 + this publication) | +| `README.md` | `Current version: v0.8.0`; `Vire_0.8.0_aarch64.dmg` example | `v0.8.2`; `Vire_0.8.2_aarch64.dmg` | + +No change under `src-tauri/src/update_check/`. No new dependency; the only lockfile line touched is +the `vire` package version. + +## DMG build + publish (Part B, after merge) + +1. `git fetch origin && git checkout main` (must include `df79423`); confirm + `git show HEAD:src-tauri/Cargo.toml` reads `0.8.2`. +2. `npm run tauri:build` → `src-tauri/target/release/bundle/dmg/Vire_0.8.2_aarch64.dmg` (aarch64; + x86_64 not built — matches the `v0.8.1` single-arch precedent). +3. `gh release create v0.8.2 --repo kaikkone4/vire --target --title "Vire v0.8.2" + --notes-file src-tauri/target/release/bundle/dmg/Vire_0.8.2_aarch64.dmg` — **full + release**, no `--draft`/`--prerelease` (either would keep `/releases/latest` on `v0.8.1`). Tag is + created server-side over the authenticated `gh` session → avoids the local SSH-key passphrase + blocker that deferred every prior signed task-tag. +4. Notes MUST contain: asset name, "unsigned/unnotarized", the exact + `xattr -dr com.apple.quarantine /Applications/Vire.app` command, official-source trust caveat, and + the TASK-028 Developer-ID/notarization long-term pointer. MUST NOT contain `sudo`, + `spctl --master-disable`, or any global-Gatekeeper-disable guidance. + +## xattr — why Plan B stays user-run (answers Janne's question) + +Do **not** embed a quarantine bypass in the DMG/installer. Architecturally impossible-and-wrong: + +- **The quarantine attribute is not in the artifact.** `com.apple.quarantine` is an extended + attribute stamped by Gatekeeper onto the file **on the user's Mac at download time**, keyed to the + downloading app. It does not exist inside the DMG we upload, so there is nothing in the artifact to + pre-clear. `xattr -dr …` must run on the user's filesystem, after download, against the installed + `/Applications/Vire.app`. +- **A postinstall "bypass" is circular and unsafe.** A plain drag-install DMG runs no install script; + and any script that ran `xattr`/`spctl` would itself be blocked by the same Gatekeeper it tries to + defeat, and would teach users to globally weaken Gatekeeper — a security anti-pattern we reject. +- **The only artifact-side removal of the user step is Developer ID + notarization.** A validly + signed, Apple-notarized, ticket-stapled app launches without any user xattr action. That requires a + paid Apple Developer ID certificate and a notarization pipeline — tracked as **TASK-028** (BA + decision FB-054-1 still open). Until that exists, Plan B remains the documented, user-run, per-app + remedy — exactly the posture Janne validated on a physical Mac in TASK-054. + +## Verification (proportional) + +- Part A: `cargo build` OK + `Cargo.lock` `vire = 0.8.2`; version triple all `0.8.2`; + `cargo test update_check` green (logic unchanged); `npm run test:frontend` for + `updateCheckUi.test.mjs` green; `npm run build` + `cargo fmt --all -- --check` clean; + `openspec validate task-057-publish-v082-release --strict` passes. +- Part B live: `gh release view v0.8.2 --json tagName,isDraft,isPrerelease,assets` → + `v0.8.2`, `isDraft=false`, `isPrerelease=false`, DMG asset present; `/releases/latest` → `200` + with `tag_name=v0.8.2`; **0.8.1 build → UpdateAvailable (0.8.1→0.8.2, real url)**; **0.8.2 build → + UpToDate**; releases page `200`. Physical-Mac DMG smoke (mount, drag, xattr, launch) is standing + human UAT (carried from TASK-053/054), not a merge blocker. diff --git a/openspec/changes/task-057-publish-v082-release/docs.md b/openspec/changes/task-057-publish-v082-release/docs.md new file mode 100644 index 0000000..19f1899 --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/docs.md @@ -0,0 +1,73 @@ +# Documentation Gate — TASK-057 Part A (publish v0.8.2 release: version bump + docs) + +- **Gate**: Documentation Engineer (L2 pre-SW-6 docs check) +- **Branch / PR**: `feat/task-057-publish-v082-release` · draft PR #44, base `main` +- **Scope gated**: Part A only — `README.md` + `RELEASE.md` wording for the v0.8.2 version/docs + reconciliation. Part B (DMG build, `gh release create`, live update-check smoke) is SW-6, out of + scope here. + +## Verdict: complete — no doc changes required + +Read `handoff.md`, `qa.md`, `review.md`, `sec.md`, then independently re-read `README.md` and +`RELEASE.md` on the branch tip. All required doc checks already hold; SW-4/SW-5 verified the same +content and no drift has occurred since. + +## Checklist verified + +- **Version says v0.8.2**: `README.md:5` ("Current version: v0.8.2"), `RELEASE.md:3` (`## v0.8.2` + heading). Version triple confirmed on disk: `src-tauri/tauri.conf.json:4` and + `src-tauri/Cargo.toml:3` both read `"0.8.2"`/`0.8.2`. +- **TASK-056 Settings UI included**: `RELEASE.md:3,10` state this release publishes the TASK-056 + Active-window capture Settings panel (PR #43, merge `df79423`) so the update check can point at + it; `README.md:382-402` documents the panel's UAT steps under "TASK-056 — required before + release". +- **Update-check behavior documented accurately**: `RELEASE.md:13` describes the exact transition — + a distributed `0.8.1` build resolves **UpdateAvailable (0.8.1 → 0.8.2)** with a real + `release_url`; a rebuilt `0.8.2` build resolves **UpToDate**; "Open GitHub Releases" reaches a + real release page. Matches `update_check` module scope (untouched, comparison-logic only). + `RELEASE.md:38` restates the same transition as the Part B live-verification target. + `README.md` install flow does not describe or imply an in-app auto-download/auto-install path. +- **Manual install / "Open Releases" — not an in-app updater**: `RELEASE.md:15` explicitly states + "No `tauri-plugin-updater`, no download/install/auto-updater"; `RELEASE.md:54` ("the in-app + downloader/installer remains TASK-042 territory") and `:58`/`:86` ("no auto-updater") are + consistent carried-forward wording from v0.8.1. `README.md:66-105` documents the manual + download-DMG-and-drag install path with no auto-update claim. + `grep -rn "tauri-plugin-updater|auto-updat" README.md RELEASE.md` → all hits are explicit + negations, none are an in-app updater claim. +- **Unsigned/unnotarized Plan B guidance**: `README.md:77-101` and `RELEASE.md:19-25` both carry + the `xattr -dr com.apple.quarantine /Applications/Vire.app` remedy, an explicit "only run this if + you downloaded from the official releases page and trust it" caveat, and the TASK-028 + signing/notarization pointer as the real fix. + `grep -n "sudo\|spctl --master-disable" README.md RELEASE.md` → only the sentence in + `RELEASE.md:25` stating the notes *never* instruct `sudo` or a global Gatekeeper disable; no + actual unsafe instruction present in either file. +- **Part B (release asset) deferred to SW-6**: `RELEASE.md:31-38` scopes "Verification (Part A)" to + build/test/lint checks only, and explicitly states "Live release verification (... DMG asset + present and downloadable ...) is performed in SW-6 (Part B), after this change merges and the + Release is published." `handoff.md` "Exact next action" agrees. +- **No stale version references**: `grep -rln "0.8.1|0.8.0" docs/` → no hits; `docs/` carries no + version-pinned copy that would drift. Older `RELEASE.md` entries (`v0.8.1`, `v0.8.0`, ...) are + historical changelog content, correctly left unmodified below the new `v0.8.2` entry. + +## Checks run + +- `grep -n "0.8.2\|v0.8.2" README.md RELEASE.md` — confirms consistent version references. +- `grep -n "version" src-tauri/tauri.conf.json src-tauri/Cargo.toml` — confirms `0.8.2` triple. +- `grep -rn "Open GitHub Releases|Open Releases|Check for updates|UpdateAvailable|update_check|tauri-plugin-updater|auto-updat" README.md RELEASE.md` — confirms update-check wording and absence of in-app-updater claims. +- `grep -n "sudo\|spctl --master-disable" README.md RELEASE.md` — confirms no unsafe Gatekeeper-bypass instruction. +- `grep -rln "0.8.1|0.8.0" docs/` — confirms no stale version references under `docs/`. + +## Changes made + +None. `README.md` and `RELEASE.md` already satisfy every Part A doc requirement; no edit was +needed. + +## Blockers + +None. + +## Suggestions + +None for Part A. (Non-blocking, future/SW-6 scope: once the `v0.8.2` Release and DMG are published, +SW-6 should backfill the asset name/size/sha256 table into the `RELEASE.md` v0.8.2 entry, matching +the pattern already used for v0.8.1's "DMG asset attached" section.) diff --git a/openspec/changes/task-057-publish-v082-release/handoff.md b/openspec/changes/task-057-publish-v082-release/handoff.md new file mode 100644 index 0000000..91dbcdc --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/handoff.md @@ -0,0 +1,41 @@ + + +# Handoff — TASK-057 publish Vire v0.8.2 release + +- **Change dir**: `openspec/changes/task-057-publish-v082-release/` +- **Branch / PR**: `feat/task-057-publish-v082-release` · PR #44 **ready for review**, base `main` +- **Phase / gate**: SW-4 **PASS**, SW-5 **PASS**, Docs **complete**, **SW-6 Release Part A DONE** +- **Tier**: L2 · Part A only; Part B belongs to SW-6 after merge + +## Last gate result +SW-6 (this gate): task `RELEASE.md` written with all 3 declarations (patch / automated / +compatibility matrix — no dep drift). Tag `task-057/v0.8.2` created and **signed** (SSH format, +verified via `git cat-file -p`). PR #44 promoted draft → ready-for-review. Gate artifacts +(qa/review/sec/docs/handoff) committed. No GitHub Release or DMG built — correctly deferred to +Part B per task scope. + +## Active blockers +None. DevOps CI-unblock applied (2026-07-03): required `dependency-advisories` gate was failing on +two new SCORED quick-xml advisories (RUSTSEC-2026-0194/0195, DoS 7.5). No upstream fix exists (plist +1.9.0 hard-pins quick-xml <0.40; latest tauri/plist). Fixed via documented targeted `[advisories]. +ignore` in `src-tauri/deny.toml` (group C) — unreachable in vire (own Info.plist only; no untrusted +XML; no first-party quick-xml/plist use). Verified locally: cargo-deny 0.19.9 `advisories ok` exit 0; +control run fails on exactly those 2 IDs. See `ops-review.md`. Pushed to PR #44; awaiting CI re-run. + +## Exact next action +Janne merges PR #44 → route to SW-6 Part B: build `Vire_0.8.2_aarch64.dmg`, publish full +non-draft/non-prerelease `v0.8.2` release on merged `main` (steps in this dir's `RELEASE.md` +§Part B), live-smoke update check, backfill DMG name/size/sha256 into root `RELEASE.md`. + +## Required files +- `ops-review.md` (this dir) — CI-unblock rationale (quick-xml advisories) + local verification. +- `src-tauri/deny.toml` — targeted `[advisories].ignore` group (C) for RUSTSEC-2026-0194/0195. +- `RELEASE.md` (this dir) — 3 declarations + Part B instructions. +- `review.md`, `qa.md`, `docs.md`, `sec.md` — gate evidence. +- `README.md`, root `RELEASE.md` — install/release note wording. +- `src-tauri/Cargo.toml`, `tauri.conf.json`, `Cargo.lock` — version triple (0.8.2, confirmed). + +## Notes carried forward +- Merge base `df79423` (TASK-056) confirmed ancestor of `HEAD`. Product diff (excl. openspec) is + exactly README, RELEASE, Cargo.toml, tauri.conf.json, Cargo.lock; `Cargo.lock` only bumps local + `vire` version — no dep drift. No update_check/updater/asset/schema/CSP/capability/npm changes. diff --git a/openspec/changes/task-057-publish-v082-release/ops-review.md b/openspec/changes/task-057-publish-v082-release/ops-review.md new file mode 100644 index 0000000..8335918 --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/ops-review.md @@ -0,0 +1,85 @@ +# Ops Review — TASK-057: unblock required CI (quick-xml advisories on PR #44) + +**Role:** DevOps Engineer · **Tier:** L2 · **Branch:** `feat/task-057-publish-v082-release` · **PR:** #44 +**Scope of this review:** CI-unblock only. No version change (stays `0.8.2`), no release assets, +no `v0.8.2` GitHub Release. Single policy file touched: `src-tauri/deny.toml`. + +## 1. Blocker + +Required workflow **`dependency-advisories` / cargo-deny (advisories, Apple-target-scoped)** failed +on PR #44 (run 28681953325, job 85067075469). Two new **scored** RustSec advisories landed against +`quick-xml 0.39.4`: + +| ID | Class | CVSS | Fixed in | Title | +|----|-------|------|----------|-------| +| RUSTSEC-2026-0194 | DoS (algorithmic complexity) | 7.5 HIGH | quick-xml ≥ 0.41.0 | O(N²) duplicate-attribute scan in `BytesStart::attributes()` | +| RUSTSEC-2026-0195 | DoS (memory exhaustion) | 7.5 HIGH | quick-xml ≥ 0.41.0 | Unbounded namespace-declaration allocation in `NsReader` | + +cargo-deny 0.19.9 (the pinned scanner) reports both as `error[vulnerability]` — reproduced locally +(see §5). These are **not** the unmaintained/unscored class of the existing accepted advisories. + +## 2. Remediation options evaluated (upgrade preferred over suppression) + +Dependency path (verified `cargo tree -i quick-xml --target aarch64-apple-darwin`): `quick-xml` has +a **single** parent, `plist 1.9.0`, consumed only by Tauri's `tauri-utils` / `tauri-codegen` / +`tauri-build`. + +1. **Bump quick-xml to ≥ 0.41.0** — REJECTED by resolver. `plist 1.9.0` hard-pins + `quick-xml = "^0.39.2"` (`< 0.40`). `cargo update -p quick-xml --precise 0.41.0` fails: + `candidate versions found which didn't match: 0.41.0 … required by plist v1.9.0`. +2. **Bump plist to a release built on quick-xml ≥ 0.41** — NOT AVAILABLE. `plist 1.9.0` is the + latest published plist; `cargo update -p plist` = "0 packages to update"; probes for 1.9.1 / + 1.10.0 / 1.11.0 return `no matching package`. +3. **Bump Tauri** — NO EFFECT. `tauri 2.11.2` is already the latest 2.x (`cargo update -p tauri` = + "0 packages"), and any Tauri still pulls `plist 1.9.0 → quick-xml 0.39.x`. Even bumping the + `^2.2` constraint cannot clear this while plist has no fixed release. + +**Conclusion:** there is no dependency/lockfile move that resolves these advisories today. This is a +genuine "no safe upgrade" situation, mirroring the repo's existing group-(B) acceptance. + +## 3. Chosen fix — documented targeted ignore (repo-sanctioned mechanism) + +Added the two IDs to the pre-existing `[advisories].ignore` list in `src-tauri/deny.toml` (the same +mechanism already used for the 5 unic-* advisories), under a new **group (C)** block. The fix is +policy-only: no code, no dependency, no version, no lockfile change. + +The block explicitly flags that — unlike group (B) — these are **scored 7.5 HIGH** and are accepted +on a **documented unreachability basis**, not on low severity. + +## 4. Reachability / trust-boundary rationale + +- **Sole entry point is `plist`**, used only for Apple **Info.plist** handling (build/codegen time + plus `tauri-utils` metadata). The XML parsed is the application's **own** Info.plist — + developer-authored and sealed inside the signed `.app` at build time. It is never network-, + user-, or attacker-supplied. +- **Both advisories are remote DoS against services that ingest untrusted XML at scale** (the 0195 + PoC OOM-killed NLnet Labs Routinator's concurrent RRDP workers). vire is a **single-user local + macOS desktop app** with no untrusted-XML ingestion path — the CVSS-7.5 network-attacker vector + is not reachable. +- **vire first-party code calls neither quick-xml nor plist** (grep-verified across `src/` and + `src-tauri/src/`). Foreground-app bundle IDs in `active_window` come from AppKit APIs, not XML + parsing. +- **No upstream fix exists** (§2), so acceptance — not upgrade — is the correct action. + +**Residual risk:** low. Accepting a scored advisory is a real posture decision, recorded here and in +`deny.toml`. Review triggers (any of): (i) plist ships a release on quick-xml ≥ 0.41 → bump and drop +these IDs; (ii) vire begins parsing untrusted/remote XML → reachability changes, re-evaluate +immediately; (iii) either advisory is re-scored or superseded. + +## 5. Verification (local equivalent of the required CI gate) + +Ran the **exact** pinned scanner and command the workflow runs +(`cargo deny check advisories`, cargo-deny **0.19.9**, from `src-tauri/`): + +- `cargo-deny --version` → `cargo-deny 0.19.9` (matches `deny.toml` header + workflow pin). +- **With fix** (patched `deny.toml`): `advisories ok` — **exit 0**. +- **Without the two new IDs** (control, `grep -v` copy of the same config): fails on **exactly** + `RUSTSEC-2026-0194` and `RUSTSEC-2026-0195` (`error[vulnerability]` ×2, `advisories FAILED`) — no + other advisory surfaces. Confirms the ignore is complete **and** minimal (no over-suppression; + the target-scoping tripwire for the gtk3/glib group-(A) IDs is untouched). + +## 6. Scope guard + +- Version stays `0.8.2`; no `Cargo.toml` / `tauri.conf.json` / `Cargo.lock` edits. +- No DMG, no `gh release create`, no tag changes — Part B remains SW-6's post-merge responsibility. +- Only `src-tauri/deny.toml` (+ this artifact and `handoff.md`) changed. diff --git a/openspec/changes/task-057-publish-v082-release/proposal.md b/openspec/changes/task-057-publish-v082-release/proposal.md new file mode 100644 index 0000000..8461e75 --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/proposal.md @@ -0,0 +1,81 @@ +# TASK-057 — Publish Vire v0.8.2 so the in-app update check detects a new version after TASK-056 + +## Why + +TASK-056 (in-app **Settings → Active-window capture** panel + truthful status/privacy copy) merged +to `main` in PR #43 (`mergedAt=2026-07-02T10:01:46Z`, merge commit `df79423`). But it shipped **no +version bump** — `main` still carries `0.8.1` metadata, and the latest published GitHub Release is +still `v0.8.1` (TASK-052 published it; TASK-053 attached the aarch64 DMG; TASK-054 fixed the +unsigned first-launch guidance). Consequences confirmed against the release precedent: + +1. **No user can discover TASK-056.** The distributed `0.8.1` build runs "Check for updates" and + resolves **UpToDate** — `compare_release` (`src-tauri/src/update_check/mod.rs`) sees `latest + v0.8.1 == current 0.8.1`, so the Settings-UI work is invisible to anyone on the released binary. +2. **The update check has nothing newer to point at.** Until a `v0.8.2` Release exists, + `/releases/latest` keeps returning `v0.8.1`; the U-lite "UpdateAvailable → Open Releases → manual + install" path cannot be exercised end-to-end for the TASK-056 build. + +The fix is operational and small, mirroring TASK-052 but folding in the now-established DMG asset +step (TASK-053) and the validated unsigned-launch guidance (TASK-054): reconcile the app metadata to +`0.8.2` on `main`, **build a fresh macOS aarch64 DMG from the `main` commit that includes TASK-056**, +and publish a single **full** GitHub Release tagged `v0.8.2` with that DMG attached and Plan-B xattr +notes. No update-check *logic* changes. + +## What changes + +One component (the **Vire desktop app**) plus a release-ops publication, with a hard internal +ownership boundary between them (same shape as TASK-052 — see `arch-review.md`). **No task split.** + +**Part A — Code change (SW-2 → gates → merge), branch off the current `origin/main` that +includes the TASK-056 merge (`df79423`):** + +- `src-tauri/Cargo.toml` — `version = "0.8.1"` → `"0.8.2"`. +- `src-tauri/tauri.conf.json` — `"version": "0.8.1"` → `"0.8.2"`. +- `src-tauri/Cargo.lock` — the `vire` package `version` entry follows to `0.8.2` (regenerated by + `cargo build`; commit only that lockfile delta). +- `RELEASE.md` (repo root) — prepend a `## v0.8.2` entry rolling up TASK-056 (Settings UI) + this + publication. Keep the `## v0.8.1` / `## v0.8.0` entries intact below. +- `README.md` — update the stale `Current version: v0.8.0` header to `v0.8.2` and the download + example to `Vire_0.8.2_aarch64.dmg`. + +**Part B — Release publication (SW-6, AFTER Part A merges to `main`):** + +- Build `Vire_0.8.2_aarch64.dmg` from the merged `main` head via `npm run tauri:build` (output at + `src-tauri/target/release/bundle/dmg/`), so the binary contains TASK-056. +- Publish a **full** (NOT `--draft`, NOT `--prerelease`) GitHub Release tagged **`v0.8.2`** against + the merged `main` SHA, tag created server-side via `gh release create v0.8.2 --target ` + (sidesteps the recurring local signing-passphrase blocker), and **attach the DMG asset**. +- Release notes MUST state the build is **unsigned/unnotarized** and document the Plan-B remedy + `xattr -dr com.apple.quarantine /Applications/Vire.app` with the official-source trust caveat and + a pointer to TASK-028 (Developer ID signing + notarization) as the long-term fix — verbatim + posture Janne validated on a physical Mac in TASK-054. + +## What does NOT change + +- **No in-app download/install updater.** This does **not** implement `tauri-plugin-updater`, + auto-download, or auto-install. The feature stays U-lite: detect → "Open GitHub Releases" → + **manual install**. Auto-update remains TASK-042 territory and is explicitly out of scope here. +- **No update-check logic change.** `src-tauri/src/update_check/` (comparison, IPC commands, bounded + fail-soft client, scoped opener capability, canonical-owner guards) is untouched. +- **No quarantine-bypass embedded in the DMG/installer.** The `com.apple.quarantine` attribute is + applied by Gatekeeper on the *user's* machine at download time; it does not exist in the shipped + artifact and cannot be pre-cleared from it. Plan B stays **user-run and per-app**; the only + artifact-side removal of that step is a valid Developer ID signature + Apple notarization + (TASK-028). See `design.md` §xattr and `arch-review.md` §3. +- **No schema, migration, IPC-storage, CSP, capture, classifier, or Langfuse change.** No new + dependency; zero non-version lockfile drift. Generated files (`src-tauri/gen/**`, + `src-tauri/target/**`) are gitignored — not hand-edited. +- `package.json` has no `version` field and gains none — canonical version stays `Cargo.toml` / + `tauri.conf.json` (in-app value via `CARGO_PKG_VERSION`). + +## Impact + +- **Makes TASK-056 discoverable.** Once `v0.8.2` is published, the distributed `0.8.1` build resolves + "Check for updates" to **UpdateAvailable (0.8.1 → 0.8.2)** with a real `release_url`; a rebuilt + `0.8.2` build resolves to **UpToDate**. "Open GitHub Releases" reaches a real page offering a + downloadable, install-ready DMG. +- **No security-boundary change.** Egress host set, capability type, CSP, and client posture are + identical to TASK-052/056; SEC-002 unaffected. Publishing a public Release + asset is an + outward-facing action owned by SW-6, after gates. +- **Component scope:** one component (desktop app) + a release-ops action. No cross-boundary work; + **no task split required.** diff --git a/openspec/changes/task-057-publish-v082-release/qa.md b/openspec/changes/task-057-publish-v082-release/qa.md new file mode 100644 index 0000000..0e5fbe4 --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/qa.md @@ -0,0 +1,86 @@ +# QA Gate — TASK-057 Part A (publish v0.8.2 release: version bump + docs) + +**Branch:** `feat/task-057-publish-v082-release` · **PR:** #44 (draft, base `main`) · **Tier:** L2 +**Scope gated:** Part A only (version triple + docs). Part B (DMG build + `gh release create` + +live update-check smoke) is explicitly out of scope here — carried by SW-6. + +## Verdict: PASS + +## Branch provenance + +- `git merge-base origin/main feat/task-057-publish-v082-release` = `df79423` (the TASK-056 merge + commit) after `git fetch origin`. `git merge-base --is-ancestor df79423 ` confirms + `df79423` is an ancestor of the branch tip. Branch correctly cut from the real current `main`, + including TASK-056. (Local `main` ref was stale at `6eaffc8`/TASK-052 before fetch — expected per + `design.md`'s noted precondition, not a defect.) + +## Change scope — verified against `df79423..HEAD` + +Exactly 5 files changed outside `openspec/changes/`, matching `proposal.md`/`tasks.md` Part A scope +1:1, no more, no less: +- `src-tauri/Cargo.toml` — `version = "0.8.1"` → `"0.8.2"`. +- `src-tauri/tauri.conf.json` — `"version": "0.8.1"` → `"0.8.2"`. +- `src-tauri/Cargo.lock` — single-line diff, only the `vire` package `version` entry + (`0.8.1`→`0.8.2`); no other dependency line touched, confirming zero dep drift. +- `RELEASE.md` — new `## v0.8.2` section prepended; `## v0.8.1` / `## v0.8.0` and all older entries + (16 version headings total) intact and unmodified below it. +- `README.md` — `Current version: v0.8.0` → `v0.8.2`; both DMG filename examples + `Vire_0.8.1_aarch64.dmg` → `Vire_0.8.2_aarch64.dmg`. + +Confirmed **no** changes anywhere under `src-tauri/src/update_check/` (diff is empty against that +path), and no `tauri-plugin-updater` (or any updater dependency) added in `Cargo.toml`/`Cargo.lock`. +No schema/CSP/capture/classifier/Langfuse touch — none of those paths appear in the diff. + +## Content accuracy checks + +- Version triple all read `0.8.2` on the branch tip (`tauri.conf.json`, `Cargo.toml`, `Cargo.lock` + `vire` block) — grepped directly, not just diffed. +- `RELEASE.md` v0.8.2 entry: states no source/schema/CSP/dependency change beyond version strings; + documents the DMG asset name and the unsigned/unnotarized posture; contains the exact + `xattr -dr com.apple.quarantine /Applications/Vire.app` remedy, an official-source trust caveat, + and the TASK-028 Developer-ID/notarization pointer — matches spec requirement wording. Grepped the + full file for `sudo` / `spctl --master-disable`: the only hit is the sentence stating the notes + *never* instruct `sudo` or a global Gatekeeper disable — not an actual instruction. No prohibited + guidance present. +- `README.md` edits are accurate and localized to the two intended spots (header line, DMG example) + plus the artifact-location table's parenthetical example; no unrelated copy changed. + +## Scenario coverage matrix (`specs/in-app-update-check/spec.md`) + +| Requirement / Scenario | Coverage | Notes | +|---|---|---| +| Req 1 — Scenario: 0.8.1 build sees the update | **Covered (code/test level)** | `update_check::tests::newer_release_is_update_available` exercises the exact unchanged `parse_release_payload`/`compare_release` path with a higher-version pair; version-value-agnostic logic, untouched by this change. Literal 0.8.1→0.8.2 live-binary check is Part B (deferred per task scope). | +| Req 1 — Scenario: 0.8.2 build is up to date | **Covered (code/test level)** | `same_version_is_up_to_date` exercises the equal-version path; `lower_release_is_up_to_date` confirms the never-downgrade guarantee holds. Literal 0.8.2-build live smoke is Part B. | +| Req 1 — Scenario: draft/prerelease does not satisfy | **N/A to Part A** | Governs how SW-6 invokes `gh release create` (no `--draft`/`--prerelease`); no code path in this repo to unit-test. Correctly a Part B/operational responsibility — flagged, not a Part A gap. | +| Req 2 — Scenario: DMG asset present and downloadable | **Deferred to Part B** | No DMG is built or published in Part A; nothing to verify yet. | +| Req 2 — Scenario: Notes carry remedy, not a bypass | **Content pre-verified here** | The exact notes text SW-6 will carry into `gh release create --notes-file` is authored in this change's `RELEASE.md` v0.8.2 entry and verified accurate above (command, caveat, TASK-028 pointer, no bypass wording). Actual publish action is Part B. | + +No scenario in this spec delta is unaccounted for: every one is either code-level-covered now or +explicitly and correctly deferred to Part B per the task's stated boundary. + +## Checks run (independently re-executed, not just trusted from handoff) + +- `cargo build --locked` — **OK** (only pre-existing dead-code warnings in unrelated `active_window` + modules, not introduced by this change). +- `cargo test update_check` — **11 passed / 0 failed**. +- `node --import tsx --test tests/updateCheckUi.test.mjs` — **12 passed / 0 failed**. +- `cargo fmt --all -- --check` — clean. +- `npm run build` (tsc + vite) — clean, no errors. +- `openspec validate task-057-publish-v082-release --strict` — valid. + +No flaky behavior observed across the above (single run each, deterministic unit/UI tests, no +timing- or network-dependent assertions in scope). + +## Tier L2 proportionality note + +This change is a version/docs-only reconciliation with **no new application logic** (Part A adds no +code path). L2's "performance baselines + negative-path tests" are satisfied by the pre-existing +`update_check` suite's negative/never-downgrade case (`lower_release_is_up_to_date`) and equal-case +(`same_version_is_up_to_date`), which this change does not alter. No new integration test file was +added — none is warranted since no new behavior exists to cover; writing one would duplicate +existing coverage of unchanged logic. + +## Blockers + +None for Part A. Full live release/DMG/binary verification remains correctly scoped to SW-6 Part B +per `tasks.md` and is not a Part A gate blocker. diff --git a/openspec/changes/task-057-publish-v082-release/review.md b/openspec/changes/task-057-publish-v082-release/review.md new file mode 100644 index 0000000..1671e0c --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/review.md @@ -0,0 +1,37 @@ +# Code Review — TASK-057 Part A + +## Verdict: PASS + +Part A passes the SW-4 craft/conventions gate. The PR diff is limited to the expected release/version surface, the version triple is consistent at `0.8.2`, `Cargo.lock` only drifts the local `vire` package version, and the README/RELEASE guidance remains accurate for the unsigned Plan B path. + +## Blocking Issues + +None. + +## Suggestions + +None. + +## Escalations to SW Architect + +None. + +## Reviewed Scope + +- `src-tauri/Cargo.toml:3` reads `version = "0.8.2"`. +- `src-tauri/tauri.conf.json:4` reads `"version": "0.8.2"`. +- `src-tauri/Cargo.lock:4527` / `src-tauri/Cargo.lock:4528` show the `vire` package at `0.8.2`; `git diff --numstat origin/main...HEAD -- src-tauri/Cargo.lock` is `1 1`, and the unified diff changes only `0.8.1` to `0.8.2`. +- `README.md:5`, `README.md:72`, and `README.md:124` reference `v0.8.2` / `Vire_0.8.2_aarch64.dmg` consistently. +- `README.md:87` through `README.md:100` document the one-time `xattr -dr com.apple.quarantine /Applications/Vire.app` remedy with an official-source trust caveat and TASK-028 as the real signing/notarization fix. +- `RELEASE.md:3` through `RELEASE.md:38` add the `v0.8.2` release entry, preserve the Part B publication boundary, and explicitly state no source/schema/CSP/dependency/updater/capture changes beyond the version/docs delta. +- Branch ancestry check confirmed `origin/main` / TASK-056 merge `df79423f73d273c6dec337a5121d98c3071635bc` is the merge base and an ancestor of `HEAD`. + +## Checks + +- Read first: `openspec/changes/task-057-publish-v082-release/handoff.md`, then `qa.md`. +- `git diff --name-only origin/main...HEAD -- ':!openspec/changes/task-057-publish-v082-release'` → exactly `README.md`, `RELEASE.md`, `src-tauri/Cargo.lock`, `src-tauri/Cargo.toml`, `src-tauri/tauri.conf.json`. +- `git diff --name-only origin/main...HEAD -- src-tauri/src/update_check src-tauri/src src-tauri/capabilities src-tauri/permissions package.json package-lock.json src-tauri/Cargo.toml src-tauri/Cargo.lock src-tauri/tauri.conf.json` → only the three version files; no update-check logic, release asset/schema/CSP/capture, npm, or capability changes. +- `cargo metadata --locked --no-deps --format-version 1` from `src-tauri/` passed. +- `openspec validate task-057-publish-v082-release --strict` passed. +- `git diff --check origin/main...HEAD -- src-tauri/Cargo.toml src-tauri/tauri.conf.json src-tauri/Cargo.lock README.md RELEASE.md` passed. + diff --git a/openspec/changes/task-057-publish-v082-release/sec.md b/openspec/changes/task-057-publish-v082-release/sec.md new file mode 100644 index 0000000..e1bd73c --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/sec.md @@ -0,0 +1,105 @@ +# Security Review — TASK-057 Part A (publish v0.8.2 release: version bump + docs) + +- **Gate**: SW-5 Security (parallel with SW-4 Code Review — already PASS per `review.md`) +- **Branch / PR**: `feat/task-057-publish-v082-release` · draft PR #44, base `main` +- **Tier**: **L2** — Secrets + CVE ≥ 7 + Trivy + semgrep ERROR +- **Scope gated**: Part A only — version triple (`0.8.1`→`0.8.2`) + `RELEASE.md`/`README.md` docs. + Part B (DMG build, `gh release create`, live update-check smoke) is SW-6, out of scope here. + +## Verdict: PASS + +No auto-fail condition is **introduced by this change**. The product diff against the real base +(`origin/main` = `df79423` = TASK-056) is exactly 5 files — three version strings plus two docs — +with **zero dependency drift** (`Cargo.lock` is a 1-line change: only the local `vire` package +version). All Tier-L2 scanners are clean on the changed surface; the ≥7.0 OSV advisories are +pre-existing baseline debt (byte-identical to `main` and to the already-shipped `v0.8.1`), not +attributable to a version/docs-only bump. Consistent with the TASK-052 (v0.8.1 publish) and TASK-056 +(the code this release publishes) gate dispositions. + +## Change scope verified (base `df79423`, real `origin/main`) + +`df79423` confirmed = `origin/main` and an ancestor of `HEAD`. Product diff excluding this change's +OpenSpec artifacts is exactly: + +| File | Change | +|---|---| +| `src-tauri/Cargo.toml` | `version 0.8.1 → 0.8.2` | +| `src-tauri/tauri.conf.json` | `version 0.8.1 → 0.8.2` | +| `src-tauri/Cargo.lock` | single line — `vire` package `0.8.1 → 0.8.2`; **numstat `1 1`**, zero dep drift | +| `RELEASE.md` | new `## v0.8.2` entry prepended; older entries intact | +| `README.md` | version header + two DMG-filename examples `0.8.1/0.8.0 → 0.8.2` | + +No change under `src-tauri/src/` (update-check comparison logic, IPC commands, bounded fail-soft +client, scoped opener capability, canonical-owner guards all untouched). No `tauri-plugin-updater` / +download / install / auto-updater. No schema / migration / IPC-storage / CSP / capture / capability / +Langfuse / npm change. No release assets built or touched in Part A. + +## Scanner results (Tier L2) + +| Scanner | Version | Target | Result | Auto-fail? | +|---|---|---|---|---| +| **gitleaks** | 8.30.1 | branch delta `df79423..HEAD` (2 commits, ~34 KB) | **no leaks found** | **No** | +| **semgrep** | 1.168.0 | 4 changed non-lock files, `p/secrets`, ERROR severity | **0 ERROR findings, 0 scan errors** | **No** | +| **Trivy** | 0.72.0 | changed files, `fs` secret+misconfig, HIGH/CRITICAL | **clean (0/0)** — deliverable is a desktop bundle, no container image | **No** | +| **OSV-scanner** | 2.4.0 | `src-tauri/Cargo.lock` (529 pkgs) | 21 advisories (3 High @ 7.5, 1 @ 6.9) — **all pre-existing, none introduced by this change** (see Advisories) | **No** | + +## Advisories (OSV) — pre-existing baseline, carried-forward, NOT blocking this gate + +Every advisory below is present **byte-identical** in the base `df79423` lockfile (verified: +`quick-xml 0.39.4`, `quinn-proto 0.11.14`, `glib 0.18.5` unchanged). TASK-057 adds/removes/bumps +**zero** dependencies — a version-string + docs change cannot introduce or regress a dependency CVE. +The dependency-vulnerability posture of the published `v0.8.2` is identical to the already-shipped +`v0.8.1`. + +- **`quick-xml` 0.39.4 — RUSTSEC-2026-0194 & RUSTSEC-2026-0195, CVSS 7.5 HIGH each** (fix 0.41.0). + *New* RustSec DB entries against a pre-existing, unchanged crate — not a regression from this + change. Reachability (`cargo tree -i quick-xml`, aarch64-apple-darwin): pulled **only** transitively + via `plist v1.9.0` → `tauri-codegen`/`tauri-build`/`tauri-utils`. `plist`/`quick-xml` parse the + app's **own** `Info.plist` / Tauri config — trusted, local, developer-controlled, build-time input. + No attacker-controlled / network-supplied XML reaches this parser in Vire. Advisory, not a blocker. +- **`quinn-proto` 0.11.14 — RUSTSEC-2026-0185, CVSS 7.5 HIGH** (fix 0.11.15). Optional, non-enabled + `reqwest` QUIC feature: inverted dep tree on the macOS target is **empty** → **not compiled or + linked** into the build. Availability-only, unreachable. Same disposition as TASK-052/056. +- **`glib` 0.18.5 — RUSTSEC-2024-0429, CVSS 6.9** (below the ≥7 line) plus the GTK3 cluster + (`atk`/`gdk`/`gtk*`/`gdkx11` RUSTSEC-2024-04xx, unscored) — Linux GTK3 bindings / unmaintained-crate + notices, **not compiled on this macOS-only aarch64 app**. Governed by the TASK-047 GTK/RustSec + posture, not this release gate. +- **`anyhow` 1.0.102 (RUSTSEC-2026-0190)**, `unic-*` / `proc-macro-error` — unscored / low, + pre-existing. + +## Manual security review (change-focused) + +- **Secrets**: none in the diff (gitleaks + semgrep p/secrets + Trivy secret scan all clean). The only + credential-adjacent token is the unchanged `keyring` dependency context line in `Cargo.lock`. +- **Auth / trust boundary**: N/A — local single-user Tauri desktop app, no auth layer, no server + endpoints. This change touches no code, so no auth path is altered. +- **Endpoint / rate-limit**: no network code changed. The `/releases/latest` fetch and bounded + fail-soft client are untouched; the bump only changes the `CARGO_PKG_VERSION` string used for local + semver comparison — adds no endpoint, alters no request behavior. No new egress. +- **Gatekeeper / xattr guidance** (`RELEASE.md` / `README.md`): the unsigned-first-launch remedy is + `xattr -dr com.apple.quarantine /Applications/Vire.app` — **scoped to the single installed app** + (not a recursive global `/` sweep), gated on an explicit "only for a DMG from the official releases + page that you trust" caveat, with the true fix (Developer-ID signature + notarization) pointed at + TASK-028. The notes **explicitly state they never instruct `sudo` or a global Gatekeeper disable**; + grep confirms no `sudo`, no `spctl --master-disable`, no global-disable instruction. The quarantine + attribute is stamped by Gatekeeper on the user's Mac at download — not embedded in the artifact, so + no quarantine-bypass is (or can be) shipped. Safe. +- **Schema / CSP / capture / capability**: unchanged (no such paths in the diff). + +## Escalations + +None. No design-level security issue. No code-level FAIL. + +## Recommendation (non-blocking, dependency-lane) + +Continue addressing the pre-existing HIGH advisories in the dependency-maintenance lane +(TASK-047 lineage), not this release gate: a `tauri` bump to pull `quick-xml ≥ 0.41.0` via `plist`, +and either a `deny.toml` documented ignore for the unreachable `quinn-proto` (non-enabled `reqwest` +QUIC feature on the build target) or a `reqwest` bump once an advisory-clear `quinn-proto` resolves. + +## Gate verdict + +**SEC STATUS: pass.** No auto-fail condition introduced by this change: no secrets, no reachable +new/regressed CVE, no source/IPC/CSP/capability/network change, safe unsigned-launch guidance. +Hold for SW-4 (Code Review — already PASS); both gates green → SW-6 publishes `v0.8.2` and performs +Part B live verification. diff --git a/openspec/changes/task-057-publish-v082-release/specs/in-app-update-check/spec.md b/openspec/changes/task-057-publish-v082-release/specs/in-app-update-check/spec.md new file mode 100644 index 0000000..076b228 --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/specs/in-app-update-check/spec.md @@ -0,0 +1,59 @@ +# Spec delta — in-app-update-check + +## ADDED Requirements + +### Requirement: The v0.8.2 release is detectable by an older build + +The project SHALL publish a **full** (non-draft, non-prerelease) GitHub Release tagged `v0.8.2` +against the `main` commit that carries `0.8.2` metadata **and** the TASK-056 Settings-UI merge, and +the app's compiled version SHALL read `0.8.2` (`CARGO_PKG_VERSION`, mirrored in +`src-tauri/tauri.conf.json` and the `vire` entry of `src-tauri/Cargo.lock`). This operationalizes the +already-specified check for the TASK-056 build — it adds **no** update-check logic, network host, +capability, or auto-download/install feature; the check stays U-lite (detect → open Releases → +manual install). + +#### Scenario: The previously distributed 0.8.1 build sees the update + +- **WHEN** a build whose `CARGO_PKG_VERSION` is `0.8.1` runs "Check for updates" against the published + `v0.8.2` release +- **THEN** the result is `UpdateAvailable` with `current = "0.8.1"`, `latest = "0.8.2"`, and the + release's real `html_url` as `release_url` +- **AND** the app does not download or install anything automatically; the user reaches the release + via "Open GitHub Releases" and installs manually. + +#### Scenario: The rebuilt 0.8.2 build is up to date + +- **WHEN** a build whose `CARGO_PKG_VERSION` is `0.8.2` runs "Check for updates" against the published + `v0.8.2` release +- **THEN** the result is `UpToDate` with `current = "0.8.2"` +- **AND** the equal-version case is never reported as a downgrade or as `UpdateAvailable`. + +#### Scenario: A draft or prerelease does not satisfy the requirement + +- **WHEN** the only `v0.8.2` Release is marked draft or prerelease +- **THEN** `/releases/latest` still returns `v0.8.1` (the endpoint skips drafts/prereleases) +- **AND** the requirement is NOT met until a full `v0.8.2` release is published. + +### Requirement: Each macOS Release attaches an installable aarch64 DMG with unsigned-launch guidance + +For each shipped macOS version the published Release SHALL attach the built +`Vire__aarch64.dmg` disk image, and — because the build is unsigned and unnotarized — its +notes SHALL document the user-run Gatekeeper remedy so the app can be launched. The quarantine remedy +SHALL be user-run and per-app; no quarantine-bypass is embedded in the shipped artifact, and no +global-Gatekeeper-disable guidance is given. The only mechanism that removes the user step is a valid +Apple Developer ID signature plus notarization (tracked separately, TASK-028). + +#### Scenario: The DMG asset is present and downloadable + +- **WHEN** the `v0.8.2` Release is published +- **THEN** it has an attached, downloadable asset named `Vire_0.8.2_aarch64.dmg` +- **AND** "Open GitHub Releases" reaches a page that offers that install-ready binary. + +#### Scenario: Notes carry the unsigned first-launch remedy, not a bypass + +- **WHEN** a user reads the `v0.8.2` Release notes after macOS reports the app as "damaged" +- **THEN** the notes state the build is unsigned/unnotarized and give the exact command + `xattr -dr com.apple.quarantine /Applications/Vire.app`, an official-source trust caveat, and a + pointer to Developer ID signing + notarization (TASK-028) as the long-term fix +- **AND** the notes do NOT instruct `sudo`, `spctl --master-disable`, or any global Gatekeeper + disable, and the DMG itself embeds no quarantine-clearing script. diff --git a/openspec/changes/task-057-publish-v082-release/tasks.md b/openspec/changes/task-057-publish-v082-release/tasks.md new file mode 100644 index 0000000..d998c89 --- /dev/null +++ b/openspec/changes/task-057-publish-v082-release/tasks.md @@ -0,0 +1,69 @@ +# Tasks — TASK-057 publish v0.8.2 release + +Single component (the **Vire desktop app**) plus a release-ops publication. **Two ordered parts with +a hard ownership boundary**: Part A is a normal code change (SW-2 → gates → merge); Part B is the +public GitHub Release **with the DMG asset**, published by the **Release Manager (SW-6) only after +Part A merges to `main`**. Do **not** publish before merge — see `arch-review.md`. No update-check +logic change, no in-app auto-updater / `tauri-plugin-updater`, no signing/notarization, no +schema/CSP/capture change. Generated files (`src-tauri/gen/**`, `src-tauri/target/**`) are gitignored +— do **not** hand-edit or commit them. + +## Part A — Land v0.8.2 metadata on `main` (SW-2) + +**Precondition:** `git fetch origin`; branch `feat/task-057-publish-v082-release` off the current +`origin/main` that includes the TASK-056 merge `df79423` (local `origin/main` ref may be stale). Confirm +`git show origin/main:src-tauri/src/main.ts` shows the TASK-056 capture panel before branching. + +- [x] `src-tauri/Cargo.toml` — set `version = "0.8.2"`. +- [x] `src-tauri/tauri.conf.json` — set `"version": "0.8.2"`. +- [x] `src-tauri/Cargo.lock` — `vire` package `version` entry now `0.8.2`; `cargo build --locked` + confirms no dependency-line drift (only that lockfile line changed). +- [x] `RELEASE.md` (repo root) — prepended a `## v0.8.2` entry: rolls up TASK-056 (Settings→Active-window + capture panel + truthful status/privacy copy) and this publication; states plainly **no + source/schema/CSP/dependency change beyond the version strings**; notes the aarch64 DMG is + attached and unsigned/unnotarized (xattr Plan B). `## v0.8.1` / `## v0.8.0` kept intact below. +- [x] `README.md` — `Current version: v0.8.0` → `v0.8.2`; DMG examples updated to + `Vire_0.8.2_aarch64.dmg` (download step + artifact-location table). +- [x] Confirmed **no** change under `src-tauri/src/update_check/` and no `tauri-plugin-updater` added. + +## Part A — Verify before merge (proportional to scope) + +- [x] `cargo build --locked` (in `src-tauri/`) succeeds; `Cargo.lock` `vire` = `0.8.2`. +- [x] `cargo test update_check` passes (11 passed / 0 failed — comparison logic + owner guards unchanged). +- [x] `node --import tsx --test tests/updateCheckUi.test.mjs` passes (12 passed / 0 failed). +- [x] `npm run build` passes; `cargo fmt --all -- --check` passes. +- [x] Version triple agrees at `0.8.2`: `src-tauri/Cargo.toml`, `src-tauri/tauri.conf.json`, and the + `vire` block in `src-tauri/Cargo.lock` all read `0.8.2`. +- [x] `openspec validate task-057-publish-v082-release --strict` passes. +- [ ] Open PR → gates (SW-3 QA, SW-4 review, SW-5 security) → **merge to `origin/main`**. *(Draft PR + opened by SW-2; gates + merge pending.)* + +## Part B — Build DMG + publish the Release (SW-6, AFTER Part A is merged) + +- [ ] `git fetch origin && git checkout main`; confirm `git show HEAD:src-tauri/Cargo.toml` reads + `0.8.2` and HEAD includes TASK-056. +- [ ] `npm run tauri:build` → produces `src-tauri/target/release/bundle/dmg/Vire_0.8.2_aarch64.dmg` + (aarch64 only, matching the v0.8.1 single-arch precedent). +- [ ] Publish, creating the tag server-side and attaching the DMG: + `gh release create v0.8.2 --repo kaikkone4/vire --target --title "Vire v0.8.2" + --notes-file src-tauri/target/release/bundle/dmg/Vire_0.8.2_aarch64.dmg`. It **MUST** + be a full release — do **not** pass `--draft` or `--prerelease`. +- [ ] Release notes MUST include: the DMG asset name; "unsigned/unnotarized"; the exact + `xattr -dr com.apple.quarantine /Applications/Vire.app` remedy; official-source trust caveat; + TASK-028 Developer-ID/notarization long-term pointer. MUST NOT include `sudo`, + `spctl --master-disable`, or any global-Gatekeeper-disable wording. **Do not** embed any + quarantine-bypass in the artifact (see `design.md` §xattr). + +## Part B — Verify the live behaviour + +- [ ] `gh release view v0.8.2 --repo kaikkone4/vire --json tagName,isDraft,isPrerelease,assets` → + `tagName=v0.8.2`, `isDraft=false`, `isPrerelease=false`, DMG asset present & downloadable. +- [ ] `curl -sS -o /dev/null -w "%{http_code}" https://api.github.com/repos/kaikkone4/vire/releases/latest` + → `200`; JSON `tag_name` = `v0.8.2`; `html_url` is the real release page. +- [ ] `https://github.com/kaikkone4/vire/releases` → `200`. +- [ ] In-app smoke, **0.8.1 build** (the currently distributed binary): "Check for updates" → + **UpdateAvailable** `{ current: "0.8.1", latest: "0.8.2", release_url: }`. +- [ ] In-app smoke, **0.8.2 build** (rebuilt from merged `main`): "Check for updates" → + **UpToDate** `{ current: "0.8.2" }` — never "downgrade available". +- [ ] Physical-Mac DMG smoke (mount → drag only `Vire.app` → `xattr -dr com.apple.quarantine` → + launch): standing human UAT carried from TASK-053/054 — record result; **not** a merge blocker. diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index f2843ba..4473cfb 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4525,7 +4525,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vire" -version = "0.8.1" +version = "0.8.2" dependencies = [ "chrono", "keyring", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index b0998e3..22a13ad 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vire" -version = "0.8.1" +version = "0.8.2" description = "Vire local time tracker" authors = ["Vire"] edition = "2021" diff --git a/src-tauri/deny.toml b/src-tauri/deny.toml index eb3e5b4..86750a6 100644 --- a/src-tauri/deny.toml +++ b/src-tauri/deny.toml @@ -67,6 +67,9 @@ ignore = [ "RUSTSEC-2025-0080", # unic-common 0.9.0 — unmaintained (rust-unic EOL); urlpattern -> tauri-utils "RUSTSEC-2025-0100", # unic-ucd-ident 0.9.0 — unmaintained (rust-unic EOL); urlpattern -> tauri-utils "RUSTSEC-2025-0098", # unic-ucd-version 0.9.0 — unmaintained (rust-unic EOL); urlpattern -> tauri-utils + # -- group (C): quick-xml XML-parsing DoS, SCORED 7.5 but NOT reachable in vire — see block below -- + "RUSTSEC-2026-0194", # quick-xml 0.39.4 — O(N^2) duplicate-attr DoS; plist -> tauri-utils (own Info.plist only) + "RUSTSEC-2026-0195", # quick-xml 0.39.4 — unbounded ns-decl alloc DoS; plist -> tauri-utils (own Info.plist only) ] # ---------------------------------------------------------------------------------- @@ -91,6 +94,46 @@ ignore = [ # `urlpattern`/`tauri-utils` release drops the rust-unic dependency or if any of these advisories # is upgraded to a scored CVE. +# ---------------------------------------------------------------------------------- +# ACCEPTED — APPLE-PRESENT, SCORED advisories (group C; quick-xml XML-parsing DoS) +# ---------------------------------------------------------------------------------- +# Added TASK-057 (2026-07-03). UNLIKE group (B), these are SCORED CVSS 7.5 HIGH vulnerabilities +# (not unmaintained-class). They are accepted here on a documented UNREACHABILITY basis, not +# because they are low-severity in the abstract. Read the rationale before touching this block. +# +# ID Crate (locked) Class CVSS Fix Apple reachability +# ------------------ ----------------- ----- ---- ----------- ----------------------------------- +# RUSTSEC-2026-0194 quick-xml 0.39.4 DoS 7.5 >= 0.41.0 plist -> tauri-utils/-codegen/-build +# RUSTSEC-2026-0195 quick-xml 0.39.4 DoS 7.5 >= 0.41.0 plist -> tauri-utils/-codegen/-build +# +# 0194: O(N^2) duplicate-attribute scan in BytesStart::attributes() — a start tag with ~10^5 +# attributes burns minutes of CPU (algorithmic-complexity DoS). +# 0195: NsReader accumulates namespace declarations with no upper bound — a crafted start tag +# forces unbounded heap allocation (memory-exhaustion DoS). +# +# WHY ACCEPTED — reachability / trust boundary: +# * quick-xml enters the graph ONLY via `plist` (verified: `cargo tree -i quick-xml +# --target aarch64-apple-darwin` shows plist as the sole parent). `plist` is consumed +# exclusively by Tauri's `tauri-utils` / `tauri-codegen` / `tauri-build` for Apple +# property-list (Info.plist) handling — mostly build/codegen time, plus tauri-utils metadata. +# * In that usage the XML parsed is the application's OWN Info.plist: developer-authored and +# sealed inside the signed .app bundle at build time. It is never network-, user-, or +# attacker-supplied. Both advisories are remote DoS against services that ingest UNTRUSTED +# XML at scale (the 0195 PoC OOM-killed NLnet Labs Routinator's concurrent RRDP workers). +# vire is a single-user local macOS desktop app with no untrusted-XML ingestion path, so the +# CVSS-7.5 network-attacker vector is not reachable. +# * vire's first-party code calls neither quick-xml nor plist (grep-verified across src/, +# src-tauri/src/). Foreground-app bundle IDs in active_window come from AppKit APIs, not XML. +# * NO upstream fix is available: plist 1.9.0 is the latest plist release and hard-pins +# `quick-xml = "^0.39.2"` (< 0.40), so a quick-xml >= 0.41 bump is resolver-REJECTED +# (`cargo update -p quick-xml --precise 0.41.0` fails); no plist release consumes quick-xml +# >= 0.41; tauri 2.11.2 is already the latest 2.x. There is no dependency/lockfile move that +# clears these without an upstream plist release — hence acceptance, not upgrade. +# Review trigger (ANY of): (i) plist ships a release built on quick-xml >= 0.41 — bump and DROP +# these two IDs; (ii) vire (or a Tauri path it enables) begins parsing untrusted/remote XML — +# reachability changes, re-evaluate immediately; (iii) either advisory is re-scored or superseded. +# See openspec/changes/task-057-publish-v082-release/ops-review.md. + # ---------------------------------------------------------------------------------- # DEFERRED — LINUX-ONLY advisories (group A; NOT compiled into the shipped macOS .app) # ---------------------------------------------------------------------------------- diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 2097746..6a6ea6a 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Vire", - "version": "0.8.1", + "version": "0.8.2", "identifier": "dev.vire.app", "build": { "beforeDevCommand": "npm run dev",