Some checks failed
deploy-site / deploy (push) Failing after 1m15s
Auto-update could not work at all: the installer was 189 MB because it carried the local speech engine and ffmpeg, and the download feed rejects uploads over about 100 MiB, so update metadata could never be published. The installer now leaves those components out and the app fetches them the first time they are needed, verifying every part and the joined archive before installing. The installer is 90.6 MiB, the update feed is published again, and updates stay small because the engine is not re-sent on every release. The fetch is visible and recoverable: the download runs with progress, a failed install cleans up after itself, and Settings > STT shows the runtime status with a manual download action for when the automatic one cannot run.
133 lines
No EOL
9.3 KiB
Markdown
133 lines
No EOL
9.3 KiB
Markdown
# 서명 없이 배포하기 — D3RO Voice 배포 경로 정리 (2026-09 기준)
|
|
|
|
> 왜 이 문서가 있나: MSIX/NSIS 설치본은 public-trust Authenticode 서명이 필수다. 인증서가
|
|
> 없으면 릴리스 파이프라인이 fail-closed로 멈춘다(실측: `v1.2.0`·`v1.3.0` 태그 모두 서명
|
|
> 가드에서 실패). 그동안 사용자가 설치할 수 있는 경로가 필요해 조사하고 구현한 결과를 남긴다.
|
|
> MSIX 계획을 버리는 문서가 **아니다** — 서명이 준비되면 기존 계획을 그대로 간다.
|
|
|
|
---
|
|
|
|
## 1. 제약 (실측)
|
|
|
|
| 제약 | 값 | 근거 |
|
|
|---|---|---|
|
|
| canonical feed 업로드 본문 한도 | **100MiB (104,857,600 bytes)** — 초과 시 HTTP 413 | Cloudflare 뒤에 있음. 실측: 60MiB → 201, 110MiB → 413 (274ms, CF 오류 페이지) |
|
|
| 1.1.0 설치본이 통과한 이유 | 102,172,129 bytes = **97.4MiB** (한도 미만) | 같은 feed의 `latest.yml` |
|
|
| 사이드카 포함 앱 크기 | unpacked 688MB → zip 243MiB / 7z 162MiB | `apps/desktop/release/<v>/win-unpacked` 실측 |
|
|
| 이 PC의 Smart App Control | **꺼짐** (`VerifiedAndReputablePolicyState = 0`) | SAC가 켜져 있으면 서명 없는 바이너리는 채널과 무관하게 실행 차단 |
|
|
| Forgejo generic registry | `HEAD` 미지원(405), `Range: bytes=0-0` 지원(206 + content-range) | 크기/불변 검증은 Range GET으로 한다 |
|
|
|
|
**결론**: 서명 여부와 별개로, 100MiB를 넘는 산출물은 이 feed로 게시할 수 없다. 즉
|
|
사이드카를 포함한 NSIS 설치본(189MB)은 **인증서가 있어도 지금 게시할 수 없다**. 이건
|
|
서명과 무관한 별도 결함이며, 앱 크기를 줄이거나(엔진 분리 다운로드) 게시 경로를 바꾸는
|
|
작업이 필요하다(§4 백로그).
|
|
|
|
---
|
|
|
|
## 2. 서명 없이 설치되는 방법 비교 (2025-2026)
|
|
|
|
| 방법 | SmartScreen | Smart App Control | 관리자 | 자동 업데이트 |
|
|
|---|---|---|---|---|
|
|
| 대용 ZIP | 최초 실행 경고(다운로드 MOTW 전파) | **차단** | 불필요 | 없음 |
|
|
| **Scoop** | 패키지 매니저가 직접 내려받아 MOTW 없음 → 경고 없음 | **차단** | 불필요(per-user) | `scoop update *` + manifest `autoupdate` |
|
|
| winget | 대개 경고 없음 | **차단** | 설치 방식에 따름 | `winget upgrade` (manifest 등록 필요) |
|
|
| 서명 없는 NSIS/Inno | "Windows protected your PC" → Run anyway | **차단** | 사실상 필요 | 없음 |
|
|
| MSIX self-signed 사이드로드 | 서명 없는 것과 같음 | **차단** | 필요(인증서를 TrustedPeople에) | `.appinstaller` (ms-appinstaller는 기본 비활성) |
|
|
| Velopack(per-user Setup.exe) | 서명 없으면 자주 경고 | **차단** | 불필요 | 내장 UpdateManager |
|
|
|
|
핵심: **Smart App Control이 켜진 PC에서는 어떤 무서명 경로도 통하지 않는다.** SAC는
|
|
클린 설치 기본값이 켜져 있고, 끄면 다시 켤 수 없다(재설치 필요). 그래서 공개 배포의
|
|
정답은 여전히 "서명"이고, Scoop/휴대용은 SAC가 꺼진 환경(그리고 개발/테스트)에서
|
|
쓸 수 있는 보완 경로다.
|
|
|
|
---
|
|
|
|
## 3. 이 저장소가 택한 경로
|
|
|
|
### 3-1. 서명 없이 지금 배포되는 것 (portable 채널)
|
|
|
|
- 산출물 두 가지(같은 버라도 목적이 다름):
|
|
- **7z 분할 볼륨** `D3RO-Voice-<v>-x64-portable.7z.001/.002` — LZMA2로 688MB → 162MiB. Scoop 전용.
|
|
- **zip 분할 부품** `...zip.001/.002/.003` — 243MiB. 수동 설치 스크립트용. Windows 내장
|
|
`Expand-Archive`만으로 풀 수 있어 사용자가 7-Zip을 설치할 필요가 없다.
|
|
- 게시 위치: `.../generic/d3ro-voice/portable-<version>/` 와 `.../portable-latest/`
|
|
(**자동 업데이트 피드 `latest.yml`과 완전히 분리**).
|
|
- 설치 방법 두 가지:
|
|
1. **Scoop** — 저장소의 `bucket/` 디렉토리를 버킷으로 쓴다. Scoop은 `.7z.001` 볼륨을
|
|
이어서 해제하는 기능을 공식 지원한다(7-Zip 볼륨).
|
|
```powershell
|
|
scoop bucket add d3ro https://git.chanpaca.net/yunchan/d3ro-voice.git
|
|
scoop install d3ro/d3ro-voice
|
|
```
|
|
2. **수동 설치 스크립트** — `scripts/install/install-d3ro-voice.ps1` (피드에도 게시됨).
|
|
zip 부품을 내려받아 부품별 SHA-256 검증 → 결합 → 결합본 SHA-256 재검증 →
|
|
`Expand-Archive`로 해제 → `%LOCALAPPDATA%\Programs\D3RO Voice`에 설치 + 시작 메뉴 바로가기.
|
|
관리자 권한 불필요, 추가 도구 불필요(Windows 10/11 기본).
|
|
```powershell
|
|
irm https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-latest/install-d3ro-voice.ps1 | iex
|
|
```
|
|
- 자동화: `.forgejo/workflows/portable.yml` (태그 + 수동 실행). `WIN_CSC_*`가 필요 없다.
|
|
- 커맨드: `npm run release:portable:build` → `npm run release:portable:check` → `npm run release:portable`
|
|
|
|
**안전 규칙(구현에 반영)**:
|
|
- 볼륨은 불변 — 같은 버전 경로에 다른 바이트가 있으면 게시 중단.
|
|
- 메타데이터(`portable.json`, 설치 스크립트)만 갱신 허용.
|
|
- 같은 버전을 다시 빌드하면 7z/zip 바이트가 달라져(내부 타임스탬프) 게시가 중단된다.
|
|
즉 **한 버전의 portable 산출물은 한 번의 빌드에서만 나온다**. 태그 파이프라인(CI)이 최초
|
|
게시자가 되도록 하고, 로컬 재게시로 채널을 덮어쓰지 않는다. 이미 게시된 세트를 갱신해야 하면
|
|
버전을 올린다(이 문서 작성 시 `1.3.0` 부분 게시분을 `1.3.1`로 대체).
|
|
- 파일명에 `-portable`을 넣어 서명된 릴리스 자산과 혼동되지 않게 한다.
|
|
- `latest.yml`/`update-policy.json`은 절대 건드리지 않는다(자동 업데이트는 서명 릴리스 전용).
|
|
|
|
### 3-2. 서명이 준비되면 (원래 계획 유지)
|
|
|
|
- MSIX/NSIS는 그대로 간다. 필요한 것은 public-trust 인증서 하나다.
|
|
- 2026년 기준 가장 싼 현실적 선택:
|
|
- **Azure Artifact Signing(구 Trusted Signing)** — Basic **$9.99/월**, 하드웨어 토큰 불필요,
|
|
EXE/MSI/**MSIX** 서명 가능, SmartScreen/SAC 대응. 단 **개인은 미국/캐나다 거주자만**,
|
|
조직은 한국 포함 특정 국가에서 가능(사업자 검증 필요). CI는 `signtool` + dlib 또는
|
|
`azure/artifact-signing-action`(Windows 러너)로 연동.
|
|
- **SSL.com OV 코드 서명** — 약 $129/년(+클라우드 HSM/eSigner 별도), 사업자 필요.
|
|
- 참고: EV가 SmartScreen을 즉시 통과시키는 경로는 2024년에 폐지됐다. 평판은 누적된다.
|
|
|
|
---
|
|
|
|
## 4. 남은 백로그
|
|
|
|
| ID | 항목 | 왜 |
|
|
|---|---|---|
|
|
| GAP-REL-04 | 사이드카 포함 설치본이 Cloudflare 100MiB 한도를 넘는다(NSIS 189MB) | 인증서가 있어도 게시 불가. 크기를 줄이거나 게시 경로를 바꿔야 한다 |
|
|
| GAP-STT-07 | 엔진(사이드카) 첫 실행 다운로드 방식으로 분리 | 앱 번들을 100MiB 이하로 만들고, 엔진은 분할 다운로드 + SHA-256 검증으로 받는다. 설치 경험과 업데이트 크기가 모두 좋아진다 |
|
|
| GAP-REL-05 | winget 매니페스트 등록 | winget-pkgs 커뮤니티 저장소 제출 필요(100MiB 한도와 무관한 별도 경로) |
|
|
|
|
---
|
|
|
|
## 5. 자동 업데이트가 다시 동작한다 (2026-09-18)
|
|
|
|
- 설치본에서 로컬 AI 런타임(사이드카 94.4MiB, ffmpeg 21.7MiB)을 분리했다 → 설치본 **90.6MiB**.
|
|
- 그래서 `latest.yml` + 설치본을 canonical feed에 게시할 수 있게 되어 **자동 업데이트가 복구**됐다
|
|
(`1.3.2` 게시). 서명이 없어 electron-updater는 `publisherName` 부재로 서명 검증을 건너뛴다
|
|
(`node_modules/electron-updater/out/NsisUpdater.js:84-99`).
|
|
- 런타임은 `runtime-<version>` / `runtime-latest`에 게시되고 앱이 처음 필요할 때 내려받는다
|
|
(부품별 + 결합본 SHA-256 검증, tar 해제, 실패 시 부분 설치 정리).
|
|
- 주의: 무서명 stable 게시는 명시적 예외이며 `--ack-unsigned` 없이는 스크립트가 거부한다.
|
|
- 1.0.x 이하 설치본은 여전히 legacy GitLab mirror를 보고 있으므로 **1회 수동 설치**가 필요하다.
|
|
|
|
## 6. 채널 현황 (2026-09-18)
|
|
|
|
- 게시된 채널: `portable-1.3.1` + `portable-latest` (7z 볼륨 2개, zip 부품 3개, 인덱스, 설치 스크립트).
|
|
- `portable-1.3.0`에는 7z 볼륨 2개만 있다(부분 게시, zip 부품 없음) — `1.3.1`이 대체한다.
|
|
- updater feed(`latest.yml`)는 여전히 `1.1.0`이며 **이 채널은 그것을 건드리지 않는다**.
|
|
|
|
## 6. 검증 기록 (2026-09-18)
|
|
|
|
- 7z 볼륨 게시 후 **무인증 공개 GET**으로 인덱스/볼륨/스크립트 제공 확인.
|
|
- 설치 스크립트 end-to-end 실행: 볼륨 2개 다운로드 → SHA-256 검증 → 결합(162.1MiB) →
|
|
7-Zip 해제 → 설치 디렉토리에 `D3RO Voice.exe`, `resources/sidecar/sidecar.exe`,
|
|
`resources/sidecar/_internal/faster_whisper/assets/silero_vad_v6.onnx`, `resources/sox/sox.exe`
|
|
존재 확인 → 시작 메뉴 바로가기 생성. (검증 후 테스트 설치/바로가기는 제거)
|
|
- 재게시 시도 시 동일 볼륨은 "이미 동일한 파일" 로 건너뛰는 것 확인(불변 가드 동작).
|
|
- 1.3.1 수동 설치 스크립트를 **7-Zip 없이** end-to-end 실행: zip 부품 3개 다운로드 →
|
|
부품별 SHA-256 → 결합(242.9MiB) → 결합본 SHA-256 → Expand-Archive → 설치 디렉토리에
|
|
`D3RO Voice.exe`, `resources/sidecar/sidecar.exe`, VAD `silero_vad_v6.onnx`, `resources/sox/sox.exe`
|
|
확인 → 시작 메뉴 바로가기 생성. (검증 후 테스트 설치/바로가기 제거) |