Files
Piik/docs/reference/engineering.md
T
AAA 9150e9d312 feat(app): improve startup, diagnostics and direct connections (#397)
## Problem and outcome / 问题与结果

Machines with several local addresses could fail before opening Piik, and startup or connection failures were difficult to diagnose. This phase adds Local invitation-address selection and accessible diagnostics, preserves useful startup errors, and repairs media preparation and recovery without changing room authority or routing policy. It also aligns language handling, playback, public downloads and contributor documentation with the resulting behavior.

## Release notes

### 中文

- **多网卡也能正常选择启动方式。** 本地房间在需要时显示网卡名称与局域网地址,供你选择朋友所在的网络;公网邀请和连接已有站点不再因多个局域网地址而无法启动。
- **更容易排查启动和连接问题。** 启动页新增“诊断启动”,同时记录 App 与网页诊断;网页版也有独立诊断入口。启动失败会显示具体原因和重试方法,Windows 双击启动后的错误会停留等待确认。诊断模式下异常退出会保存报告,ZIP 由用户自行保留或清理。
- **减少连接准备中的无谓等待。** 普通候选发现不再等待可选端口映射;修正连接重启时旧候选与完成通知的处理,以及部分准备失败后的反馈。Native 媒体连接可在网络支持时使用 IPv6 直连,同时保留 IPv4 路径。
- **修正分享恢复中的状态问题。** 切换画面后重新计算首播保护所需的新帧;SFU 恢复时应用最新画质设置;一次应答发送失败不再使整个会话失去 Native 接收能力。密码错误提示也进行了去重。
- **改善播放与全屏交互。** 同步 Safari 原生全屏中的播放、音量与退出状态;可操作的视频画面显示手形光标。
- **统一语言匹配并完善翻译贡献。** 简体、繁体中文系统均默认显示简体中文,其余系统默认英文;保留中英文与纯视觉模式快捷切换,提供中英文社区翻译教程和额外语言接入方式。
- **整理下载与官网说明。** Release 附件集中展示三个平台的 App ZIP 和 Server 程序包,源码版本与校验值收进更新说明的折叠区。官网保留内嵌短片,并提供独立观看链接;教程、下载文案和运维指南同步更新。

**升级:** App 请完整解压对应平台的 ZIP 并保留目录结构。服务端按部署指南升级,保留现有配置与 SQLite 数据。此次更新不改变现有通信协议和存储格式。

**测试范围:** 主要验证 Windows App 与桌面浏览器。macOS、Linux 实机采集和移动端、跨公网 IPv6 的更广泛验证仍待完成,欢迎试用反馈。

### English

- **Choose how to start on machines with multiple network addresses.** Local rooms show interface names and invitation addresses when a choice is needed. Public invitations and connections to an existing Site no longer fail because several LAN addresses are available.
- **Diagnose startup and connection problems more easily.** The launcher now offers Debug launch for both App and browser diagnostics, alongside a separate browser diagnostics control. Startup failures include their cause and retry guidance; Windows errors remain visible for confirmation after a double-click launch. Failed exits in diagnostic mode save a report. Exported ZIPs remain available until you move or delete them.
- **Remove unnecessary connection setup waits.** Ordinary candidate discovery no longer waits for optional gateway mapping. Fixes cover stale candidates and completion signals during ICE restarts, and feedback from failed connection preparation. Native media can use direct IPv6 connections where available while retaining IPv4 paths.
- **Keep sharing recovery aligned with the current source and settings.** Source changes count new frames for startup protection, SFU recovery uses the latest quality preferences, and one failed answer submission no longer disables Native receiving for the entire session. Duplicate password-error feedback has also been removed.
- **Improve playback and fullscreen interaction.** Safari native fullscreen now synchronizes playback, volume and exit state. Interactive video surfaces show a pointer cursor.
- **Align language selection and community translation guidance.** Simplified and Traditional Chinese system locales select Simplified Chinese; other systems default to English. Chinese, English and visual-mode shortcuts remain, with bilingual translation guides and a registration path for additional languages.
- **Simplify release downloads and refresh the website.** Release attachments contain the three App ZIPs and the Server runtime archive; source identity and checksums appear in a collapsed section of the release notes. The website keeps its embedded introduction and adds a standalone viewing link, with updated setup, download and operations guidance.

**Upgrading:** Extract the complete App ZIP for your platform and keep its directory structure. Follow the deployment guide for Server upgrades, preserving existing configuration and SQLite data. Existing wire contracts and storage formats are unchanged.

**Test coverage:** Windows App and desktop browsers are the primary validated paths. Physical macOS/Linux capture, broader mobile coverage and IPv6 across separate public networks remain open; testing and feedback are welcome.

## Verification / 验证

- `npm run check`: PASS, including TypeScript, 807 tests in 60 files and the production Web build.
- `npm run check:client`: PASS on Windows, including Go formatting/tests/vet, Windows/Linux core builds and native Windows capture compile/probe. Darwin requires the native macOS CI runner.
- `npm run gate:client-media`: PASS with Chrome 152 and a real captured test window on Windows. Two receivers obtained video and audio; live and paused profile changes, recovery frames and cleanup passed. This is a single-machine test, not a public-NAT success-rate measurement.
- `npm run gate:client-link`: PASS using the newly packaged Windows App. A separate remote machine fetched its temporary public page and completed the WebSocket upgrade; App exit closed the link and cleanup passed.
- Server runtime and Windows App candidate packaging: PASS, including archive identity/checksums and execution from the extracted App ZIP. Native Linux/macOS package checks still run in the publishing pipeline before publication.
- `node scripts/check-docs.mjs`, `scripts/check-project-state.ps1`, `git diff --check` and `npm run build:website`: PASS. Seven documentation-size gardening advisories and the existing Vite chunk-size advisory remain non-blocking.
- Browser review: English/Chinese website setup and direct-download links, narrow layout and diagnostics/language controls checked. Native popover accessibility correctly exposes expanded/collapsed state without a duplicate attribute owner.
- Independent audit found no P0/P1/P2 issues; focused race checks for portmapping and mediaedge passed. Diagnostic archive accumulation is retained by the owner's explicit choice. The currently unreachable language menu placement issue is a first-community-language prerequisite in TODO.

Physical macOS/Linux capture, broader mobile behavior, restricted-network/endurance coverage and cross-network Native IPv6 remain explicitly outside this candidate's evidence.

## Impact / 影响

Compatible feature release, planned as v1.2.0 from v1.1.2. Browser/server v23, Native control v9, capture v7 and SQLite schema 2 remain unchanged. No new telemetry service, retry policy or configuration requirement is introduced. Public Demo remains open-entry and P2P-only. NAT-PMP dependency corrections are explicitly deferred to the next release and are not claimed here.

The existing product, configuration, translation, research and operations owners were updated. Raw audits, handoffs, diagnostic logs and experimental sources are excluded from the PR.
2026-09-13 18:32:24 +08:00

16 KiB

Engineering And Interface Boundaries

This is the shared engineering convention for Browser, App and Server work. AGENTS.md owns task authority and hard product constraints; CONTRIBUTING.md owns the delivery workflow. The required ponytail guidance selects the smallest correct implementation. This file owns module responsibilities and interface discipline, not a second copy of the product contracts or wire fields.

Repository Layout

Keep conventional project entry points and tool-discovered configuration at the root. Scope-specific configuration belongs with its owner: src/tsconfig.json checks the Browser, scripts/tsconfig.json checks tooling/tests, and root tsconfig.json only connects those projects.

Location What belongs here
Root README, contribution and agent files Project introduction and shared working constraints; detailed documents live in docs/
Root manifests, lockfiles, Vite/Vitest config, index.html Go/npm entry points and Browser build/test startup; root LICENSE is authoritative
src/client, src/shared Browser UI/media orchestration and its shared wire types; the same UI serves Hosted and App modes
public/ Static assets shipped with that Browser application
cmd/, internal/ Go executable composition and private application, server, media and diagnostics packages
native/capture, native/fixtures, native/probes Platform capture implementations, synthetic workloads and standalone capability probes
site/ Independently published static introduction; its assets do not implement product behavior
scripts/ Development checks, browser/media gates, dependency assembly and release packaging/publishing tools
tests/ TypeScript/tooling tests and cross-language fixtures; Go tests stay beside their packages
deploy/ Operator scripts and container, proxy, service and certificate templates
docs/ User guides, contracts, decisions and operations; documentation ownership defines each owner
licenses/ Third-party license texts, pinned notice sources and their redistribution reference
.github/, .githooks/, .agents/, .codex/ Platform automation and scoped agent/tool integration
build/, node_modules/, coverage/ Ignored local output and dependencies; never current product truth

site/ introduces Piik; src/client/ is Piik's interactive application. internal/app/nativecapture adapts the capture contract in Go; native/capture/ implements it using platform APIs. scripts/ prepares and verifies artifacts; deploy/ operates them. Move files with their actual consumers, and update paths and checks together; a smaller root alone does not justify another abstraction.

Module Map

Owner Responsibility and dependency boundary
cmd/piik-server, cmd/piik-app Thin entry points: defaults, startup and shutdown; compose the same Go room service
internal/server/app HTTP, site access, runtime capabilities, static assets and listener lifecycle; room mutations go through signal
internal/server/signal Authenticated command/effect owner; serializes room, session and route mutations and executes controller decisions
internal/server/room Room authority, credentials and persistence; caller holds the signaling lock, durable writes precede in-memory changes
internal/server/route Synchronous room graph and single-operation decisions; returns resource effects to signal, owns no locks or timers, and calls its optional diagnostic sink synchronously
internal/server/sfu, internal/app/mediaedge Server forwarding and native peer adapters over the shared internal/media/forwarding; Pion/LiveKit own transport and adaptation
internal/app Local/public-link/Site composition, loopback service, native sessions and App lifecycle; never a second room backend
internal/app/nativecapture, native/capture/* App-side capture contract and minimal platform adapters; platform-specific capture/encoding stays here
src/client/pages, lib, media, native Browser orchestration and endpoint adapters; bind current media and retire owned resources
src/client/media/viewer-presentation.ts, ui/media-status.ts Playback facts and their derived status projection; UI indicators do not create recovery or quality policy
src/client/components, locales, ui Shared interaction, visual primitives and localized copy; same product interface for Server and App
internal/server/webassets One built Browser bundle embedded by both binaries; Node/Vite are development tooling

Names follow naming. Architecture decisions explain why these boundaries exist: shared core, embedded media and Browser pooling. Directory names are a map, not a requirement to introduce another layer.

Contract Map

Boundary Schema/validation owners
Room HTTP and Browser/Server signaling src/shared/protocol.ts and internal/server/protocol; HTTP dispatch in internal/server/app, WS dispatch in signal
Site access HTTP src/client/lib/api.ts and internal/server/app/json.go; cookies and access behavior belong to rooms/access
Browser/App discovery and control src/client/native/wire.ts, internal/app/loopback/protocol.go, internal/app/nativecontrol/wire.go
App/capture sidecar internal/app/nativecapture and each platform capture adapter; probe/commands and encoded-frame envelope are distinct formats
Configuration and persistence internal/server/config, internal/app/config, internal/server/room/database.go; operator semantics in configuration
Release and update metadata Existing packaging scripts, src/client/lib/release-update.ts, cmd/piik-server/release.go; identity/compatibility in versioning, operations in deployment

The shared tests/fixtures/wire-samples.json is consumed by TypeScript and Go fixture tests. It covers room requests and signaling; it is not a universal schema for all HTTP, Native or capture traffic. Change the actual two ends and the relevant existing fixture/check together. Do not create another handwritten field list in documentation, parallel DTO hierarchy or schema generator without a demonstrated reduction in ownership or drift.

Runtime Lifecycles

Read each runtime as acquisition, the authoritative commit that makes its work visible, and the retirement that ends it. Revalidate the acquiring identity after every await or callback; presentation derives from the committed fact instead of mirroring it.

Runtime Acquire Commit Retire
Browser Host share pages/HostPage.tsx captures one source and creates its peers and route current generation plus the owning identity: local-offer epoch, publisher slot revision, source-switch and quality-change tokens disposeResources stops owned tracks, disposes peers and sends stop-sharing; a replaced source retires through retiringStreamRef
Browser Viewer route pages/ViewerPage.tsx binds one parent peer (Browser or Native) and its relay children parent, connection and message identity, then playback facts a new viewer generation retires the old binding; relay children follow activateChildren revisions
Browser relay fanout webrtc/viewer-relay.ts prepares a child before it is needed activateChildren(revision, childPeerIds) commits the prepared set replaced or emptied sets disconnect the child peers they own
Browser local encoding pool webrtc/host-peer.ts attaches a pool carrier when capture and APIs support it (ADR-0014) the carrier binding owns its pooled producer while attached source change or disposal releases the carrier; unsupported APIs keep ordinary senders
App native session the loopback service admits native control sessions and the Web UI selects the native path session identity plus the explicit media-path choice session end, share end or process exit retires that edge and its capture sidecar
Server room authority signal authenticates one Host or Viewer command room writes durably before the in-memory commit; the signaling lock serializes mutations explicit replacement or deletion, grant rotation or revocation, or process exit for memory-mode rooms
Server route and SFU signal feeds route one serial child operation; sfu/admission.go accepts one bounded publication one committed graph per controller and the forwarding publication superseded assignments retire inside the same operation; deactivate retires the publication

Source Index

Start a change at the owning entry point, then follow the interfaces listed above rather than searching the tree.

Change Start at
Host workflow, share lifecycle, notices src/client/pages/HostPage.tsx, src/client/pages/host-page-notices.ts
Viewer workflow, playback, status projection src/client/pages/ViewerPage.tsx, src/client/media/viewer-presentation.ts, src/client/ui/media-status.ts
Browser peer and route mechanics src/client/webrtc/host-peer.ts, viewer-peer.ts, viewer-relay.ts, src/client/media/host-sfu-route.ts, viewer-sfu-route.ts, route-transition.ts
Capture, profiles and sender parameters src/client/media/quality.ts, src/client/media/browser-encoding-pool.ts, browser-encoding-output.ts
SFU endpoints src/client/sfu/publisher.ts, subscriber.ts, peer.ts, internal/server/sfu
Localized copy and failure facts src/client/locales, src/client/ui/copy.ts, src/client/ui/media-failure.ts
Native bridge, capture and media edges src/client/native, internal/app/loopback, internal/app/nativecontrol, nativehost, nativeviewer, nativecapture
Room authority, persistence and graph internal/server/signal, internal/server/room, internal/server/route
Shared wire contract src/shared/protocol.ts, internal/server/protocol, tests/fixtures/wire-samples.json
Diagnostics and logging src/client/lib/debug.ts, internal/diagnostics, configuration
Configuration and deployment internal/server/config, internal/app/config, configuration, deployment

Implementation Rules

  • Reuse a current owner, standard API or mature dependency before adding a mechanism. Extract around a responsibility shared by real callers; file size alone does not justify wrappers, a generic manager or a framework. Remove superseded paths in the same change; retain no unused legacy copy. Keep test-only state seeding and setup in test harnesses. Do not export mutable resources solely for tests; preserve the production construction path when verifying defaults or restoration.
  • Review dependency direction for source/type imports as well as runtime imports. Shared types belong to a common owner. A type-only cycle is an ownership concern; it does not by itself establish a runtime or performance defect.
  • State has one writer and a clear lifetime. Separate permission, capability, connection readiness, requested settings, applied settings and observation. Derive presentation from facts; do not synchronize parallel booleans or let a cached failure outlive its evidence. Cumulative observations need a valid baseline for the resource generation they describe; missing measurements are not zero. Serialized recovery reads current committed intent when it executes rather than replaying a queued snapshot.
  • The operation that acquires a connection, clone, listener or queue owns its retirement. After an await/callback, and inside cleanup, validate the original operation/resource identity before changing current state. Cancellation, failure, missing observation and success must retain distinct meanings. A caller deadline does not cancel a dependency that ignores it. Keep late work bounded and serialize its cleanup; verify the dependency's actual lifetime.
  • Keep decisions separate from effects. Respect documented lock ordering and revalidate after unlocked work. Local native capability never grants room authority; the shared Go service remains authoritative. Injected callbacks still execute in their caller's context; include their blocking I/O in the caller's lock and latency analysis.
  • Validate input at the boundary, keep domain error meanings stable, and map them into shared localized UI. Versioning determines which extensions are compatible; an optional field is not automatically compatible. Retained media state carries failure facts; the presentation layer resolves localized copy during render. Keep raw exceptions in diagnostics. Transient event notices may resolve once when the event occurs. Values with a defined canonical form, such as HTTP origins, use one shared normalizer before storage and comparison. Verify the actual producer and consumer together instead of weakening an authorization comparison.
  • Deployment configuration describes available services; room preferences request allowed behavior. The server enforces the policy even when a client requests more. The UI retains the same controls and explains locked choices. See configuration; avoid a second flag with the same job.
  • Browser/App/Server share visual language and status projection. A new page, locale or deployment does not invent a new palette, status model or user-role metaphor.
  • Keep package comments about current responsibilities and invariants. Historical migration paths belong in ADR/research unless they explain a surviving constraint. Support Windows, macOS and Linux; new text files use LF and new filenames use ASCII, with format exceptions in .gitattributes. Scripts use stable executable paths; physical confidence stays explicit in verification status.
  • Retained tools declare their direct package dependencies. Documented TypeScript tools join the normal type-check entry point; a transitive dependency or a one-off manual check is not an owned toolchain contract. Shipped templates use reserved example values. Checks provision isolated resources or require explicit targets; they must not depend on a maintainer's workstation, private deployment or machine-specific infrastructure names.

Ablation And Review

Judge a change by verified user value against implementation, maintenance, compatibility, runtime and failure cost. After a material module, remove any new state, dependency, branch or abstraction that the accepted behavior does not need. A small performance gain does not justify permanent complexity; do not reduce viewing quality or requested functionality merely to save lines.

A concrete reduction in duplicated writers, cross-module dependencies or repeated edit sites can justify structural refactoring without a reproduced runtime failure. Name that gain and its regression risk. A completed audit is bounded evidence, not a claim that every current module boundary is optimal.

Before changing production recovery or state, trace the real request producers and their guards. A test that bypasses disabled controls or writes private state does not alone establish a reachable product defect. Reference scans, including hits from tests and previews, are leads rather than proof of product use or absence. Follow the actual producer-to-consumer path and check the changed boundary; isolated helper tests can miss a mismatch between the two ends.

Trace acquisition, use, commit and retirement, including replacement and failure. For async ownership, check A starting, B replacing it, then A completing or cleaning up. Where compatibility is promised or being established for public release, check actual old/new readers as well as the happy path. Ordinary private changes do not require support for stale private clients. Run proportionate checks through the standard entry points. This discipline does not authorize an unrelated repository rewrite or a new test suite for every helper.