d3ro-voice/docs/map/02-infrastructure.md
Yun Chan c35c6f3e95
Some checks failed
deploy-site / deploy (push) Failing after 4m9s
feat(release): ship an install path that needs no code-signing certificate
Installers could not be published at all: the signing certificate does not
exist yet, and the release pipelines stop at their signing guard. Users had no
way to install a fixed build, so the product was effectively stuck behind a
certificate that takes weeks to obtain.

There is also a second, independent blocker: the download feed sits behind
Cloudflare, which rejects any upload body over about 100 MiB, and the app with
its speech engine exceeds that even when signed.

A portable channel now publishes what can actually be delivered today: the app
compressed into 95 MiB 7z volumes (162 MiB total instead of 243 MiB), a Scoop
bucket for a normal install and uninstall experience, and a verifiable manual
installer script. It is deliberately separate from the auto-update feed, needs
no certificate, and refuses to overwrite an already published version.
2026-09-18 11:03:34 +09:00

16 KiB

02 — Repository & Infrastructure Map

Surface: whole repo Source of truth for: layout, workspaces, build/test commands, CI/CD, Docker, deploy, scripts, resources


1. Repository layout

D:/workspace/D3ROVoice
├── apps/
│   ├── desktop/          Electron app (npm workspace @d3ro/desktop)
│   ├── web/              Next.js console (npm workspace @d3ro/web)
│   ├── admin/            Next.js back office (npm workspace @d3ro/admin)
│   ├── mobile-rn/        React Native product mobile app (NOT an npm workspace)
│   ├── api-server/       ASP.NET Core 10 API (D3ROVoice.Api)
│   ├── api-server.Tests/ xUnit tests for the API
│   └── admin-swagger/    Static Swagger UI + openapi.json
├── packages/             Shared TS packages (npm workspaces)
│   ├── core/             @d3ro/core — types, errors, IPC channels, crypto, utils
│   ├── ui/               @d3ro/ui — web/desktop design system (MUI + tokens)
│   ├── ui-native/        @d3ro/ui-native — React Native design system
│   ├── i18n/             @d3ro/i18n — 12 locales, provider, formatters
│   └── api-client/       @d3ro/api-client — Supabase wrapper + shared types
├── server/
│   ├── supabase/         Supabase project: config.toml, migrations/, functions/, tests/
│   └── cloudflare-worker/ Edge gateway (wrangler.toml + src/index.ts)
├── site/                 Vite landing site (deployed to Cloudflare Pages + GitHub Pages)
├── resources/            icons/ (empty), sox/ (bundled Windows SoX binaries)
├── release/              Version identity SSOT + license/evidence public keys
├── scripts/              ~145 automation scripts + scripts/ci/ (31) + scripts/lib/
├── docs/                 Design, phases, v2/v3 plans, deployment, map/ (this map)
├── memory/               Project status, handoffs, archive
├── tests/e2e/, test-results/   Root-level e2e + last-run artifacts
├── scratch/              Large local evidence (APKs, screenshots, DBs) — not build input
├── supabase/             Empty scaffolding (.branches/, snippets/) — real project is server/supabase
├── .github/workflows/    GitHub Actions (6)
├── .gitlab-ci.yml        GitLab CI (primary desktop/mobile release pipeline)
├── .forgejo/workflows/   Forgejo Actions (site deploy to Cloudflare Pages)
├── docker-compose.yml, docker-compose.nas.yml
├── Dockerfile.admin, apps/api-server/Dockerfile, apps/admin/Dockerfile
├── turbo.json, tsconfig.json, tsconfig.base.json, pnpm-workspace.yaml
├── package.json          monorepo root, npm workspaces
├── CLAUDE.md             Claude-specific project rules
└── AGENTS.md             Cross-agent entry: rules + map obligation (this map's anchor)

Note: root package.json declares npm workspaces apps/desktop, apps/web, apps/admin, packages/*. pnpm-workspace.yaml also exists (apps/*, packages/*) but npm is the active toolchain. Mobile is intentionally outside the workspace.


2. Toolchain & versions

Tool Version Source
Node 24.19.0 .nvmrc
TypeScript 5.7 root package.json
.NET SDK 10.0.300 (rollForward latestPatch) global.json
Deno 2.8.1 CI (edge-functions-quality)
JDK 17 mobile CI
Electron 33.4.11 apps/desktop/electron-builder.yml
React Native 0.85 apps/mobile-rn/package.json
Turborepo turbo.json tasks: build/typecheck/test/lint/dev turbo.json

3. Root scripts (package.json)

npm run dev                     # desktop dev (electron-vite)
npm run build                   # desktop production build
npm run build:admin             # admin production build
npm run build:all | npm run ci  # scripts/ci/build-all.mjs
npm run checksum                # scripts/ci/generate-checksums.mjs
npm run version:check | version:sync
npm run release:metadata[:test]
npm run release:forgejo[:check]  # canonical Forgejo publisher/feed
npm run release:tag             # annotated/signed immutable release tag
npm run security:secrets[:test] # hardcoded-secret scanner
npm run test:e2e:red            # content-report red e2e
npm run release:mobile:boundary[:test]
npm run release:mobile:config[:test]
npm run release:mobile:build-config:test
npm run release:play:assets[:test]
npm run typecheck               # all workspaces
npm run test                    # all workspaces
npm run lint                    # eslint apps/desktop apps/web apps/admin packages
npm run format                  # prettier
npm run typecheck:mobile        # apps/mobile-rn tsc (outside npm workspaces)
npm run lint:mobile             # apps/mobile-rn eslint --max-warnings=0
npm run test:mobile             # apps/mobile-rn jest
npm run verify:all              # aggregate: workspaces + mobile (typecheck/lint/test)

Per-app commands that matter:

App Commands
desktop npm run dev --workspace=@d3ro/desktop, build, typecheck, test (vitest), playwright e2e; local STT engine: npm --prefix apps/desktop run sidecar:setup then sidecar:build (PyInstaller → sidecar-dist/sidecar), full local Windows package: npm --prefix apps/desktop run dist:win:full; setup:sox re-downloads the bundled SoX
mobile-rn npm run typecheck:mobile / lint:mobile / test:mobile (root), or npm --prefix apps/mobile-rn run lint/typecheck/test; android gradle builds, Maestro E2E
api-server dotnet build, dotnet test (also apps/api-server.Tests)
web next build, playwright e2e in apps/web/e2e
admin next build (build:admin)

Desktop GUI execution rule (from CLAUDE.md / .agents/rules/): run via run-desktop.bat or an external terminal; do not background-launch GUI from an agent subshell.


4. Shared packages

See 03-shared-packages.md. Summary:

Package Provides
@d3ro/core Domain types, D3ROError/ErrorCode, IPC channel SSOT, constants, crypto-license, pii-redactor, secure-memory, supabase-config, meeting-markdown, markdown-to-docx
@d3ro/ui Theme tokens, CSS vars, MUI DS components (web/desktop)
@d3ro/ui-native RN design system (MetalCard, PhosphorText, Led, PhysicalButton, WaveBars, …)
@d3ro/i18n 12 locales, I18nProvider, t(), date/number/relative formatters
@d3ro/api-client Supabase browser/server clients, meetings/history/usage/transcribe wrappers, shared types; has vitest tests

5. CI/CD

GitHub Actions (.github/workflows/)

Workflow Purpose
ci.yml Main CI: code-quality (secret scan, mobile release/config/build-config self-tests, Play asset contract, lint, typecheck), api-server-tests, edge-functions-quality (Deno), test-matrix (win/mac/ubuntu vitest), build-validation (desktop win, admin ubuntu), mobile-android (debug/CSPRNG/E2E APKs + verifiers), mobile-emulator-e2e (API 35 + Maestro 2.7.0)
release.yml On tag v*.*.*: preflight → package-windows (NSIS) → package-macos (DMG/ZIP arm64) → package-android (signed APK/AAB + evidence) → package-admin-docker (GHCR) → publish-release (checksums + GitHub Release + Forgejo canonical publish)
deploy-site.yml On site/**: build Vite site, boundary self-test, deploy GitHub Pages
build-mac.yml Manual macOS build (arm64/x64), sox + PyInstaller sidecar + electron-rebuild
payple-renew.yml Daily cron → payple-renew edge function
release-signing-ca.yml Manual Windows (Azure Trusted Signing) / macOS notarize build+sign

GitLab CI (.gitlab-ci.yml)

Stages validate → test → build → e2e → package → publish → deploy. Primary pipeline for desktop Windows/macOS releases (Forgejo Generic Registry is the canonical updater feed; GitLab project 1172 is a legacy mirror) and production mobile releases (mobile-production-release, manual/protected). Admin NAS deploy job is intentionally disabled. package-windows/package-macos build the faster-whisper sidecar (sidecar:setupsidecar:build) and run scripts/ci/verify-sidecar-bundle.mjs before electron-builder, so a release can never ship without the local STT engine.

Forgejo Actions (.forgejo/workflows/)

portable.yml — 태그/수동 실행으로 서명 없이 portable 채널(95MiB 7z 분할 볼륨 + Scoop 매니페스트 + 설치 스크립트)을 게시한다. WIN_CSC_* 불필요, updater feed는 건드리지 않는다.

deploy-site.yml / deploy-site-windows.yml — build site, write release identity, deploy to Cloudflare Pages d3ro (d3ro.chanpaca.net), verify live commit/version, app-links, legal URLs. release.yml — tag-triggered Windows build (signed) + publish-forgejo-release.mjs to the canonical Forgejo feed/release hub.


6. Docker & deployment

File Purpose
Dockerfile.admin 3-stage Next.js admin build (node 24.19.0-alpine, port 3001)
apps/admin/Dockerfile Next.js standalone runner for .next/standalone
apps/api-server/Dockerfile Multi-stage .NET 10 (sdk → aspnet runtime), port 5000, VOLUME /app/data
docker-compose.yml Dev/self-host: d3ro-api-server (5050→5000, ./data volume), d3ro-admin (3001), optional ollama (profile ai, 11434)
docker-compose.nas.yml NAS: prebuilt d3ro-voice-api:latest + d3ro-voice-admin:latest; API mounts /volume1/docker/d3ro/wwwroot/{privacy,terms,delete-account,legal.css} read-only

Deploy scripts: scripts/deploy-nas.ps1, scripts/deploy-nas.sh, scripts/deploy-site-to-nas.js, scripts/nas-control.sh (start/stop/restart/status/logs/backup/update).

Public endpoints (production): https://d3ro.chanpaca.net (portal/API), https://admin.chanpaca.net (admin CRM). Edge: server/cloudflare-worker proxying to the NAS origin, plus a Cron Trigger (* * * * *) that drains the Supabase push outbox via send-push?mode=drain (src/push-drain.ts; needs SUPABASE_URL var + SUPABASE_SERVICE_ROLE_KEY secret). Tunnel: Cloudflare Tunnel kd-nas.


7. server/supabase (backend)

  • config.toml — project d3ro-voice, ports 55321-55324, DB major 17, auth redirects (localhost, d3ro.chanpaca.net, d3ro-voice://auth-callback), providers Google/GitHub/Apple.
  • migrations/63 SQL migrations (schema, RLS, auth triggers, storage, team invites, knowledge/pgvector, push outbox, Payple/Stripe billing, admin roles, mobile platform/monetization, atomic command reorder, device revocation, content reporting, audit log, meeting documents, STT quota reservations, ad reward replay protection, team activity feed).
  • functions/~27 Deno Edge Functions (stt-proxy, llm-proxy, content-report, generate-meeting-document, embed-chunks, search-knowledge, realtime-token, team-invite, team-accept, send-push, account-delete, admin-users, admin-subscriptions, admin-payments, admin-audit-log, billing billing-catalog/stripe-checkout/stripe-portal/stripe-webhook/payple-checkout/payple-manage/payple-renew/payple-webhook, iap-verify, admob-ssv, google-play-rtdn). Shared contracts in functions/_shared/ — push transports now include webpush.ts (VAPID + RFC 8291) and apns.ts (.p8 token) alongside FCM. CI (edge-functions-quality) runs deno check + deno test and also the Cloudflare worker drain test.
  • tests/ — integration/E2E for content report, mobile platform/recording/reward-race, payments, mobile release preflight, push, team push security, STT quota.

Full detail: 09-supabase-backend.md.


8. scripts/ groups

  • CI (scripts/ci/, 33 files): build/version/release (build-all, sync-version, generate-checksums, verify-release-metadata, create-release-tag, extract-release-notes), security (check-no-hardcoded-secrets), mobile release gates (verify-mobile-release-boundary/-config/-build-config, verify-android-artifact/-app-links, verify-play-store-assets, prepare-whisper-model, create-mobile-release-evidence, prepare-mobile-release-publication, emulator/CSPRNG gates), keys (create-desktop-license-keypair, create-release-evidence-key, etc.), publish (publish-forgejo-release canonical, publish-gitlab-release mirror; legacy sync-and-publish-forgejo-release, upload-asset-to-forgejo-release), env/tooling (bootstrap-linux-toolchain.sh, audit-nas-stt-config.ps1, mobile local E2E scripts).
  • Deploy/release: deploy-nas.ps1/.sh, deploy-site-to-nas.js, nas-control.sh, publish-gh.ps1, gen-keystore.js.
  • GCP/Google OAuth automation + inspection (~70 *.mjs): auto-configure-oauth, automate-google-oauth, setup-consent, create-*-client, check-*, inspect-* — mostly one-off/browser-driven console automation.
  • AdMob console automation: admob-probe.mjs (read-only login/app/ad-unit probe), admob-login.mjs + run-admob-login.bat (one interactive headful Chrome login into a persistent profile), admob-automate.mjs (dry-run by default; --apply creates/verifies banner+rewarded units and reports Play-store link). Uses playwright with channel: 'chrome' and the gitignored .chrome-playwright-profile.
  • E2E / verification: e2e-desktop-*.js, real-app-multi-tab-e2e.js, test-and-capture-all-10-ad-services.js, verify-live-production-d3ro.js.
  • Screenshots/captures: capture-*.js.
  • Forgejo ops: check-forgejo-actions-runs.js, capture-forgejo-*.js.

scripts/ is large and partially scratch. Prefer scripts/ci/* for anything release-gated, and server/supabase/tests for backend integration.


9. Release & versioning SSOT

File Purpose
release/product-version.json version 1.3.0, androidVersionCode/iosBuildNumber 1030001, releaseDate, desktop license keyId
release/android-release-identity.json package com.d3ro.voice, Play app ID, app-signing SHA-256, upload cert SHA-256, evidence keyId, AdMob unit IDs
release/desktop-license-public.pem Ed25519 public key for desktop offline licenses
release/mobile-release-evidence-public.pem Ed25519 public key for mobile release evidence
apps/desktop/electron-builder.yml appId com.d3ro.voice, NSIS x64 (forced code signing), macOS DMG/ZIP arm64, generic Forgejo publish feed, asarUnpack native modules + @ffmpeg-installer, extraResources (icons, sounds, sox, sidecar, ffmpeg, ollama)
apps/desktop/src/main/update-feed.ts Auto-update feed SSOT (canonical Forgejo + legacy GitLab mirror, channels)
release/update-policy.json Update policy SSOT (channels, minimum supported version, forced update, delta/full, staged rollout, kill switch)
apps/desktop/src/main/update-policy.ts Policy parsing/decision logic
scripts/ci/publish-forgejo-release.mjs Canonical Forgejo registry + Release + feed publisher

Version sync is enforced by scripts/ci/sync-version.mjs and verify-release-metadata.mjs; npm run version:check should be clean.


10. Resources & tests

  • resources/sox/ — bundled Windows SoX (sox.exe + DLLs) for audio capture.
  • resources/ffmpeg/ — optional bundled ffmpeg (CI or manual); getFfmpegPath() also resolves the @ffmpeg-installer/ffmpeg binary from app.asar.unpacked.
  • apps/desktop/sidecar-dist/ — PyInstaller sidecar bundle consumed by extraResources (gitignored; built by npm --prefix apps/desktop run sidecar:build).
  • resources/icons/ — empty; electron-builder falls back to build/icon.ico|png.
  • Desktop tests: apps/desktop/tests/ (vitest unit + playwright e2e), apps/desktop/test-results/.
  • Mobile tests: apps/mobile-rn/__tests__/ (57 suites / 353 tests per mobile SSOT), .maestro/ + .maestro-output/ E2E evidence. Note: a full parallel Jest run can hit the 5s render timeout on slow machines; re-run the failing spec in isolation before treating it as a regression.
  • Web tests: apps/web/e2e/ (playwright).
  • API tests: apps/api-server.Tests/ (xUnit).
  • Root: tests/e2e/, test-results/.last-run.json.

11. Known infrastructure gaps

See 11-gap-backlog.md for the maintained list (INFRA-*). Headlines:

  • apps/mobile-rn is not an npm workspace member; use typecheck:mobile/lint:mobile/test:mobile or verify:all.
  • Admin NAS deploy job disabled in GitLab CI; admin ships via GitHub/GHCR + manual NAS compose.
  • Two identity systems (.NET JWT/SQLite vs Supabase); a canonical resolver now exists in @d3ro/core/entitlement but web/mobile/.NET adoption is incremental (11 GAP-ID-02).