1# Open source license notices
2
3License notices are generated independently for the client that ships them:
4
5- The web build emits `third-party-licenses.json` beside `index.html`. The Settings page loads that
6 static file, so the same artifact works in hosted web, the client bundled with `npx t3`, and
7 desktop.
8- The mobile Metro config generates an ignored virtual module before each development, native, or
9 over-the-air JavaScript bundle. Mobile loads and decodes that module only when a license screen
10 opens, so the notice text does not occupy memory during ordinary app startup. It does not need a
11 network request.
12
13Neither path depends on the connected environment or an RPC.
14
15## What the build collects
16
17The generator follows installed production and optional dependencies, including dependencies of
18workspace packages, and omits first-party `@t3tools/*` packages. The web manifest starts from the
19web, server, and desktop package manifests. The mobile manifest starts from the mobile package
20manifest. During the web bundle, the generator also checks emitted module ids to catch a bundled
21npm import missing from a package manifest.
22
23The mobile manifest deliberately follows the complete production dependency closure declared by
24Expo and React Native. That is conservative and can include build tooling that is not present in
25the final JavaScript bundle, but it avoids dropping a notice when platform bundling changes.
26
27The build fails when a collected package has no distributable license identifier or contains no
28license or notice text. Generated notices use license templates from the pinned SPDX License List,
29committed under `third-party-licenses/spdx/`. Builds never fetch them, so a network blip cannot
30fail a build. A build fails when a configured template is missing; run `vp run licenses:sync` to
31download it, then commit the new file.
32
33## Custom notices and package overrides
34
35The repository-level `third-party-licenses.config.json` holds manually maintained exceptions for
36all clients. Add an entry to `customNotices` for adapted icons, fonts, media, native modules, or
37another asset that did not come from an npm package:
38
39```json
40{
41 "name": "asset-name",
42 "license": "CC-BY-4.0",
43 "generatedNotices": [
44 {
45 "licenseId": "CC-BY-4.0",
46 "preamble": ["Asset by Example Author. Changes: converted to MP3."]
47 }
48 ],
49 "sourceUrl": "https://example.com/source",
50 "bundles": ["assets", "web"]
51}
52```
53
54Each `generatedNotices` item names an SPDX license template and can add `copyrights` or a short
55`preamble` for attribution and provenance. Multiple items are joined into one row for software
56that vendors separately licensed code. Keep `noticeFile` or `noticeFiles` only when a vendored
57source tree already carries an intrinsic license file that should remain beside it. Paths are
58relative to the config file. `bundles` controls which generated manifests include the entry and
59supplies the label shown to users. Use `includeInBundles` when those differ, such as an optional
60server tool that should appear in both client manifests but is not bundled into either client.
61
62The `agent-device` and `expo-device-hub` custom notices take their versions from the
63[installer pins](../../apps/server/src/device/deviceToolVersions.json). Keep their package names
64in the config; their versions do not need manual updates.
65
66Use `packageOverrides` only when an installed npm archive omits its notice or has incorrect
67metadata:
68
69```json
70{
71 "name": "package-name",
72 "version": "1.2.3",
73 "generatedNotice": {
74 "licenseId": "MIT",
75 "copyrights": ["Copyright (c) 2026 Example Author"]
76 },
77 "license": "MIT",
78 "sourceUrl": "https://example.com/package-name"
79}
80```
81
82For packages containing separately licensed code, use `generatedNotices` with an array of templates instead of `generatedNotice`. The generator includes every notice in the package row.
83
84`version`, `license`, and `sourceUrl` are optional. Omitting `version` applies the override to every
85installed version of that package. An override can use `repositoryUrl` instead of `name` when
86several packages from one monorepo share the same notice:
87
88```json
89{
90 "repositoryUrl": "https://github.com/example/project",
91 "generatedNotice": {
92 "licenseId": "Apache-2.0"
93 }
94}
95```
96
97The generator also reuses an installed sibling package's notice when both packages declare the
98same normalized repository and license. A name-and-version override always wins over these
99repository fallbacks.
100
101The `@react-grab/cli` override uses the root React Grab repository's MIT license because the CLI's
102npm archive omits both its license field and license file. Keep the override until the published
103CLI package carries that metadata itself.
104
105Generated mobile files live under `apps/mobile/.generated/` and are ignored. Do not commit or edit
106them; updating dependencies or configuration is enough for the next build to refresh the output.