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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/source-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,39 @@ jobs:
- run: npm ci
- run: npm test
- run: npm run build

linux-desktop:
name: Linux GTK desktop
runs-on: ubuntu-24.04
timeout-minutes: 45
env:
CARGO_BUILD_JOBS: "2"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22.23.2
cache: npm
- name: Install GTK and WebDriver prerequisites
run: |
sudo apt-get update
sudo apt-get install -y build-essential pkg-config libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev libssl-dev libxdo-dev webkit2gtk-driver xvfb dbus-x11
- name: Install pinned Rust and verification tools
run: |
rustup toolchain install 1.98.1 --profile minimal
rustup default 1.98.1
cargo install cargo-audit --version 0.22.2 --locked
cargo install tauri-driver --version 2.1.0 --locked
- run: npm ci
- run: npm run build
- run: node scripts/build-desktop.mjs --community
- name: Compile the native GTK desktop
run: cargo build --locked --manifest-path src-tauri/Cargo.toml --features community-desktop,tauri/custom-protocol
- name: Inspect the Linux GLib dependency path
run: cargo tree --locked --manifest-path src-tauri/Cargo.toml --target x86_64-unknown-linux-gnu --features community-desktop,tauri/custom-protocol -i glib
- name: Audit the complete desktop lockfile
run: cargo audit --file src-tauri/Cargo.lock --deny unsound
- name: Exercise the native editor
run: dbus-run-session -- xvfb-run -a -s '-screen 0 1440x1000x24' node scripts/linux-native-editor-smoke.mjs src-tauri/target/debug/editkin
36 changes: 36 additions & 0 deletions docs/BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,42 @@ The Rust and Tauri source is included for development. The desktop media pipelin

Do not distribute a binary as an official Editkin release based solely on a passing web build or source test. A public installer needs a fresh, exact-artifact review: third-party corresponding source and notices (especially FFmpeg), platform-specific build and edit/export testing, final hashes and SBOM, a verified release identity, and provenance attestation. The current packaged Windows updater specifically requires Authenticode; an unsigned community build cannot use that updater. [RELEASE.md](RELEASE.md) tracks the available release paths.

### Linux community desktop (GTK 0.19 migration)

Use Node.js 22.13+ on `PATH`, Rust 1.92 or newer (verification used 1.98.1), a C/C++ toolchain, and `pkg-config`. GTK **Rust crate** 0.19 still targets system GTK **3**, not GTK 4. The locked Linux features require GTK 3.24+, GLib/GIO 2.70+, WebKitGTK 4.1 API 2.40+, JavaScriptCoreGTK 4.1 API 2.38+, and libsoup 3.0+. These minimums come from the enabled sys-crate features and their `system-deps` metadata.

Ubuntu 24.04 prerequisites:

```sh
sudo apt-get update
sudo apt-get install -y build-essential pkg-config libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev libssl-dev libxdo-dev webkit2gtk-driver xvfb dbus-x11
npm ci
npm run build
node scripts/build-desktop.mjs --community
CARGO_BUILD_JOBS=2 cargo build --locked --manifest-path src-tauri/Cargo.toml --features community-desktop,tauri/custom-protocol
cargo tree --locked --manifest-path src-tauri/Cargo.toml --target x86_64-unknown-linux-gnu --features community-desktop,tauri/custom-protocol -i glib
cargo install cargo-audit --version 0.22.2 --locked
cargo audit --file src-tauri/Cargo.lock --deny unsound
cargo install tauri-driver --version 2.1.0 --locked
dbus-run-session -- xvfb-run -a -s '-screen 0 1440x1000x24' node scripts/linux-native-editor-smoke.mjs src-tauri/target/debug/editkin
```

For aarch64, use `aarch64-unknown-linux-gnu` in the tree command. Ubuntu 26.04 calls the WebDriver package `webkitgtk-webdriver`. The smoke harness resolves Node on `PATH` to its canonical executable and configures the existing debug resident-service `EDITKIN_NODE_PATH`; it does not relax executable validation.

[Ubuntu 24.04 GTK](https://packages.ubuntu.com/noble/libgtk-3-dev) 3.24.41, [GLib](https://packages.ubuntu.com/noble/libglib2.0-dev) 2.80.0, [WebKit/JavaScriptCore](https://packages.ubuntu.com/noble/libwebkit2gtk-4.1-dev) 2.44.0 or newer, and [libsoup](https://packages.ubuntu.com/noble/libsoup-3.0-dev) 3.4.4 satisfy the selected feature minimums. This is a package/manifest compatibility check, not an executed Ubuntu 24.04/x86_64 native result; that job remains to be exercised in CI.

The actual locked aarch64 Linux path is Editkin → Tauri 2.12.0 → GTK 0.19.0 → GLib 0.22.10, with Tauri runtime/runtime-wry 2.12.0, Tao 0.37.0, Wry 0.57.0, Muda 0.20.0, and WebKitGTK Rust 2.0.2. The whole lockfile has one version of each GTK-family crate, including optional tray dependencies; no GTK 0.18/GLib 0.18 chain remains. Optional tray dependencies are present in the lockfile but are not enabled by the app or exercised by the native smoke. JavaScriptCore Rust 2.0.0 and Soup Rust 0.9.0 complete the migration.

These are unreleased upstream migration revisions, pinned by full immutable Git commit in [Cargo.toml](../src-tauri/Cargo.toml): [Tauri #16170](https://github.com/tauri-apps/tauri/pull/16170), [Tao #1332](https://github.com/tauri-apps/tao/pull/1332), [Wry #1843](https://github.com/tauri-apps/wry/pull/1843), [WebKitGTK #167](https://github.com/tauri-apps/webkit2gtk-rs/pull/167) (including #166), [Muda #403](https://github.com/tauri-apps/muda/pull/403), [tray-icon #369](https://github.com/tauri-apps/tray-icon/pull/369), and [libappindicator #53](https://github.com/tauri-apps/libappindicator-rs/pull/53). Replace them with compatible registry releases only after rechecking the complete chain, audit, native build, and smoke.

### Observed native verification and limits

On 2026-09-30, Ubuntu 26.04 aarch64 with Rust 1.98.1, Node 22.22.1, GTK 3.24.52, WebKitGTK 2.52.6, and system GLib 2.88.0 compiled and ran the real community desktop under Xvfb/D-Bus. The native WebKit page used `tauri://localhost` and real IPC. The public synthetic demo accepted caption text “Linux native caption edit” and duration 5, undo restored duration 3, redo restored 5, and autosave/recovery preserved the exact edit and the original 12-second demo clip/asset. Final evidence is under ignored `.rd/tmp/linux-native-editor-0wGr6t/`; the exercised binary SHA-256 was `b4e713aea4d13e93e6dfba5c9b72ccc68ecba4b6eb026388e83e381950b3cd74`.

The inspected final screenshot showed the edited caption on the timeline and duration 5 in the inspector. The preview was hidden by a native ffprobe error card reporting the missing relative path `editkin-demo-preview.mp4`; rendered preview captions and playback were not verified. The public demo fixture exists in the checkout/build, but its relative media URI did not resolve through the native media path. CJK glyphs were tofu, and the absent private Creator Pack remained unavailable/loading. This does **not** prove full rendering or media playback, import, codecs/GPU, export, native dialogs, installers, signatures, private speech models, Windows, macOS, Wayland, or an exact delivered official artifact. No private assets are needed for this limited public smoke.

The community service builder bundles the real service, and the explicit Cargo feature generates schema 1 identity with scope `editkin.community-desktop-build/v1` and `officialRelease: false`. It is not a fallback official manifest. An unflagged native build still requires the owner's schema 2 `.release-input-manifest.json`; even spoofing the ambient `CARGO_FEATURE_COMMUNITY_DESKTOP` variable does not bypass the compile-time feature guard.

## Source asset provenance

- `public/demo-source.mp4`, `public/editkin-demo-preview.mp4`, and the two benchmark MP4s were generated from FFmpeg `lavfi` test patterns for this source edition. See [FIXTURES.md](FIXTURES.md).
Expand Down
8 changes: 8 additions & 0 deletions docs/RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ The source repository does not publish an official installer. The maintainer mus
6. The current packaged Windows updater requires Authenticode. An unsigned build must leave automatic installer updates disabled until an independently reviewed project-key update design is implemented and tested against tampering, rollback, and a previous release. Never relax the current signature check merely to ship an unsigned installer.

Passing CI or a static scan cannot replace these checks. Contributions may be merged while official binary publication remains closed.

## Community desktop is not an official release

The explicit `community-desktop` Cargo feature embeds a generated schema 1 identity, scope `editkin.community-desktop-build/v1`, with `officialRelease: false`. Its service bundle is real, but it cannot satisfy the owner-only schema 2 formal product identity or the private product/runtime gates. An unflagged build fails when `.release-input-manifest.json` is absent, including with a spoofed ambient `CARGO_FEATURE_COMMUNITY_DESKTOP=1`; the feature boundary is compile-time.

The GTK 0.19 / GLib 0.22 migration uses immutable unreleased upstream PR revisions rather than a GLib 0.18 backport. Passing the dependency audit and the observed Ubuntu 26.04 aarch64 native caption/timing/undo/redo/recovery smoke is not an official release-ready claim. Compatible registry releases still need a reviewed upgrade and renewed verification.

The native screenshot showed the edited caption on the timeline and duration 5 in the inspector, but a native ffprobe missing-relative-path error card hid the preview; rendered preview captions and playback were not verified. CJK glyphs were tofu and private Creator Pack content was unavailable. Optional tray dependencies are locked but not enabled by the app or exercised by the smoke. No full rendering/playback, import/export, codecs/GPU, dialogs, delivered installer, signing, private model, Windows/macOS/Wayland, or Ubuntu 24.04/x86_64 result is claimed. See [BUILDING.md](BUILDING.md) for reproducible commands, system-library minimums, and measured scope. Official binary publication remains blocked until every gate above is met for the exact delivered artifact.
10 changes: 10 additions & 0 deletions docs/SECURITY_MODEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ No automated verifier can prove that a pull request contains no malicious logic.
5. **Runtime boundary:** external Skills, plugins, models, and media are untrusted inputs. Review requested permissions, use explicit user installation, and keep private user files and credentials out of telemetry and logs. A repository badge or popularity does not grant trust.

The community source build currently has no signed installer claim. The 39 integration suites listed in [source-test-exclusions.json](../source-test-exclusions.json) need external runtimes, the separate video-autopilot skill, or generated release products; the default CI result does not cover them. The Windows Authenticode installer suite runs only on Windows. See [RELEASE.md](RELEASE.md) for the remaining official binary gate.

## GTK dependency advisory remediation

[RUSTSEC-2024-0429](https://rustsec.org/advisories/RUSTSEC-2024-0429.html) concerns unsound `glib::VariantStrIter` iteration in GLib Rust versions `>=0.15, <0.20`; the fixed range is `>=0.20`. The original desktop lockfile resolved GLib 0.18.5 through Tauri 2.11.5 → GTK 0.18.2, including the native window/event-loop, embedded WebKit, and menu dependency chain. Absence of a direct application `VariantStrIter` call is not proof that the advisory is unreachable.

The full GTK 0.19 dependency migration now resolves GLib 0.22.10. The Cargo-generated lockfile has no old or duplicate GTK-family versions, including optional tray dependencies. It uses reviewed immutable upstream PR pins listed in [BUILDING.md](BUILDING.md); these unreleased revisions require provenance review and eventual migration to compatible registry releases, not blind dependency overrides.

Observed `cargo-audit` 0.22.2 results with `--deny unsound`: the original lockfile failed with RUSTSEC-2024-0429 (and six unmaintained warnings); the migrated complete lockfile passed with 493 dependencies and no warnings. The advisory database revision was `f23b768236fe2880e4cfa167da662cad8ca79240` (1,277 advisories), and the audited lockfile SHA-256 was `c9846f69fb3c7d1fe5d5f751d1e8521b1f8e9d5ebd8c9b6c3f835c765216f9bf`. An audit is time-specific, not proof of all runtime safety.

Source CI retains pinned Actions, read-only permissions, and no signing/publication secrets. Its Linux job builds the explicit non-official community feature, inspects the locked GLib path, denies unsound advisories, and exercises the real native editor under Xvfb/D-Bus. The observed local native proof is Ubuntu 26.04 aarch64 only; Ubuntu 24.04/x86_64 CI is not yet an observed result. Community schema 1 identity (`officialRelease: false`) cannot replace the formal schema 2 release gate. The public `remote-relay.json` supplies the required safe LAN/BYO policy without endpoints or credentials; it does not authorize a private service or weaken runtime input validation.
85 changes: 46 additions & 39 deletions scripts/build-desktop.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const production = {
legalComments: "none",
};

// A source-only desktop smoke needs the real service, not owner release assets.
const community = process.argv.includes("--community");

await Promise.all([
"desktop-dist/main.mjs.map",
"desktop-dist/preload.cjs.map",
Expand All @@ -16,37 +19,39 @@ await Promise.all([
"desktop-dist/remote.mjs.map",
].map((path) => rm(path, { force: true })));

await build({
entryPoints: ["electron/main.ts"],
bundle: true,
platform: "node",
target: "node22",
format: "esm",
outfile: "desktop-dist/main.mjs",
external: ["electron"],
...production,
});
if (!community) {
await build({
entryPoints: ["electron/main.ts"],
bundle: true,
platform: "node",
target: "node22",
format: "esm",
outfile: "desktop-dist/main.mjs",
external: ["electron"],
...production,
});

await build({
entryPoints: ["electron/preload.ts"],
bundle: true,
platform: "node",
target: "node22",
format: "cjs",
outfile: "desktop-dist/preload.cjs",
external: ["electron"],
...production,
});
await build({
entryPoints: ["electron/preload.ts"],
bundle: true,
platform: "node",
target: "node22",
format: "cjs",
outfile: "desktop-dist/preload.cjs",
external: ["electron"],
...production,
});

await buildMaterialColorBundle({
entryPoints: ["src/mcp/server.ts"],
bundle: true,
platform: "node",
target: "node22",
format: "esm",
outfile: "desktop-dist/mcp.mjs",
...production,
});
await buildMaterialColorBundle({
entryPoints: ["src/mcp/server.ts"],
bundle: true,
platform: "node",
target: "node22",
format: "esm",
outfile: "desktop-dist/mcp.mjs",
...production,
});
}

await build({
entryPoints: ["src/service/cli.ts"],
Expand All @@ -61,14 +66,16 @@ await build({
...production,
});

await build({
entryPoints: ["src/remote/server.ts"],
bundle: true,
platform: "node",
target: "node22",
format: "esm",
outfile: "desktop-dist/remote.mjs",
...production,
});
if (!community) {
await build({
entryPoints: ["src/remote/server.ts"],
bundle: true,
platform: "node",
target: "node22",
format: "esm",
outfile: "desktop-dist/remote.mjs",
...production,
});

await import("./build-release-input-manifest.mjs");
await import("./build-release-input-manifest.mjs");
}
Loading
Loading