d3ro-voice/docs/map/02-infrastructure.md
Yun Chan 0ca9e242fa
Some checks failed
deploy-site / deploy (push) Failing after 1m26s
fix(site): serve the landing domain from the Pages deployment
d3ro.chanpaca.net answered an empty Cloudflare 404 because the account had no
Pages project for the landing site, and attaching the custom domain still needs
a DNS CNAME that the local Cloudflare credentials cannot create. Add a small
Workers route bridge that serves the Pages deployment on that hostname so the
domain works now and keeps following Pages deploys, and record the cleanup
left to do.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4.1-flash
2026-09-19 09:21:27 +09:00

18 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 check:desktop-renderer[:test] # built renderer pages reference only assets on disk
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. Every pipeline that runs npm run build --workspace=@d3ro/desktop (.forgejo release/portable, .github CI/release) then runs scripts/ci/verify-desktop-renderer-bundles.mjs, which fails packaging when a renderer page references an asset the build did not emit (GAP-INFRA-05).

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.pages.dev), verify live commit/version, app-links, legal URLs. 커스텀 도메인 d3ro.chanpaca.net 은 Pages 커스텀 도메인이 DNS CNAME을 요구하므로, DNS를 건드릴 수 없는 동안은 Workers 라우트 브리지 server/cloudflare-site-bridge/(d3ro.chanpaca.net/* → Pages 프록시, 수동 npx wrangler deploy)가 서빙한다. CNAME을 추가한 뒤 브리지를 삭제하면 Pages 커스텀 도메인으로 직접 서빙된다(GAP-REL-09b). 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랜딩/다운로드 센터(2026-09-19부터 Pages d3ro 배포본을 Workers 라우트 브리지가 서빙; 그 이전에는 바인딩이 없어 빈 404였다), 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 (NAS 포털/API는 현재 이 호스트네임에 바인딩되어 있지 않다).


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.7, androidVersionCode/iosBuildNumber 1031007, 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/web/src/lib/desktop-release.ts, site/src/release.ts Download-center desktop release contract (installer filename + release date); version and date are kept on the SSOT by npm run version:sync (drifted to 1.2.0 once — GAP-REL-08)
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).