Ctrl K
ComunidadGuías…

THIRD_PARTY_LICENSES.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# MailFathom third-party license register

This file records the license review for the third-party components MailFathom actually pins, bundles, runs, or calls. It is a review register: it names components, states the terms verified for them, and records a compatibility decision. It reproduces no license text and grants no rights of its own, and it is not legal advice.

It describes software in use and nothing else. A planned, evaluated, or rejected component has no row here, because a row asserts a completed review of something that exists in the repository today.

Four documents describe MailFathom's licensing, and each answers a different question:

| Document | Question it answers | Status |
|---|---|---|
| `LICENSE` | Which rights MailFathom grants for its own code | The unmodified AGPL-3.0 text, SPDX identifier `AGPL-3.0-only`. Never edit it: GitHub detects the license by matching that file, and an edit turns a detected `AGPL-3.0` into `NOASSERTION`. |
| `NOTICE` | Who originally created the work, which section 7(b) permits a distribution to require be preserved | Present, and informational only. Third-party notice text belongs beside it in the bundle below, never inside it. |
| `THIRD_PARTY_LICENSES.md` — this file | Which third-party components MailFathom uses, under which terms, and whether those terms fit both distribution models | Maintained here. |
| A generated third-party notice bundle | The full license texts and attribution notices that must travel with a distributed binary, container, or source artifact | Not produced yet. Issue #191 owns generating it from each artifact's resolved dependency graph; the obligations it must satisfy are the ones recorded in this file. |

The register is deliberately not the notice bundle. A notice bundle is generated from the resolved dependency graph of a specific artifact and contains verbatim license texts; this file is the human decision record behind it, and it exists whether or not an artifact has been built.

A redistribution therefore carries three separate things: the project grant in `LICENSE`, the original-author attribution in `NOTICE`, and whatever notices the components below require. Never merge third-party notice text into `NOTICE`, because that would attribute third-party work to MailFathom's author.

`0.1.0` has been released, so the redistribution obligations recorded here are outstanding rather than anticipated: every component the container image and the Helm chart carry has been distributed, and the notice bundle issue #191 owns is a live obligation against a published artifact rather than a task ahead of the first one.

## License acceptance policy

MailFathom must remain compatible with both contemplated distribution models:

1. commercial closed-source distribution; and
2. open-source publication of the MailFathom project.

A third-party component may be used only when its license permits both, without forcing MailFathom itself to be distributed under a copyleft license.

**MailFathom's own licence moved from Apache-2.0 to AGPL-3.0-only in 2026 and this policy did not move with it.** The two are independent, and the reading that a copyleft project may now consume copyleft dependencies is the one this paragraph exists to refuse. What MailFathom grants for its own code is the owner's to change — `CLA.md` is what keeps it changeable — and what a dependency grants is not, so a single copyleft dependency would end model 1 permanently and no later decision could undo it. Every row below is therefore written against model 1 exactly as it was before the relicensing. [ADR 0025](https://github.com/fathomise/MailFathom/blob/main/docs/decisions/0025-publishing-mailfathom-under-agpl-3-0-only.md) records the move and this consequence of it.

Allowed licenses include permissive OSI-approved licenses such as MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, PostgreSQL License, and similarly permissive licenses after review.

Licenses that require review before use include LGPL, MPL, EPL, Unicode, custom commercial licenses, source-available licenses, dual licenses, preview license terms, and any license with field-of-use, redistribution, network-service, data-use, patent, trademark, telemetry, or attribution conditions that are not already understood for this project.

Prohibited without explicit owner approval are strong copyleft or source-available licenses that could conflict with closed-source commercial distribution or require relicensing MailFathom, including GPL, AGPL, SSPL, BUSL, Commons Clause, PolyForm Noncommercial, and similar restrictions. AGPL is on that list as a dependency licence whatever MailFathom itself is published under.

A permissive SDK license never approves the hosted service, model, container image, or trademark behind it. Those terms are reviewed separately and recorded separately.

[ADR 0016](https://github.com/fathomise/MailFathom/blob/main/docs/decisions/0016-third-party-licence-obligations-per-artifact.md) states how a component in the review-before-use band is decided, and the three rules it adds are what the rows below are written against. The unit of review is `(component, version, artifact)` rather than the repository, because a redistribution obligation attaches to what is handed to somebody else; the exposure sections below are the coarse form of that, and a component carrying a condition is decided against the artifact rather than against its section. A component that resolves but reaches no artifact is **latent**, which is a decided state rather than a pending review: its row says so, names the event that makes it live, and states what is then owed. And a conditioned component stays **separately replaceable** — unmodified, its own file in the artifact, never merged into MailFathom's own output, trimmed, rewritten, statically or ahead-of-time linked, or sealed in a bundle a recipient cannot open — with the component dropped from an artifact whose build shape cannot allow that, rather than the condition waived for it.

## How the inventory is produced

Two questions decide every row here, and they are answered separately. **What earns a row** is a decision this repository took: a component it **pins** in a manifest, **invokes** from a script, a workflow, or a skill, **calls** by name from a published artifact at run time, or **ships** — inside an artifact, or as the runtime an artifact published here is written to run on. Something a contributor installs on their own machine, that this repository neither pins nor invokes nor ships, gets no row: an editor, a language server, a shell, a container desktop, a recommended editor extension. Usefulness earns nothing, and neither does being named in a setup document — prose that helps a contributor configure their machine describes that machine, while a row asserts a completed review of a version this project chose and would notice going stale. A row for software nobody here decided is worse than no row, because it makes the absence of a row unreadable. Where the two readings pull apart — a tool a document recommends but no script runs — the rule decides and the prose stays: it keeps its version and its reasoning, and stops promising a row.

**What a row costs** is decided by exposure, which is what the rest of this section is about. A package that ships inside the product carries redistribution and notice obligations; one that only runs on a build agent or a developer machine does not. That distinction is derived from the repository rather than assumed, which is why the sections below are ordered by exposure and why each was produced by a command that can be repeated:

```bash
# Direct pins and the transitive closure they resolve to.
cat backend/Directory.Packages.props
jq -r '.dependencies[] | to_entries[] | select(.value.resolved != null) | "\(.key) \(.value.resolved)"' \
  backend/src/*/packages.lock.json backend/tests/*/packages.lock.json | sort -u


# License expression, license URL, and upstream repository, from each package's own published metadata.
curl -s "https://api.nuget.org/v3-flatcontainer/<id-lowercase>/<version>/<id-lowercase>.nuspec"

# What is actually redistributed: the assemblies a framework-dependent publish of the host emits.
dotnet publish backend/src/Host/Host.csproj -c Release -o <output>
ls <output>/*.dll

# Upstream license of a component that is not a NuGet package.
gh api repos/<owner>/<repo>/license --jq '.license.spdx_id + "  " + .html_url'

# The client stack's direct pins, and the licenses of the closure they resolve to.
cat frontend/package.json frontend/src/*/package.json
pnpm --dir frontend licenses list

# License expression and upstream repository, from a package's own published metadata.
curl -s "https://registry.npmjs.org/<name>/<version>" | jq -r '.license, .repository.url'

# What a bundle actually carries, which is the production closure and nothing the workspace installs to build or test.
pnpm --dir frontend licenses list --prod

# The JavaScript census: how many packages under each term, and every one of them that is not MIT.
pnpm --dir frontend licenses list --json | jq -r 'to_entries[] | "\(.value | length)\t\(.key)"' | sort -rn
pnpm --dir frontend licenses list --json \
  | jq -r 'to_entries[] | select(.key != "MIT") | .key as $terms | .value[]
           | "\($terms)\t\(.name) \(.versions | join(" "))"' | sort

# The desktop shell's direct pins, and the license every crate the lock file resolves declares for itself.
cat frontend/src-tauri/Cargo.toml
cargo metadata --manifest-path frontend/src-tauri/Cargo.toml --format-version 1 --locked \
  | jq -r '.packages[] | "\(.license // "UNDECLARED")\t\(.name) \(.version)"' | sort

# What the Android head's APK packages, resolved rather than declared: Gradle prints the whole tree with each
# conflict-resolved version after an arrow, so the arrow is what the last stage reads back onto the artifact it names.
frontend/src-tauri/gen/android/gradlew --project-dir frontend/src-tauri/gen/android --quiet --console=plain \
  :app:dependencies --configuration universalDebugRuntimeClasspath \
  | sed 's/ (\*)$//; s/ (c)$//; s/ (n)$//' \
  | grep -oE '[a-zA-Z0-9._-]+:[a-zA-Z0-9._-]+:[0-9][A-Za-z0-9._-]*( -> [0-9][A-Za-z0-9._-]*)?' \
  | awk -F' -> ' '{ if (NF == 2) { n = split($1, p, ":"); print p[1] ":" p[2] ":" $2 } else { print $1 } }' | sort -u

# The crate census, and which of those crates a Linux desktop build links out of the graph Cargo resolves for every
# target it knows about.
cargo metadata --manifest-path frontend/src-tauri/Cargo.toml --format-version 1 --locked \
  | jq -r '.packages[].license // "UNDECLARED"' | sort | uniq -c | sort -rn
cargo tree --manifest-path frontend/src-tauri/Cargo.toml --locked --target x86_64-unknown-linux-gnu \
  --edges normal --prefix none --no-dedupe | sed 's/ (\*)$//' | awk 'NF >= 2 { print $1 }' | sort -u
```

Two of the three client enumerations answer for the machine they run on, which is a property to state rather than a defect to correct — the Android one does not, Gradle resolving the same artifacts from the same declarations wherever it runs. `pnpm licenses list` reads what is installed, so it names the platform-specific optional packages this machine selected — one `lightningcss-*`, one `@tauri-apps/cli-*`, one `@rollup/rollup-*` — and not the ones another platform would take instead; the terms are the package's own either way, so a per-platform binary changes which file is installed rather than which licence applies. `cargo metadata` is the opposite and names every target's crates at once, which is why the `cargo tree` line beside it exists: the difference between the two is the difference between what Cargo resolved and what a Linux build links.

`AppHost` and `IntegrationTests` carry no lock file, because the Aspire SDK selects part of their graph from the host platform's runtime identifier. Their packages are pinned centrally like every other project's and are recorded below from `backend/Directory.Packages.props` and the project files instead.

## The client's three dependency closures

The client brings three graphs this register had never carried a shape for, and each is orders of magnitude larger than anything the service pins: 276 packages behind the pnpm workspace, 502 crates behind the native shell's five direct pins, and 81 Android artifacts behind the five the committed Gradle project declares. Everything below decides how those are recorded, and it is written here once rather than restated in each of the rows below that depend on it.

**The granularity is a direct pin per row and a census per closure, and the reason is what a row asserts.** Every other row in this file names a component this repository *chose* — somebody read its terms, decided it was allowed, and wrote down what a distribution then owes. A transitive closure is mostly packages nobody chose: `scheduler` arrives because React depends on it, `foldhash` because something under Tauri does, and `androidx.tracing` because `androidx.appcompat` does. Giving each of those a row of its own would put eight hundred and fifty-nine assertions of a completed review into a file where a row *means* a completed review, and it would bury the fifty decisions that were real — forty npm pins, five crates, and five Android declarations — under the consequences of taking them. Giving the closure no record at all is the opposite failure, because the terms that travel with an artifact are the closure's terms rather than the direct pins'. So each closure is recorded as a census — how many packages resolve under which terms, with every distinct term named and every package carrying a condition named individually — and each direct pin keeps its row. A package under a permissive, unconditional term is counted rather than named, because counting it is the whole of what its terms oblige; a package under anything else is named, reviewed, and decided, because that is what a row does. A census is checkable: the commands above reproduce it, and a number that no longer matches is a graph that moved.

**Naming follows the package identifier rather than the project's own capitalisation.** The client rows write `react`, `react-dom`, `vite`, `typescript`, `prettier`, and `vitest` because those are the names a manifest, a lock file, and a registry use, and `scripts/update-dependencies.sh` reads this file by matching them. A row headed *ReactDOM* is a row the survey reports as missing.

**What each census currently says**, read from the committed lock files on 2026-10-13, when the Tauri pins moved and the Android project was regenerated from the template they carry:

- **The JavaScript closure resolves 276 packages at 290 name-version pairs**, and every one of them declares terms — the census has no unknown bucket. 203 are MIT. The rest are Apache-2.0 (37), ISC (11), BSD-2-Clause (8), BSD-3-Clause (3), `Apache-2.0 OR MIT` (3, the Tauri CLI, its native binding, and `@tauri-apps/api`, all taken under MIT), MPL-2.0 (2), MIT-0 (2), BlueOak-1.0.0 (2), Unlicense (1, `fetch-cookie`), `MIT OR Apache-2.0` (2, `@tauri-apps/plugin-opener` and `@tauri-apps/plugin-notification`, taken under MIT and the same choice spelled the other way round), CC0-1.0 (1), and CC-BY-4.0 (1). The Apache-2.0 bucket grew by sixteen when the client's OpenTelemetry pipeline was pinned, and those sixteen are the first packages under a term other than MIT to reach a bundle — which is why the redistributed row below is the one that decides them. The MIT bucket grew by twelve and the Unlicense bucket appeared when `@microsoft/signalr` was pinned, all thirteen being that package and the closure it resolves for a Node runtime. The MIT bucket grew by one more when `markdown-to-jsx` was pinned, which resolves nothing behind it. It grew by seven when the lint pins moved: `eslint`'s file cache now resolves eight packages it did not — `cacheable`, `@cacheable/memory`, `@cacheable/utils`, `@keyv/bigmap`, `@keyv/serialize`, `hashery`, `hookified`, and `qified` — while `json-buffer` left with the `keyv` they replaced, and all nine are build tooling no bundle carries. The closure shrank by twelve when the unit runner moved a major, and every one of the fourteen names that left was the runner's own or the coverage provider's: `@vitest/expect`, `@vitest/pretty-format`, `@vitest/runner`, `@vitest/snapshot`, `@vitest/utils`, `@standard-schema/spec`, `pathe`, `stackback`, `html-escaper`, and `make-dir` out of the MIT bucket, `siginfo` out of the ISC one, and `istanbul-lib-coverage`, `istanbul-lib-report`, and `istanbul-reports` out of the BSD-3-Clause one — the last three replaced by the two names that arrived, `@vitest/istanbul-lib-coverage` and `@vitest/istanbul-lib-report`, which the census counts as MIT and the test-only row below reads as BSD-3-Clause. It shrank by one more when `jsdom` moved a minor: `symbol-tree` left the MIT bucket, jsdom no longer resolving it, and two name-version pairs went with it without taking a name — `whatwg-url` and the `tr46` behind it each lost the version only jsdom's `data-urls` had held. **No package in the closure is offered under a strong copyleft or source-available licence** — no GPL, LGPL, AGPL, SSPL, BUSL, Commons Clause, or PolyForm term appears anywhere in it — **and the only weak copyleft in it is the MPL-2.0 pair**, which is a build tool. That is the finding, recorded rather than left as silence, because a closure this size returning nothing is exactly the result a reader would otherwise take for a review nobody performed.
- **Unlicense** — `fetch-cookie`, reached only through `@microsoft/signalr`'s Node fallback — is named here rather than in a row, for the reason MIT-0 below is: it is a public-domain dedication that obliges nothing at all, so it asks strictly less than the majority term already does, and nothing this repository publishes carries the package regardless.
- The four terms that are conditional at all are each decided in the row of the pin that brings them: **MPL-2.0** for `lightningcss` and `lightningcss-linux-x64-gnu`, **CC-BY-4.0** for `caniuse-lite`, and **BlueOak-1.0.0** for `minimatch` in the build-tooling row; **CC0-1.0** for `mdn-data` and **BlueOak-1.0.0** for `lru-cache` in the test-only row. **MIT-0** — `@csstools/color-helpers` and `@csstools/css-syntax-patches-for-csstree`, reached through jsdom's colour parsing — is named here rather than in a row, because it is MIT with the attribution condition removed and obliges strictly less than the majority term already does.
- **What a bundle carries is twenty-four packages under two terms**: eight taken under MIT — `react`, `react-dom`, `scheduler`, `@microsoft/signalr`, and `markdown-to-jsx`, which declare MIT, and `@tauri-apps/plugin-opener` and `@tauri-apps/plugin-notification` with the `@tauri-apps/api` behind them, which offer a choice including MIT — and sixteen under Apache-2.0, which is the OpenTelemetry closure the client's own pipeline resolves and has no alternative arm to take. Everything else the workspace installs builds, lints, or tests, and reaches no artifact.
- **`pnpm --dir frontend licenses list --prod` is the closure and no longer the bundle, and the difference is exactly seventeen packages.** It returns forty-one, and the seventeen beyond the twenty-four above are `@microsoft/signalr`'s Node runtime — `abort-controller`, `eventsource`, `fetch-cookie`, `node-fetch`, and `ws`, and what those resolve behind them — `tough-cookie` under `fetch-cookie` and `event-target-shim` under `abort-controller` among them. That package reaches for them inside a branch it takes only outside a browser, through a `require` call resolved at run time, so what the built chunk keeps of the seventeen is the module name as a string argument and nothing else. That was read off the built bundle rather than reasoned about — searching `frontend/src/Client.App/dist/assets/index-*.js` for each name finds the `require` line and no module — and it is why the notice beside the bundle names `@microsoft/signalr` alone and says so. Until this pin the two readings agreed, which is why the claim used to be made mechanically; a pin whose package ships a runtime branch of its own is what separated them.
- **The Rust closure resolves 502 crates** beside this repository's own `mailfathom-desktop`, which is the one entry `cargo metadata` reports with no licence because it is not published. 311 offer `MIT OR Apache-2.0` in one spelling or another, 111 are MIT alone, and 11 more are `Unlicense OR MIT`, all taken under MIT. The complete tail is in the desktop shell's row below, which is where each of them is decided.
- **298 of those 502 are what a Linux desktop build actually links**, over normal dependency edges for `x86_64-unknown-linux-gnu` — the enumeration above prints 299 names, `mailfathom-desktop` itself being one of them and not one of the 502. The rest are another target's — the macOS and Windows families — or a build dependency the binary does not carry. All five MPL-2.0 crates are inside the 298, so the review that row records is a review of something an artifact would really contain rather than of a crate Cargo merely resolved. The opener plugin brought five crates and four of them are inside it: `open`, `is-docker`, and `is-wsl` beside the plugin itself, with `tauri-plugin` outside as a build dependency the binary does not carry. The notification plugin brought ten and seven are inside it: the plugin itself, the `notify-rust` it raises a notification and hears a click on one through, `rand` with `rand_chacha`, `rand_core`, and `ppv-lite86` behind it, and `zerocopy` beside the plugin, with `mac-notification-sys` and `tauri-winrt-notification` outside as the other two platforms' notification services and `zerocopy-derive` outside as an optional feature of `zerocopy` nothing here turns on.
- **The Android closure resolves 81 artifacts under one term.** `androidx.webkit`, `androidx.appcompat`, `androidx.activity:activity-ktx`, `androidx.lifecycle:lifecycle-process` and `com.google.android.material` are the five declarations the committed Gradle project makes, and every artifact on the packaged runtime classpath behind them is Apache-2.0 — the census has no second term and no undeclared entry, which is why it is stated in the row that decides them rather than broken out here. It is fixed the way the other two are, by `frontend/src-tauri/gen/android/app/gradle.lockfile`: Gradle reads a bare version as a preference and would otherwise resolve a different closure whenever either repository published a transitive one, so the committed project turns dependency locking on and every build verifies against that file. Rewriting it is `./gradlew :app:dependencies --write-locks`, and it belongs to the change that moved a declaration.

**A census is a completed reading and not a derived value, which is what decides who maintains it.** `scripts/update-dependencies.sh` surveys the npm and crate families of direct pins and rewrites them on `--apply`, and it never writes a line of this file — so a moved client pin resolves a new closure whose census nothing recomputes. The run says so when it moves one, and re-running the enumerations above is part of the same change rather than a task after it. The Android declarations are outside that survey altogether, being Gradle's rather than a family it reads, so what moves that closure is a regeneration of the committed project — `frontend/AGENTS.md` § *The Android head* is where that obligation is stated.

## Redistributed with MailFathom

A framework-dependent publish of `backend/src/Host` emits 107 assemblies, seven of which are MailFathom's own. The 100 third-party assemblies below are what a binary, container, or source release has to carry notices for. `backend/src/Cli` is published beside it as the `mfctl` administration command and shares that graph apart from three packages of its own, recorded in three rows of their own below. Versions are the ones the host's own graph resolves, which is not always the version pinned in `backend/Directory.Packages.props`: nearest-wins resolution can raise a pin that is only a floor. The last seven rows are the client's, and each is carried differently from every other and from the others: the directory of static files the client stack builds travels inside every published container image, copied into its web root rather than emitted into a publish output, while the desktop application beside it is installed from a `.deb`, an `.rpm`, or an NSIS installer a release attaches. The same static files travel inside that application as well, which is why all but the last two of the seven rows answer for both artifacts. Two of them are not packages at all but files committed into the client's own source tree — a typeface and a set of icon outlines — and they are here rather than under *Externally sourced source material* for the reason that section's rows are not: these are redistributed verbatim inside an artifact, which is what puts a notice obligation on a release rather than on a file in this repository. The final row is the Android head's, and it is the one artifact in this section a release does not carry: [ADR 0027](docs/decisions/0027-an-android-head-built-every-night-and-supported-by-nothing.md) makes the APK one left on the run that built it rather than a published artifact, so nothing about it reaches a release page — it is here rather than under *Build-time and design-time only* because the libraries in it are redistributed to whoever installs that build, and an artifact nobody is promised still travels under the terms its contents carry.

| Component and exact version | Use in this repository | License terms verified | Compatibility decision and obligations | Sources reviewed |
|---|---|---|---|---|
| .NET 10 runtime and ASP.NET Core shared framework. The packages it supplies, which the project locks resolve and the host's own graph then prunes: Microsoft.Extensions.Caching.Abstractions 10.0.13, Microsoft.Extensions.Caching.Memory 10.0.12, Microsoft.Extensions.Configuration 10.0.12, Microsoft.Extensions.Configuration.Abstractions 10.0.12, Microsoft.Extensions.Configuration.Binder 10.0.12, Microsoft.Extensions.Configuration.EnvironmentVariables 10.0.12, Microsoft.Extensions.DependencyInjection 10.0.12, Microsoft.Extensions.DependencyInjection.Abstractions 10.0.13, Microsoft.Extensions.Diagnostics 10.0.12, Microsoft.Extensions.Diagnostics.Abstractions 10.0.12, Microsoft.Extensions.Diagnostics.HealthChecks 10.0.12, Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 10.0.12, Microsoft.Extensions.FileProviders.Abstractions 10.0.12, Microsoft.Extensions.FileSystemGlobbing 10.0.12, Microsoft.Extensions.Hosting.Abstractions 10.0.12, Microsoft.Extensions.Http 10.0.12, Microsoft.Extensions.Logging 10.0.12, Microsoft.Extensions.Logging.Abstractions 10.0.13, Microsoft.Extensions.Logging.Configuration 10.0.12, Microsoft.Extensions.ObjectPool 10.0.12, Microsoft.Extensions.Options 10.0.12, Microsoft.Extensions.Options.ConfigurationExtensions 10.0.12, Microsoft.Extensions.Options.DataAnnotations 10.0.12, Microsoft.Extensions.Primitives 10.0.13, and System.Threading.RateLimiting 10.0.12 | The published host is framework-dependent, so the runtime is a deployment prerequisite rather than a copied file. A self-contained build or a container image built on the runtime image carries it instead. Seven of the packages listed here are pinned centrally and are still supplied by the framework, so a publish emits an assembly for none of them. Where two projects resolve one of them at two versions, the version named is the higher: `backend/src/Mcp` resolves seven of them at 10.0.10, `backend/src/Infrastructure` resolves four of them at 10.0.12 below the 10.0.13 `Microsoft.Extensions.AI` raises them to in `backend/src/AI`, and `backend/src/AI` resolves `System.Threading.RateLimiting` at 8.0.0 behind `Polly.RateLimiting`. None of those reaches an artifact either. | MIT for the .NET platform repositories and their NuGet package metadata | Allowed. Permissive license supports commercial closed-source use and open-source distribution with notices. A container or self-contained artifact must carry the runtime's own notices. | <https://github.com/dotnet/dotnet>, <https://github.com/dotnet/runtime>, <https://github.com/dotnet/aspnetcore> |
| Microsoft.EntityFrameworkCore 10.0.12, Microsoft.EntityFrameworkCore.Abstractions 10.0.12, Microsoft.EntityFrameworkCore.Relational 10.0.12, Microsoft.AspNetCore.Authentication.JwtBearer 10.0.12, Microsoft.Bcl.Cryptography 10.0.2, Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 10.0.12, System.Numerics.Tensors 10.0.13 | Persistence, the JWT bearer authentication middleware, the database health check, and the tensor primitives the AI abstractions use | MIT, declared as a license expression in each package's NuGet metadata | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/dotnet/dotnet>, <https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/10.0.12>, <https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBearer/10.0.12> |
| Microsoft.Extensions.AI 10.11.0, Microsoft.Extensions.AI.Abstractions 10.11.0, Microsoft.Extensions.AI.Evaluation 10.10.0, Microsoft.Extensions.AmbientMetadata.Application 10.10.0, Microsoft.Extensions.Compliance.Abstractions 10.10.0, Microsoft.Extensions.DependencyInjection.AutoActivation 10.10.0, Microsoft.Extensions.Diagnostics.ExceptionSummarization 10.10.0, Microsoft.Extensions.Http.Diagnostics 10.10.0, Microsoft.Extensions.Http.Resilience 10.10.0, Microsoft.Extensions.Resilience 10.10.0, Microsoft.Extensions.ServiceDiscovery 10.10.0, Microsoft.Extensions.ServiceDiscovery.Abstractions 10.10.0, Microsoft.Extensions.Telemetry 10.10.0, Microsoft.Extensions.Telemetry.Abstractions 10.10.0 | Provider-neutral chat and embedding abstractions, and the service-defaults stack for HTTP resilience, service discovery, and telemetry enrichment. Two packages resolve at two versions, because a central pin is a floor and nearest-wins resolution raises it per project. `Microsoft.Extensions.AI.Abstractions` is 10.11.0 in the host's graph and in `backend/src/AI`, where the provider adapter and the agent framework require it, and 10.8.3 in `backend/src/Mcp`, where `ModelContextProtocol.Core` 2.2.0 asks for no more. `Microsoft.Extensions.AI.Evaluation` is not referenced by any production project: the Agent Framework brings it in at its own floor, 10.10.0, which is the version the host carries, while the evaluation suite's central pin raises it to 10.11.0 there alone. The host's version is the redistributed one; the others are restored during a build of those projects and reach no artifact. | MIT, declared as a license expression in each package's NuGet metadata | Allowed. Permissive license supports both distribution models with notice preservation. Provider-specific AI packages, model terms, and service terms remain separately reviewable and none is adopted. | <https://github.com/dotnet/extensions>, <https://www.nuget.org/packages/Microsoft.Extensions.AI/10.11.0>, <https://www.nuget.org/packages/Microsoft.Extensions.AI.Abstractions/10.11.0> |
| Microsoft.Extensions.AI.OpenAI 10.11.0, OpenAI 2.14.0, and transitive System.ClientModel 1.15.0 | The embedding and chat provider adapters in `backend/src/AI/ProviderAdapters`, which together are the whole of MailFathom's contact with a provider. Left at its defaults the OpenAI library describes the process on every request — six `X-Stainless-*` headers and its share of the `User-Agent`, naming its own version, the .NET runtime and its version, the operating system, and the processor architecture. The adapters switch that off through the library's own `OpenAI.DisableTelemetry` switch before the first client is built, so a request to a provider carries none of it; *Hosted services* below states what a request does carry. The `SyntheticMail` development tool's opt-in AI content mode is the one other place in the repository that reaches a provider: it runs on a developer's machine against an endpoint that developer names in a local file, sends only content the tool invented locally under the library's default headers, which describe that developer's machine, and ships in no artifact. One client construction serves both supported providers and both roles, because an Azure OpenAI resource's v1 data plane is OpenAI-compatible, so the SDK is pointed at the resource's own address rather than replaced by a second library. A chat endpoint additionally states which of the SDK's two request clients its calls go through — chat completions or responses — which is a second path within this same library at the same address under the same credential, and is what a reasoning model requiring the responses API is reached by. `Azure.AI.OpenAI` is deliberately not adopted: it would be a second *library*, and it has published no stable release since 2.1.0. | MIT, declared as a license expression in each package's NuGet metadata | Allowed. Permissive license supports both distribution models with notice preservation. The library is what talks to a provider; the provider's own service terms are separate and are recorded under *Hosted services* below. | <https://github.com/openai/openai-dotnet>, <https://github.com/dotnet/extensions>, <https://github.com/Azure/azure-sdk-for-net>, <https://www.nuget.org/packages/OpenAI/2.14.0>, <https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI/10.11.0>, <https://www.nuget.org/packages/System.ClientModel/1.15.0>, <https://github.com/openai/openai-dotnet/blob/main/docs/Observability.md#telemetry-and-privacy> |
| Azure.Identity 1.21.0 and transitive Azure.Core 1.53.0, Microsoft.Identity.Client 4.83.1, Microsoft.Identity.Client.Extensions.Msal 4.83.1, System.Memory.Data 10.0.9, Microsoft.Bcl.AsyncInterfaces 10.0.3, and System.Security.Cryptography.ProtectedData 4.5.0 | The four non-interactive Microsoft Entra credentials an Azure OpenAI endpoint may be reached with where no key exists to provision: managed identity, workload identity, client secret, and client certificate. MailFathom composes that set explicitly and never uses `DefaultAzureCredential`, whose own chain reaches an interactive browser credential and the developer-tool credentials of whoever is signed in on the host. The MSAL token cache brings the DPAPI package into this graph as well as into `mfctl`'s, at the version MSAL pins rather than the one pinned centrally; the call it guards is Windows-only and this service runs on Linux. | MIT, declared as a license expression in each package's NuGet metadata, except `System.Security.Cryptography.ProtectedData` 4.5.0, whose metadata carries a license URL to the MIT text of the repository that built it | Allowed. Permissive license supports both distribution models with notice preservation. The directory these libraries authenticate against is a deployment choice with its own service terms and is not selected here. | <https://github.com/Azure/azure-sdk-for-net>, <https://github.com/AzureAD/microsoft-authentication-library-for-dotnet>, <https://www.nuget.org/packages/Azure.Identity/1.21.0>, <https://www.nuget.org/packages/Microsoft.Identity.Client/4.83.1> |
| Microsoft.Agents.AI 1.24.0 and transitive Microsoft.Agents.AI.Abstractions 1.24.0 | Agent and RAG orchestration, including `ChatClientAgent` and `TextSearchProvider` integration. The abstractions package carries a feature-usage token the framework can append to a request's `User-Agent`, and it is written only by the framework's own Foundry and Azure OpenAI client wrappers, which are separate packages and not adopted; no request MailFathom sends carries it. | MIT, declared as a license expression in both packages' NuGet metadata | Allowed. Package terms and any model or service terms reached through an agent remain separately reviewable on every update. | <https://github.com/microsoft/agent-framework>, <https://www.nuget.org/packages/Microsoft.Agents.AI/1.24.0> |
| Microsoft.Extensions.VectorData.Abstractions 10.10.0 | Vector-store abstractions the agent framework brings in transitively. MailFathom compiles against neither this package nor a vector-store implementation of its own. | MIT, declared as a license expression in the NuGet package metadata; published from the `dotnet/extensions` repository | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/dotnet/extensions>, <https://www.nuget.org/packages/Microsoft.Extensions.VectorData.Abstractions/10.10.0> |
| Microsoft.ML.Tokenizers 2.0.0 | Tokenizer primitives the AI abstractions bring in transitively | MIT, declared as a license expression in the NuGet package metadata | Allowed. Permissive license supports both distribution models with notice preservation. Model and vocabulary files are separate artifacts and none is bundled. | <https://github.com/dotnet/machinelearning>, <https://www.nuget.org/packages/Microsoft.ML.Tokenizers/2.0.0> |
| Microsoft.IdentityModel.Abstractions 8.19.2, Microsoft.IdentityModel.JsonWebTokens 8.19.2, Microsoft.IdentityModel.Logging 8.19.2, Microsoft.IdentityModel.Protocols 8.19.2, Microsoft.IdentityModel.Protocols.OpenIdConnect 8.19.2, Microsoft.IdentityModel.Tokens 8.19.2, System.IdentityModel.Tokens.Jwt 8.19.2 | Token validation and OpenID Connect metadata handling, brought in transitively by the JWT bearer middleware | MIT, declared as a license expression in each package's NuGet metadata | Allowed. Permissive license supports both distribution models with notice preservation. The identity provider these libraries talk to is a deployment choice with its own service terms and is not selected here. | <https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet>, <https://www.nuget.org/packages/Microsoft.IdentityModel.Tokens/8.19.2> |
| Microsoft.AspNetCore.OpenApi 10.0.12 and Microsoft.OpenApi 2.12.2 | OpenAPI document generation for the HTTP API, referenced by `backend/src/Host` alone. The generator builds the document from the routes the host already mapped; `Microsoft.OpenApi` supplies the object model the host's own document and operation transformers name, which is why it is pinned directly rather than left to the generator's dependency range. That range is what holds the object model on its 2.x line: the generator declares `[2.12.0, 3.0.0)`, so 2.12.2 is the newest version that restores beside it and a 3.x pin is refused with `NU1608`. Every version in the range is past CVE-2026-49451 (GHSA-v5pm-xwqc-g5wc, high, a stack overflow parsing circular schema references), which `Microsoft.OpenApi` 2.0.0 carried. Both assemblies are redistributed even though the routes they serve are registered only in a `Development` process. | MIT, declared as a license expression in both packages' NuGet metadata | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/dotnet/aspnetcore>, <https://github.com/microsoft/OpenAPI.NET>, <https://www.nuget.org/packages/Microsoft.AspNetCore.OpenApi/10.0.12>, <https://www.nuget.org/packages/Microsoft.OpenApi/2.12.2>, <https://github.com/advisories/GHSA-v5pm-xwqc-g5wc> |
| Scalar.AspNetCore 2.18.0 | The API explorer a developer reads the generated document in, referenced by `backend/src/Host` alone and mapped only in a `Development` process. It declares no package dependency of its own. What it redistributes beyond the managed assembly is its own script bundle and icon, embedded as assembly resources and served by the instance rather than fetched from a content delivery network — so a notice bundle carries Scalar's licence once for the assembly and the assets together. | MIT, declared as a license expression in the NuGet package metadata and confirmed through the GitHub license API for `scalar/scalar` | Allowed. Permissive license supports both distribution models with notice preservation. The hosted service at scalar.com is a separate offering with its own terms and is not adopted: nothing in this repository calls it, and the explorer runs entirely from assets the instance serves. | <https://github.com/scalar/scalar>, <https://github.com/scalar/scalar/blob/main/LICENSE>, <https://www.nuget.org/packages/Scalar.AspNetCore/2.18.0> |
| ModelContextProtocol.AspNetCore 2.2.0 and transitive ModelContextProtocol 2.2.0 and ModelContextProtocol.Core 2.2.0 | MCP server SDK and Streamable HTTP transport: the tool descriptors and their generated input and output schemas, the call-tool filter the error and outcome mapping is installed as, the endpoint the host maps, and the MCP authentication scheme that publishes the RFC 9728 protected resource metadata document and the challenge pointing at it | Apache-2.0, declared as a license expression in all three packages' NuGet metadata. The upstream `LICENSE` at tag `v2.2.0` records that the project is transitioning from MIT to Apache-2.0: new contributions are Apache-2.0, and contributions whose authors have not consented to relicensing remain MIT. Both are permissive and both are on the allow-list, so the transition needs no decision of its own. | Allowed. Apache-2.0 requires the license and any attribution notices to be preserved on redistribution. No package bundles a `NOTICE` file; the upstream repository carries `THIRD-PARTY-NOTICES.txt`, which must be reproduced if MailFathom ever redistributes the SDK's own sources. Service, model, and client terms of anything reached over the protocol stay separately reviewable. | <https://csharp.sdk.modelcontextprotocol.io/>, <https://github.com/modelcontextprotocol/csharp-sdk/blob/v2.2.0/LICENSE>, <https://github.com/modelcontextprotocol/csharp-sdk/blob/v2.2.0/THIRD-PARTY-NOTICES.txt>, <https://www.nuget.org/packages/ModelContextProtocol.AspNetCore/2.2.0> |
| Microsoft.AspNetCore.SignalR.StackExchangeRedis 10.0.12, MessagePack 2.5.303, and transitive StackExchange.Redis 2.7.27, Pipelines.Sockets.Unofficial 2.2.8, MessagePack.Annotations 2.5.303, and Microsoft.NET.StringTools 17.6.3 | The optional signal backplane. Where a `SignalBackplane` section names a RESP endpoint, a signal raised on one replica reaches the client connections another replica holds, which is what makes the client's channel correct above one replica; a deployment declaring no section registers none of this and behaves exactly as one built before it existed. Where a section is written every replica connects, whichever surfaces it serves, because the same endpoint carries the empty announcement that a configuration change committed; the hub's fan-out over it is registered only where the client surface is served. The six assemblies are emitted by every publish regardless, because a publish cannot know which sections an operator will write. `StackExchange.Redis` is the RESP client and the other four are behind it: its socket pipeline, the MessagePack serializer SignalR's Redis protocol writes each invocation with, that serializer's annotations, and the string interner MessagePack brings along. MessagePack is pinned directly rather than taken at the backplane's 2.5.302 floor, because releases below 2.5.303 carry a buffer-limit advisory against nested arrays. | MIT, declared as a license expression in each package's NuGet metadata | Allowed. Permissive license supports both distribution models with notice preservation. The RESP server an operator points the section at is a deployment choice with its own terms and is not selected here; the Valkey image the AppHost can start for a developer and the Helm chart can render for a deployment is recorded under *Local orchestration and container images* below. | <https://github.com/dotnet/aspnetcore>, <https://github.com/StackExchange/StackExchange.Redis>, <https://github.com/mgravell/Pipelines.Sockets.Unofficial>, <https://github.com/MessagePack-CSharp/MessagePack-CSharp>, <https://github.com/dotnet/msbuild>, <https://www.nuget.org/packages/Microsoft.AspNetCore.SignalR.StackExchangeRedis/10.0.12>, <https://www.nuget.org/packages/StackExchange.Redis/2.7.27>, <https://www.nuget.org/packages/MessagePack/2.5.303> |
| MailKit 4.18.1 | IMAP, SMTP, SASL, IDLE, NOTIFY, CONDSTORE, QRESYNC, and TLS modes | MIT, declared as a license expression in the NuGet package metadata | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/jstedfast/MailKit/blob/master/LICENSE>, <https://www.nuget.org/packages/MailKit/4.18.1> |
| MimeKit 4.18.1 | MIME parsing for message metadata and body-text extraction: the streaming structural reader, the parsed message tree, TNEF part recognition, decoded-content measurement, and the HTML tokenizer that derives readable text from an HTML body and reads the embedded-resource references an attachment summary depends on, the RFC 8601 `Authentication-Results` parser both the sender-authentication verdict and spam classification read a message's authentication outcomes with, and the signed-message containers signature verification opens: `multipart/signed` and the opaque S/MIME part, whose signed content MimeKit canonicalizes through a cryptography context of its own that MailFathom subclasses only to capture those bytes. Pinned directly rather than taken transitively, because MailFathom compiles against MimeKit's own API and this is also the version MailKit 4.18.1 requires. | MIT, declared as a license expression in the NuGet package metadata | Allowed. Permissive license supports both distribution models with notice preservation. | <https://www.mimekit.net/>, <https://github.com/jstedfast/MimeKit/blob/master/LICENSE>, <https://www.nuget.org/packages/MimeKit/4.18.1> |
| BouncyCastle.Cryptography 2.7.0 | The cryptography provider MailKit and MimeKit bring in transitively for S/MIME, PGP, and SASL primitives, and one MailFathom compiles against directly in four places, all inside `backend/src/Infrastructure`. The DKIM key locator names `AsymmetricKeyParameter`, which is what MimeKit's public-key locator contract returns; that key is parsed by MimeKit's own base class. An account's OpenPGP signing key is parsed with `ArmoredInputStream` and `PgpSecretKeyRingBundle` and opened with its passphrase to judge it when the configuration is read, and nothing it parses is kept. Verifying a message's own signature calls the library itself: its CMS parser and signer-information verification for an S/MIME signature, its OCSP request builder and response parser and its CRL parser for revocation, and its OpenPGP packet reader and signature verification for a PGP/MIME signature and the trusted keys configured for one. An organization's signing key is generated by its RFC 8032 `Ed25519` implementation from its `SecureRandom`, into a buffer MailFathom owns, and its two halves leave the library as raw bytes, the private one sealed under the deployment's key ring at once and then erased. Chain building stays with the .NET platform's own `X509Chain`, nothing is signed or encrypted with this library directly, and no type of it leaves the adapter that uses it. Pinned directly for that reason rather than taken transitively, and at the version MailKit and MimeKit 4.18.1 already resolve to. | MIT, declared as a license expression in the NuGet package metadata; the upstream project publishes the same MIT-style terms | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/bcgit/bc-csharp>, <https://www.nuget.org/packages/BouncyCastle.Cryptography/2.7.0> |
| System.Security.Cryptography.Pkcs 10.0.13 | The CMS and PKCS primitives MimeKit brings in transitively at 10.0.0. No type of it is named by MailFathom. Pinned directly at the release that fixes two .NET denial-of-service advisories against 10.0.0 through 10.0.12, and because that version is newer than the one the ASP.NET Core shared framework carries, a framework-dependent publish of the host copies this assembly beside it and the host loads it in place of the framework's own. | MIT, declared as a license expression in the NuGet package metadata | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/dotnet/runtime>, <https://www.nuget.org/packages/System.Security.Cryptography.Pkcs/10.0.13> |
| DnsClient 1.8.0 | The DNS resolver behind local DKIM verification and the DMARC record read beside it, referenced by `backend/src/Infrastructure` alone: one TXT query for `<selector>._domainkey.<signing-domain>` per signature whose key is not already cached, and the TXT queries of RFC 9989's tree walk under `_dmarc.` for a displayed domain whose record is not already cached. It is adopted because the base class library publishes no TXT lookup at all — `Dns.GetHostEntry` resolves addresses and nothing else — and the alternative is parsing DNS wire format from an unauthenticated network response by hand. The library's types stay inside the two resolver adapters, each the implementation of an application-owned port — the key port trafficking in strings, the DMARC port in domain types — so no DNS type crosses into `Application` or `Domain`. Under the `net8.0` assets .NET 10 resolves it through, it declares no package dependencies of its own, so nothing joins the graph behind it. | Apache-2.0, declared as a license expression in the NuGet package metadata and confirmed through the GitHub license API for `MichaCo/DnsClient.NET` | Allowed. Apache-2.0 requires the license and any attribution notices to be preserved on redistribution; the package bundles no `NOTICE` file. | <https://dnsclient.michaco.net/>, <https://github.com/MichaCo/DnsClient.NET/blob/dev/LICENSE>, <https://www.nuget.org/packages/DnsClient/1.8.0> |
| HtmlSanitizer 9.2.1039 | The HTML sanitizer that reduces a message body to markup safe to hand to a reader, referenced by `backend/src/Infrastructure` alone. It enforces an allow-list of elements, attributes, CSS properties, CSS at-rules, and URI schemes, and MailFathom configures two policies over it: the model-facing one, which admits no URI scheme and no style at all, and the one behind the self-contained representation a full-HTML surface reads, which keeps the layout while removing everything that runs or reaches another host. Its security history is tracked deliberately — CVE-2026-25543 (GHSA-j92c-7v7g-gj3f, moderate, 2026-02-03), a `<template>` bypass fixed in 9.0.892 and never reachable under the default configuration, is why `template` stays off the allow-list and why the pin is watched rather than forgotten. The 9.2 line is the floor for the same reason: it filters a `<style>` element whatever its `type` attribute says, an empty one included, and it hands the addresses of a font face, an `@import`, and a candidate list to the URL filter the second policy decides every reference with. | MIT, declared as a license expression in the NuGet package metadata and confirmed through the GitHub license API for `mganss/HtmlSanitizer` | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/mganss/HtmlSanitizer>, <https://www.nuget.org/packages/HtmlSanitizer/9.2.1039>, <https://github.com/mganss/HtmlSanitizer/security/advisories/GHSA-j92c-7v7g-gj3f> |
| AngleSharp 1.8.4 and AngleSharp.Css 1.1.2 | The HTML parser a message body is read with, referenced by `backend/src/Infrastructure` alone. It arrived transitively behind HtmlSanitizer and is now pinned directly as well, because the document reduction that turns a body into the closed block tree a client draws parses the markup itself and walks the resulting node tree: MailFathom compiles against `HtmlParser`, `IElement`, and the node interfaces, and against nothing else the library offers. It runs no script, resolves no external reference, and fetches nothing — AngleSharp only does any of those when a requester is configured, and none is — so what it contributes is parsing a hostile string into nodes rather than any behaviour of its own. Every AngleSharp type stays inside `Mail/Mime/Rendering/`, so no node, document, or parser reaches `Application` or `Domain`. HtmlSanitizer 9.2.1039 declares AngleSharp 1.7.2 as its floor and the pin sits above it, so the version in the closure is the one this repository names rather than the one the sanitizer would settle for: the parser's own advisories are answered by the parser's releases, and GHSA-cgp3-27rh-pcp2 (a mutation through the serialization of an SVG `style` element, fixed in 1.8.2) is one that floor does not carry the fix for. Neither policy admits `svg` or `math`, and a disallowed element is removed with its content before anything is serialized, which is what kept that advisory and GHSA-pgww-w46g-26qg (an `annotation-xml` integration point, fixed in 1.5.0) out of reach here; the pin is still held at the release carrying both fixes. `AngleSharp.Css` is pinned directly as well, and for the same reason: the self-contained representation a full-HTML surface reads states which CSS at-rules a message may keep, and `CssRuleType` is that library's type rather than the sanitizer's, so a version resolved for the sanitizer would decide that set somewhere other than in this repository. The reduction to the closed tree still reads a declaration block with a closed reader of its own and reaches nothing else the library offers. HtmlSanitizer 9.2.1039 declares 1.0.2 as its floor, and the pin sits above that one as well. | MIT, declared as a license expression in both packages' NuGet metadata | Allowed. Permissive license supports both distribution models with notice preservation. | <https://anglesharp.github.io/>, <https://www.nuget.org/packages/AngleSharp/1.8.4>, <https://www.nuget.org/packages/AngleSharp.Css/1.1.2>, <https://github.com/AngleSharp/AngleSharp/security/advisories/GHSA-cgp3-27rh-pcp2>, <https://github.com/AngleSharp/AngleSharp/security/advisories/GHSA-pgww-w46g-26qg> |
| PdfPig 0.1.16 | The PDF reader an attachment's text is extracted with, referenced by `backend/src/Infrastructure` alone. MailFathom compiles against `PdfDocument`, `ParsingOptions`, `ContentOrderTextExtractor`, and `PdfDocumentEncryptedException`, and against nothing else the library offers: the adapter opens a document from a buffered stream, reads each page's characters, and closes it. It executes nothing a PDF may declare — no JavaScript, no open action, no embedded file, no form submission — because none of those is ever asked for, and it reaches no network and writes no file. Every PdfPig type stays inside `Documents/`, so the application port sees characters and a closed set of reasons rather than a document model. It was chosen over the alternatives on its dependency graph as much as on its reading: under the `net8.0` assets .NET 10 resolves it through it declares no package dependencies at all, so adopting it adds nothing behind it, and the three Office Open XML formats need no package of their own because the base class library reads a zip archive of XML. The package itself is split across seven assemblies — `UglyToad.PdfPig`, and `.Core`, `.DocumentLayoutAnalysis`, `.Fonts`, `.Package`, `.Tokenization`, and `.Tokens` beside it — and a publish emits all seven, which is what the assembly count above is measured against. The test suite also uses its `PdfDocumentBuilder` to compose the documents it reads, so no binary fixture is committed. | Apache-2.0, declared as a license expression in the NuGet package metadata and confirmed against the `UglyToad/PdfPig` repository | Allowed. Apache-2.0 requires the license and any attribution notices to be preserved on redistribution; the package bundles no `NOTICE` file. | <https://github.com/UglyToad/PdfPig>, <https://github.com/UglyToad/PdfPig/blob/master/LICENSE>, <https://www.nuget.org/packages/PdfPig/0.1.16> |
| Ical.Net 5.2.3 and transitive NodaTime 3.2.2 | The iCalendar (RFC 5545) reader an imported `.ics` file is parsed by, referenced by `backend/src/Infrastructure` alone. The base class library carries no iCalendar reader, and the format is the kind nobody hand-rolls safely: lines are folded at 75 octets, values escape their own separators, every property carries parameters, and a date names a zone, a UTC instant, or neither — so an import written by hand is how a date lands silently wrong. MailFathom compiles against `CalendarCollection`, the parsed `CalendarEvent`, and `CalDateTime`, and against nothing else it offers: the adapter under `Calendar/` reads six properties of an entry and answers in MailFathom's own types, so no Ical.Net type reaches an application port, a use case, or a domain type. It parses and nothing more — it opens no socket, resolves no URI an entry declares, and writes no file. NodaTime is its zone database and arrives behind it; MailFathom names no type of it, so it is taken transitively rather than pinned, and it declares no dependency of its own on the `net8.0` assets .NET 10 resolves. | Ical.Net is MIT and NodaTime is Apache-2.0, each declared as a license expression in its own NuGet package metadata and confirmed against its upstream repository | Allowed. MIT and Apache-2.0 both support either distribution model with notice preservation; Apache-2.0 additionally requires any attribution notices to be preserved on redistribution, and NodaTime bundles no `NOTICE` file. | <https://github.com/ical-org/ical.net>, <https://github.com/ical-org/ical.net/blob/main/LICENSE>, <https://www.nuget.org/packages/Ical.Net/5.2.3>, <https://nodatime.org/>, <https://github.com/nodatime/nodatime/blob/main/LICENSE.txt>, <https://www.nuget.org/packages/NodaTime/3.2.2> |
| NCalc 7.2.0 and transitive NCalc.Core 7.2.0, NCalc.Parser 7.2.0, and NCalc.Domain 7.2.0 | The expression language a mail rule's condition is authored in, referenced by `backend/src/Infrastructure` alone, so the parser, its syntax tree, and every NCalc type stay inside the adapter that compiles and evaluates a condition. MailFathom uses the parser, the tree, and the asynchronous evaluator; it registers three text functions of its own and refuses at configuration-read time every identifier, function, and operator outside the closed surface it publishes, because the library offers no switch that removes one. [ADR 0010](https://github.com/fathomise/MailFathom/blob/main/docs/decisions/0010-rule-authoring-in-configuration-and-ncalc-conditions.md) records why an expression language is admissible in a file read at startup at all. | MIT, declared as a license expression in each package's NuGet metadata | Allowed. Permissive license supports both distribution models with notice preservation. | <https://ncalc.github.io/ncalc/>, <https://github.com/ncalc/ncalc/blob/master/LICENSE>, <https://www.nuget.org/packages/NCalc/7.2.0> |
| Parlot 1.5.9 | The parser-combinator runtime NCalc brings in transitively and parses a condition with. MailFathom compiles against none of it and defines no grammar of its own. | BSD-3-Clause, declared as a license expression in the NuGet package metadata | Allowed. Together with Google.Protobuf and Polly this is one of three BSD-3-Clause components in the redistributed set: a distributed artifact must reproduce the copyright notice, the three conditions, and the disclaimer, and the third clause forbids using the copyright holder's or contributors' names to endorse derived products without permission. | <https://github.com/sebastienros/parlot>, <https://www.nuget.org/packages/Parlot/1.5.9> |
| ExtendedNumerics.BigDecimal 3003.2.0.161 | The arbitrary-precision decimal type NCalc brings in transitively for arithmetic that leaves the range of the built-in numeric types. MailFathom compiles against none of it, and a mail rule's numeric facts are ordinary sizes and counts. | MIT, declared as a license expression in the NuGet package metadata | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/AdamWhiteHat/BigDecimal>, <https://www.nuget.org/packages/ExtendedNumerics.BigDecimal/3003.2.0.161> |
| Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 and transitive Npgsql 10.0.3, Npgsql.DependencyInjection 10.0.3, and Npgsql.OpenTelemetry 10.0.3 | PostgreSQL ADO.NET provider, its EF Core provider, the data-source registration the infrastructure composes asynchronously at startup, and the provider's OpenTelemetry instrumentation | PostgreSQL License, declared as a license expression in each package's NuGet metadata | Allowed. Permissive license supports both distribution models; preserve the copyright notice and permission paragraphs when redistributing. | <https://github.com/npgsql/efcore.pg>, <https://github.com/npgsql/npgsql>, <https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL/10.0.3>, <https://www.nuget.org/packages/Npgsql/10.0.3> |
| Pgvector.EntityFrameworkCore 0.3.0 and transitive Pgvector 0.3.2 | EF Core vector mapping and the vector type it maps | MIT, declared as a license expression in both packages' NuGet metadata and confirmed through the GitHub license API for `pgvector/pgvector-dotnet` | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/pgvector/pgvector-dotnet>, <https://www.nuget.org/packages/Pgvector.EntityFrameworkCore/0.3.0>, <https://www.nuget.org/packages/Pgvector/0.3.2> |
| Aspire.Npgsql.EntityFrameworkCore.PostgreSQL 13.6.1 | The enrichment half of the Aspire PostgreSQL EF Core client integration, referenced by `backend/src/Infrastructure`. It adds the database health check, OpenTelemetry tracing and metrics, and explicit connection-resilience settings to a `DbContext` this repository registers itself, and needs no orchestrator at run time, so a native deployment reports the same telemetry. | MIT, declared as a license expression in the NuGet package metadata and in the upstream repository | Allowed. Permissive license supports both distribution models with notice preservation. | <https://www.nuget.org/packages/Aspire.Npgsql.EntityFrameworkCore.PostgreSQL/13.6.1>, <https://github.com/microsoft/aspire> |
| AWSSDK.S3 4.0.104.3 and AWSSDK.Core 4.0.102.10 | The client the object-storage content backend reaches its S3-compatible endpoint over, referenced by `backend/src/Infrastructure` alone, so every Amazon type stays inside the adapter under `ObjectStorage/` and no application port, use case, or domain type sees one. It is adopted because the S3 REST API is what every self-hosted and hosted implementation publishes, and the alternative is hand-signing SigV4 against an unauthenticated network response. The core runtime is pinned directly rather than taken transitively, for the reason MimeKit is: it carries the credential, the endpoint configuration, and the signer MailFathom compiles against, so the version is decided in `backend/Directory.Packages.props` rather than inherited from a dependency range. The S3 package's only declared dependency is that runtime, and under the `net8.0` assets .NET 10 resolves them through the runtime declares none of its own, so the two are the whole of what joins the graph. Every ambient resolution the SDK offers is switched off, which [ADR 0017](https://github.com/fathomise/MailFathom/blob/main/docs/decisions/0017-object-storage-content-backend-consistency-and-object-identity.md) § 8 records. | Apache-2.0, declared as a license expression in both packages' NuGet metadata and confirmed through the GitHub license API for `aws/aws-sdk-net` | Allowed. Apache-2.0 requires the license and any attribution notices to be preserved on redistribution; neither package bundles a `NOTICE` file, and the upstream repository's `Notice.txt` is what a distributed artifact reproduces. The endpoint these libraries talk to is a deployment choice with its own service terms and none is selected here. | <https://github.com/aws/aws-sdk-net>, <https://github.com/aws/aws-sdk-net/blob/main/License.txt>, <https://github.com/aws/aws-sdk-net/blob/main/Notice.txt>, <https://www.nuget.org/packages/AWSSDK.S3/4.0.104.3>, <https://www.nuget.org/packages/AWSSDK.Core/4.0.102.10> |
| OpenTelemetry 1.19.1, OpenTelemetry.Api 1.19.1, OpenTelemetry.Api.ProviderBuilderExtensions 1.19.1, OpenTelemetry.Exporter.OpenTelemetryProtocol 1.19.1, OpenTelemetry.Extensions.Hosting 1.19.1 | The tracing and metrics SDK, OTLP export, and the host bootstrap logging pipeline that exports process start, startup failure, and shutdown records before the dependency-injection container exists | Apache-2.0, declared as a license expression in each package's NuGet metadata | Allowed. Apache-2.0 requires preserving the license and any `NOTICE` content on redistribution, which applies to the exporter in particular. | <https://github.com/open-telemetry/opentelemetry-dotnet>, <https://www.nuget.org/packages/OpenTelemetry.Exporter.OpenTelemetryProtocol/1.19.1> |
| OpenTelemetry.Instrumentation.AspNetCore 1.19.0, OpenTelemetry.Instrumentation.Http 1.19.0, OpenTelemetry.Instrumentation.Runtime 1.19.0 | ASP.NET Core, HTTP client, and runtime instrumentation in the shared service defaults | Apache-2.0, declared as a license expression in each package's NuGet metadata | Allowed. Preserve the license and any `NOTICE` content on redistribution. | <https://github.com/open-telemetry/opentelemetry-dotnet-contrib>, <https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNetCore/1.19.0> |
| Google.Protobuf 3.30.2 | The protocol-buffer runtime the OTLP exporter brings in transitively to encode telemetry. MailFathom compiles against none of it and defines no `.proto` file. | BSD-3-Clause, declared as a license expression in the NuGet package metadata; the upstream `LICENSE` is the three-clause BSD text under `Copyright 2008 Google Inc.` | Allowed. Beside Polly and Parlot this is one of three BSD-3-Clause components in the redistributed set: a distributed artifact must reproduce Google's copyright notice, the three conditions, and the disclaimer. The third clause also forbids using the copyright holder's or contributors' names to endorse derived products without permission. | <https://github.com/protocolbuffers/protobuf/blob/main/LICENSE>, <https://www.nuget.org/packages/Google.Protobuf/3.30.2> |
| Polly.Core 8.8.0, Polly.Extensions 8.8.0, Polly.RateLimiting 8.8.0 | Named resilience pipelines for the non-HTTP outbound dependency classes: IMAP session establishment and data retrieval, SMTP delivery, database command execution, and AI provider invocation | BSD-3-Clause, declared as a license expression in each package's NuGet metadata and confirmed through the GitHub license API for `App-vNext/Polly` | Allowed. Preserve the copyright notice, the three conditions, and the disclaimer when redistributing. | <https://www.nuget.org/packages/Polly.Core/8.8.0>, <https://github.com/App-vNext/Polly/blob/main/LICENSE> |
| Microsoft.Security.Utilities.Core 1.21.0 | The secret-detection engine behind the `SensitiveContent:Secrets` scanner, referenced by `backend/src/Infrastructure` alone: the pattern type a rule is declared as and reads its own match back through to say which product issued a key, the option defaults its expressions are built with, and a corpus of roughly ninety high-confidence Microsoft credential formats that MailFathom adopts as `ProviderToken` rules. It declares no package dependencies of its own, so the whole of it stays inside the scanning adapter. MailFathom runs that corpus itself rather than through the package's masker, so that a scan can be ended between two expressions; the masking and key-generation halves are unused as well, because redaction is the application's own and produces one placeholder scheme for every consumer. | MIT, declared as a license expression in the NuGet package metadata and confirmed through the GitHub license API for `microsoft/security-utilities` | Allowed. Permissive license supports both distribution models with notice preservation. The regular expressions it ships are part of the package rather than material copied into this repository, so they travel under this row; the third-party rule data MailFathom carries in source is a separate entry under *Externally sourced source material*. | <https://github.com/microsoft/security-utilities>, <https://www.nuget.org/packages/Microsoft.Security.Utilities.Core/1.21.0> |
| System.CommandLine 2.0.12 | Argument parsing, subcommands, and help for the `mfctl` administration command, which ships as a self-contained binary beside each release. `backend/src/Cli` references it, two other packages, and one project — `Common`, which depends on nothing but the base class library — so nothing else in the redistributed set reaches the published command. | MIT, declared as a license expression in the NuGet package metadata and confirmed through the GitHub license API for `dotnet/command-line-api` | Allowed. Permissive license supports both distribution models with notice preservation. | <https://github.com/dotnet/command-line-api/blob/main/LICENSE.TXT>, <https://www.nuget.org/packages/System.CommandLine/2.0.12> |
| Spectre.Console 0.57.2, Spectre.Console.Ansi 0.57.2 | How `mfctl` draws what it writes: the tables its listings are laid out in, the two-column records a single reading is shown as, and the colour that tells a failure and a caution from an ordinary line. `backend/src/Cli` references the first and the second comes with it, both from the one upstream project. Only `backend/src/Cli/Output/CliRenderer.cs` uses either, so no type from them reaches a command file, and no other project in the solution references them. Nothing of the interactive half is used — no prompt, no spinner, no progress bar, no live display — and the command declares what each stream accepts itself rather than leaving the library to decide, so a redirected run and a run setting `NO_COLOR` are drawn without escape sequences. | MIT, declared as a license expression in each package's NuGet metadata and confirmed through the GitHub license API for `spectreconsole/spectre.console`, which builds both | Allowed. Permissive license supports both distribution models with notice preservation. Both assemblies are trimmed into the published single-file binaries, and the trimmed publish reports no warning against either. | <https://github.com/spectreconsole/spectre.console/blob/main/LICENSE.md>, <https://www.nuget.org/packages/Spectre.Console/0.57.2>, <https://www.nuget.org/packages/Spectre.Console.Ansi/0.57.2> |
| YamlDotNet 18.1.0 | Reads a deployment-provisioned configuration file written in YAML in the host, and renders and reads back the YAML view `mfctl --format yaml` offers of a document the deployment holds as JSON. Only the event-level parser and emitter are used — no object serializer — so nothing reaches the library through reflection and the trimmed `mfctl` binaries stay free of it; anchors, aliases, tags, and several documents are refused by MailFathom's own reader on top of that parser. The package has no dependencies and carries a `net10.0` build. It is not among the `mfctl` packages of its own counted above, because the host redistributes the same assembly. | MIT, declared as a license expression in the NuGet package metadata, copyright Antoine Aubry and contributors | Allowed. Permissive license supports both distribution models with notice preservation. Chosen over a configuration-provider package built on it because a provider applies its own scalar and anchor rules, which are the ones this repository refuses. | <https://github.com/aaubry/YamlDotNet/blob/master/LICENSE.txt>, <https://www.nuget.org/packages/YamlDotNet/18.1.0> |
| System.Security.Cryptography.ProtectedData 10.0.12 | DPAPI, which the base class library does not carry because it exists on one platform. The `mfctl` command uses it to bind the key sealing its stored credentials to the current Windows user; the call is guarded by a runtime platform check, so the Linux binaries carry the assembly and never enter it. | MIT, declared as a license expression in the NuGet package metadata and confirmed through the GitHub license API for `dotnet/runtime`, which is the repository that builds it | Allowed. Permissive license supports both distribution models with notice preservation. Microsoft-authored and versioned with the runtime. | <https://github.com/dotnet/runtime/blob/main/LICENSE.TXT>, <https://www.nuget.org/packages/System.Security.Cryptography.ProtectedData/10.0.12> |
| `react` 19.3.0, `react-dom` 19.3.0, and the transitive `scheduler` 0.28.0, in `frontend/src/Client.App` | The client's rendering runtime, and three of the twenty-four packages the bundle `pnpm build` writes carries — everything else the workspace pins is a build or lint tool that leaves nothing behind in the output. The other twenty-one are the five rows below, and § *The client's three dependency closures* is where that count is checked against the built chunk rather than asserted. | MIT, declared as a license expression in each package's npm metadata and carried as `LICENSE` in the upstream repository | Allowed, and **live** against two artifacts: `deploy/docker/Dockerfile` builds the bundle in a stage of its own and copies it into `/app/wwwroot`, so every image this repository publishes redistributes all three, and every desktop bundle `build-desktop-client.yml` publishes packages that same output behind the shell recorded in the next row. What that owes is the MIT notice for each, and the bundle carries none — it is minified into one chunk and no upstream package ships a banner, which was verified by searching the built chunk for one. The notice is therefore carried as text beside the code: `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt`, which the build copies verbatim into the bundle and which reproduces the one licence text all three share. Moving any of these three pins moves that file with them. The desktop bundles carry the same file for the same reason and by the same mechanism — the shell packages what `pnpm build` wrote, `public/` and all — so one notice answers for both artifacts rather than each needing its own. | <https://github.com/facebook/react/blob/main/LICENSE>, <https://www.npmjs.com/package/react/v/19.3.0>, <https://www.npmjs.com/package/react-dom/v/19.3.0>, <https://www.npmjs.com/package/scheduler/v/0.28.0> |
| `@tauri-apps/plugin-opener` 2.7.1 and the `@tauri-apps/api` 2.12.3 behind it, in `frontend/src/Client.App` | Two more of the packages the bundle carries. The client asks one operation of whichever shell it is running in — that a link a reader followed is opened out of the application rather than navigated to inside it, which is what [ADR 0024](docs/decisions/0024-rendering-mail-in-the-client-as-a-closed-document-tree.md) requires of both heads — and on the desktop head that operation is the opener plugin's, reached through the API package's command bridge. The web head answers the same call with `window.open` and enters neither. `frontend/src/Client.App/src/shellOperations/linkOpener.ts` is the one place either is imported, and `frontend/src-tauri/capabilities/open-a-link.json` narrows what the plugin may be asked to open to `http`, `https`, and `mailto`. | `MIT OR Apache-2.0` for the plugin and `Apache-2.0 OR MIT` for the API package, each declared as a license expression in its npm metadata, with both licence texts carried in the upstream repository. Taken here under **MIT**, which is the arm the bundle's notice reproduces. | Allowed, and **live** against the same two artifacts the row above names: they reach the bundle `pnpm build` writes, so `deploy/docker/Dockerfile` copies them into every published image and every desktop bundle packages them behind the shell. What MIT owes is the notice, and `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt` carries it beside React's — one file answering for both artifacts, by the same mechanism the row above records. Moving either pin moves that file with it. The plugin's Rust half is a separate component under separate terms and is the crate-closure row below rather than this one. | <https://github.com/tauri-apps/plugins-workspace/blob/v2/LICENSE_MIT>, <https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT>, <https://www.npmjs.com/package/@tauri-apps/plugin-opener/v/2.7.1>, <https://www.npmjs.com/package/@tauri-apps/api/v/2.12.3> |
| `@tauri-apps/plugin-notification` 2.6.0, in `frontend/src/Client.App` | One more package the bundle carries, and the second operation the client asks of whichever shell it is running in: that a system notification is raised while nobody is looking at the window, carrying how many arrived and of what kind. It reaches the shell's own plugin through the `@tauri-apps/api` command bridge the row above already records, so it adds one package to the bundle rather than two. The web head resolves the operation to one that does nothing and enters neither. `frontend/src/Client.App/src/shellOperations/systemNotifier.ts` is the one place it is imported, and `frontend/src-tauri/capabilities/raise-a-notification.json` narrows what the webview may ask of it to asking whether permission stands and asking for it, on the three desktop targets alone — raising one is asked of the shell's own command, which hands it to the plugin's Rust half. Its own binding reaches the plugin through the replacement for `window.Notification` the shell installs before the bundle runs, which is why the module's test stands that replacement up rather than the command bridge. | `MIT OR Apache-2.0`, declared as a license expression in its npm metadata, with both licence texts carried in the upstream repository. Taken here under **MIT**, which is the arm the bundle's notice reproduces and the same arm the opener plugin is taken under. | Allowed, and **live** against the same two artifacts the rows above name: it reaches the bundle `pnpm build` writes, so `deploy/docker/Dockerfile` copies it into every published image and every desktop bundle packages it behind the shell. What MIT owes is the notice, and `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt` carries it in the same block as the opener plugin's, both being the same text under the same copyright line. Moving this pin moves that file with it. The plugin's Rust half is the crate-closure row below rather than this one. | <https://github.com/tauri-apps/plugins-workspace/blob/v2/LICENSE_MIT>, <https://github.com/tauri-apps/plugins-workspace/blob/v2/plugins/notification/README.md>, <https://www.npmjs.com/package/@tauri-apps/plugin-notification/v/2.6.0> |
| The OpenTelemetry JavaScript closure the client's own instrumentation resolves — sixteen packages. Pinned directly: `@opentelemetry/api` 1.9.1 and `@opentelemetry/api-logs` 0.223.0 in `frontend/src/Client.Backend`, and in `frontend/src/Client.App` those same two packages beside `@opentelemetry/resources` 2.12.0, `@opentelemetry/sdk-trace-web` 2.12.0, `@opentelemetry/sdk-metrics` 2.12.0, `@opentelemetry/sdk-logs` 0.223.0, `@opentelemetry/core` 2.12.0, and the three exporters `@opentelemetry/exporter-trace-otlp-proto`, `@opentelemetry/exporter-metrics-otlp-proto`, and `@opentelemetry/exporter-logs-otlp-proto`, all 0.223.0. Behind them the lock file resolves `@opentelemetry/sdk-trace` 2.12.0, `@opentelemetry/sdk-trace-base` 2.12.0, `@opentelemetry/otlp-transformer` 0.223.0, `@opentelemetry/otlp-exporter-base` 0.223.0, `@opentelemetry/exporter-metrics-otlp-http` 0.223.0, and `@opentelemetry/semantic-conventions` 1.43.0 | The client's own traces, metrics, and logs, and the whole of what carries them: one pipeline composed at `frontend/src/Client.App/src/main.tsx`, exporting OTLP over HTTP with protobuf to the receiver the client endpoint serves on the deployment the client is signed in to. The split across the two packages is the client's own boundary rather than a packaging preference — `Client.Backend` may name no browser API, so it pins the two interface packages alone, each of which is a registry and a set of interfaces, and every implementation is registered by the application above it. `@opentelemetry/sdk-trace-base`, `@opentelemetry/sdk-metrics`, and `@opentelemetry/sdk-logs` are additionally pinned as devDependencies, in `Client.Backend` and in `Client.App`, because a test that proves what a span or a log record carries needs somewhere to read one back from; all three are already in the closure this row records, so none of them adds a package to it. `frontend/src/Client.App/src/telemetry/`, `frontend/src/Client.Backend/src/telemetry.ts`, and `frontend/src/Client.Backend/src/session.ts` are the only modules that import any of the sixteen — the last of those for the propagator alone, which writes the request's trace context into the headers every call on the client surface composes there | Apache-2.0 for all sixteen, declared as a license expression in each package's npm metadata and carried as `LICENSE` in each published package. **None of them ships a `NOTICE` file**, which was checked across the installed closure rather than assumed, so Apache-2.0 § 4(d) attaches nothing here and what § 4 obliges is the licence text, the retained copyright and attribution notices, and the disclaimer | Allowed, and **live** against the same two artifacts the two rows above name: the bundle `pnpm build` writes carries them, `deploy/docker/Dockerfile` copies that bundle into `/app/wwwroot` of every published image, and every desktop bundle packages the same output. These are the first packages under a term other than MIT to reach a bundle, so the notice beside it grew a section rather than a paragraph: `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt` names all sixteen with their versions and reproduces the Apache-2.0 text once, which is what all sixteen share. Moving any of these pins moves that file with them. **The bundle is split so that the fourteen behind the two interface packages are fetched rather than served**: the SDK and the exporters sit in a chunk of their own, which a browser downloads beside the first screen rather than inside it. It is fetched as the client starts rather than at a sign-in, because the pipeline records from the composition root and holds what it records until there is a session to attribute it to. `@opentelemetry/api` and `@opentelemetry/api-logs` are the two in the entry chunk, being the registries every recording call reaches whether or not a pipeline was ever registered behind them. That is a decision about what a person waits for rather than about licensing, and it changes nothing here — a chunk the deployment serves is redistributed exactly as the entry chunk is | <https://github.com/open-telemetry/opentelemetry-js/blob/main/LICENSE>, <https://www.npmjs.com/package/@opentelemetry/api/v/1.9.1>, <https://www.npmjs.com/package/@opentelemetry/sdk-trace-web/v/2.12.0>, <https://www.npmjs.com/package/@opentelemetry/exporter-trace-otlp-proto/v/0.223.0>, <https://www.apache.org/licenses/LICENSE-2.0> |
| `@microsoft/signalr` 10.0.11, in `frontend/src/Client.App` | The client's half of the signal channel, and one more of the packages the bundle carries. The deployment serves a SignalR hub beneath `/api/client`, and this is what opens a connection to it, presents the single-use ticket an authenticated route minted, and hands each payload the deployment sent to the client's own parser. `frontend/src/Client.App/src/signals/signalChannel.ts` is the one module that imports it, deliberately: `Client.Backend` declares no DOM and cannot name a package whose type declarations do, so it publishes the connection as a function type and the application supplies this implementation — the same boundary `sendToDeployment.ts` holds for `fetch`. The connection is opened over WebSockets alone with negotiation skipped, which is what makes a ticket that opens exactly one connection possible. Nothing is added on the service side for it: `Microsoft.AspNetCore.SignalR` is part of the shared framework the host already targets, so no row below moves. | MIT, declared as a license expression in the package's npm metadata. The published package ships no licence file of its own; the text is the `LICENSE.txt` at the root of `dotnet/aspnetcore`, which is the repository the package's metadata names and which the GitHub licence API reports as MIT. The package resolves five further packages for a Node runtime — `abort-controller` 3.0.0, `eventsource` 2.0.2, `fetch-cookie` 2.2.0, `node-fetch` 2.7.0, and `ws` 7.5.13, with twelve more behind them, `tough-cookie` 4.1.4 under `fetch-cookie` among them — of which `fetch-cookie` is the closure's one Unlicense entry and is decided in § *The client's three dependency closures* above. | Allowed, and **live** against the same two artifacts the rows above name: the bundle `pnpm build` writes carries it, `deploy/docker/Dockerfile` copies that bundle into `/app/wwwroot` of every published image, and every desktop bundle packages the same output. What MIT owes is the notice, and `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt` carries it beside the others; moving this pin moves that file with it. **The seventeen packages behind it reach no artifact**, which is the one claim here worth checking rather than assuming: the package reaches for them inside a branch it takes only outside a browser, through a `require` resolved at run time, so the built chunk keeps their names as string arguments and none of their code. That was read off `frontend/src/Client.App/dist/assets/index-*.js` after a build, and it is why the notice names this package alone. Were a later version to bundle them, each would owe a notice of its own and `fetch-cookie` would need deciding as a redistributed component rather than as a closure entry. | <https://github.com/dotnet/aspnetcore/blob/main/LICENSE.txt>, <https://www.npmjs.com/package/@microsoft/signalr/v/10.0.11>, <https://learn.microsoft.com/en-us/aspnet/core/signalr/javascript-client> |
| `markdown-to-jsx` 9.10.3, in `frontend/src/Client.App` | The Markdown renderer an agent answer is drawn with: the synthesized answer's text is whatever a model wrote, and a model structures an answer in Markdown, so `frontend/src/Client.App/src/answerCanvas/blocks/AnswerText.tsx` — the one module that imports it — turns that text into React elements rather than showing its markers. It builds elements and never HTML, and the module refuses what would reach past the answer: raw HTML is parsed as text, an image renders nothing and so fetches nothing, and a link is drawn as its words without a destination. It declares no dependency and only optional peers, so the one package is the whole of what it adds to the closure and to the bundle. | MIT, declared as a license expression in the package's npm metadata and carried as `LICENSE` in the published package | Allowed, and **live** against the same two artifacts the rows above name: the bundle `pnpm build` writes carries it, `deploy/docker/Dockerfile` copies that bundle into `/app/wwwroot` of every published image, and every desktop bundle packages the same output. What MIT owes is the notice, and `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt` carries it beside the others; moving this pin moves that file with it. | <https://github.com/quantizor/markdown-to-jsx/blob/main/lib/LICENSE>, <https://www.npmjs.com/package/markdown-to-jsx/v/9.10.3> |
| Geist and Geist Mono, the Google Fonts `v5` and `v6` revisions, as the four `woff2` subsets committed under `frontend/src/Client.App/src/assets/fonts/`: the one upright variable face each family is published as, covering the whole 100–900 weight range, each split into the `latin` and `latin-ext` subsets the upstream stylesheet splits it into | The client's two typefaces, and the whole of both. The design settles them — Geist for everything a screen says and Geist Mono for a literal — `--font-sans` and `--font-mono` in `frontend/src/Client.App/src/styles.css` are where they are declared, and the four `@font-face` rules above those declarations point at these files relatively — which is what puts them in the bundle. Neither family publishes an italic, so a slanted run is the upright face obliqued by the browser rather than a fifth file. They are served from the deployment and from nowhere else, deliberately: the design's own pages load both families from `fonts.googleapis.com`, and a deployment that keeps mail on its own server does not hand a font CDN a request per reader, while the desktop head has to render with no route out at all. The files are the upstream subsets byte for byte; nothing here reshapes, renames, or re-subsets them | SIL Open Font License 1.1, from the one `OFL.txt` in the upstream repository that both families are published under, and declared as the repository licence through the GitHub licence API. Google Fonts serves the same families under the same terms, which is where these particular subset files come from | Allowed, and **live** against the same two artifacts the client's package rows are: the bundle travels inside every published container image and inside every desktop application. The OFL permits bundling and redistribution inside a software package, and it attaches two conditions rather than one. The copyright notice and the licence text must travel with the font files, and they do: `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt` reproduces the upstream `OFL.txt` in full and is copied verbatim into the built bundle, so it travels wherever the fonts do. And a Reserved Font Name may not be used for a modified version — the upstream copyright line declares none, and nothing here modifies the files either, so that condition has nothing to bind; re-subsetting or hinting them later would still be a modification, which is a change to review rather than one to make in passing. The OFL's one prohibition, selling the fonts on their own, is not something any MailFathom artifact does | <https://github.com/vercel/geist-font/blob/main/OFL.txt>, <https://fonts.google.com/specimen/Geist>, <https://fonts.google.com/specimen/Geist+Mono> |
| Material Symbols Rounded, the one hundred and fifty-four outlines listed in `frontend/src/Client.App/src/controls/icons.ts` and committed beside it under `frontend/src/Client.App/src/assets/icons/`, taken from `google/material-design-icons` at commit `84ccef280841abfac506afc4ad4a2782f6d0a1d0`, each the `<name>_wght300_24px.svg` form — weight 300, `FILL 0`, `GRAD 0` | The client's whole iconography, which the design settles as this family at this weight, addressed by the ligature name Google's catalogue uses. `icons.ts` inlines each file at build time and `Icon.tsx` draws it as one path, so a symbol costs no request; only the symbols the client actually draws are in the tree, and its own test fails both a declared name with no file and a file nothing draws. The variable icon *font* the project loads from `fonts.googleapis.com` is deliberately not taken: the reason is the one above the row for the typeface, and the SVGs are what that reason leaves | Apache-2.0, from the `LICENSE` at the root of the upstream repository and declared as the repository licence through the GitHub licence API | Allowed, and **live** against the same two artifacts. Apache-2.0 permits redistribution of the files verbatim and attaches its notice condition: a distribution must carry the licence text and the attribution for these outlines, and `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt` carries both beside the font notice, in the same bundle the outlines are compiled into. The outlines are copied unchanged apart from being read for their one `d` attribute at build time, which selects rather than modifies. Nothing here uses Google's or Material's trademarks to describe MailFathom, and the family name appears only in this row and in the comment at the head of `icons.ts` that says where the files came from | <https://github.com/google/material-design-icons/blob/master/LICENSE>, <https://fonts.google.com/icons> |
| Noto Emoji, the thirty-two emoji pictures committed under `frontend/src/Client.App/src/assets/emoji/` and listed in `frontend/src/Client.App/src/composer/offeredEmoji.ts`, taken from `googlefonts/noto-emoji` at commit `99701e07d3d1fe1bdbcdb14964547f0fd592966f` out of its `2D/svg/` directory, each the `emoji_u<code points>.svg` file it publishes, byte for byte | What the composer's *Emoji* menu draws each emoji it offers with, so the picker shows an openly licensed drawing on every head rather than whichever emoji font the operating system carries. A choice is written into the message as the character itself, so nothing of these files reaches a message; the bundle serves each as a file of its own, fetched only once the menu draws it, and `offeredEmoji.test.ts` fails both an offered emoji with no file and a file the menu does not offer | Apache-2.0, from the `LICENSE` file in the upstream `2D/svg/` directory these files are taken from. The repository's root `LICENSE` is the SIL Open Font License 1.1 and governs the emoji *fonts*; the upstream README states that the image resources are under Apache-2.0, and the directory's own licence file is the one read here | Allowed, and **live** against the same two artifacts the outlines above ship in. Apache-2.0 permits redistribution of the files verbatim and attaches its notice condition, which `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt` meets with the attribution beside the Material Symbols entry and the licence text the two share. The files are copied unchanged. Nothing here uses Google's or Noto's trademarks to describe MailFathom | <https://github.com/googlefonts/noto-emoji/blob/main/2D/svg/LICENSE>, <https://github.com/googlefonts/noto-emoji#license> |
| Simple Icons 16.31.0, the nine brand marks committed under `frontend/src/Client.App/src/assets/providers/` — `apple.svg`, `auth0.svg`, `discord.svg`, `github.svg`, `gitlab.svg`, `gmail.svg`, `keycloak.svg`, `nextcloud.svg`, and `okta.svg` — each the file the `simple-icons` package publishes at that version, byte for byte | The marks the sign-in screen's provider grid draws, which are the one family in the client that is neither Material Symbols nor MailFathom's own and are therefore in a directory of their own. `frontend/src/Client.App/src/signIn/providerMarks.ts` inlines each file at build time and matches it on the name the deployment's configuration gives an authorization server, so a mark costs no request and a provider the client carries none for is drawn with the `key` symbol instead — which is the ordinary case, since an operator names their own servers. The files carry no colour of their own, so what each mark is drawn in is declared beside the palette in `frontend/src/Client.App/src/styles.css` — the brand colour the design's own provider table gives it, which is the same value this family publishes per icon. The design draws these from `cdn.simpleicons.org`; that is the one place the screen departs from it, under the rule in `frontend/src/AGENTS.md` § *UI* that everything a screen draws comes out of the bundle. The package itself is pinned nowhere and is not a dependency: the nine files were taken out of it and committed, which is what this row records in place of a pin | CC0-1.0, declared as the `license` field of the upstream package and as `LICENSE.md` at the root of the upstream repository. The upstream states separately that a brand's own trademark rights are unaffected by it | Allowed, and **live** against the same two artifacts the rows above are: the bundle travels inside every published container image and inside every desktop application. CC0-1.0 is a dedication to the public domain and attaches no condition at all — no notice, no attribution, no licence text to carry — so `frontend/src/Client.App/public/THIRD-PARTY-NOTICES.txt` is unchanged by this family and names it nowhere, which is the accurate record rather than an omission. What CC0 does not grant is trademark permission, and nothing here needs one: each mark is drawn beside the name the deployment's own operator configured for a server they run or subscribe to, as a label for where that person signs in, and no MailFathom artifact uses any of these marks to describe MailFathom or to suggest any of these companies endorse it | <https://github.com/simple-icons/simple-icons/blob/master/LICENSE.md>, <https://simpleicons.org> |
| The Rust crate closure the desktop shell links: tauri 2.12.3, tauri-build 2.7.2, keyring 4.2.0, tauri-plugin-opener 2.7.1, and tauri-plugin-notification 2.6.0 pinned in `frontend/src-tauri/Cargo.toml`, and the 502 crates `frontend/src-tauri/Cargo.lock` resolves behind them — wry 0.57.0 and tao 0.37.1 are the webview and window layers under Tauri, the gtk, glib, soup3, and webkit2gtk binding crates are how the Linux build reaches the system WebView, the zbus 5.19.0 and secret-service 5.2.0 families under keyring are how it reaches the credential store of the session it runs in, open 5.4.2 with is-docker and is-wsl beside it is how the opener plugin reaches the desktop's own idea of a default browser, and notify-rust 4.18.2 is how the notification plugin reaches each platform's own notification service — the Freedesktop one over D-Bus on Linux, mac-notification-sys 0.6.15 on macOS, and tauri-winrt-notification 0.8.1 on Windows — with the rand 0.9.5 family and zerocopy 0.8.56 behind the plugin | Both native heads. `frontend/src-tauri/src/lib.rs` opens one window over the bundle `pnpm build` writes, and links no crate this repository wrote beyond that; the Android head builds that same library as a shared object and links the same graph but for one pin, the notification plugin and the nine crates behind it being stated per target so that they reach no mobile build — `frontend/src-tauri/Cargo.toml` carries the reason, which is the Android project's own locked dependency graph rather than anything about terms. Beside the window it registers the six commands the client's credential store, its configuration reader, and its system notification call, and two upstream plugins. The commands are the whole of what `keyring` is pinned for: ADR 0023 keeps the finished `Basic` header value in the operating system's own store on this head, and one crate reaching all three desktop platforms' stores is what keeps that out of the application above it. Its Linux path speaks the Secret Service D-Bus interface through `zbus` rather than linking `libsecret`, so it adds a conversation with whatever store the session already runs and nothing to what the binary links — which is why the `libsecret` row in *Called on the machine an artifact runs on* stays `mfctl`'s alone. The two plugins answer the two operations the bundle asks of a shell — that a link a reader followed is opened out of the application, per [ADR 0024](docs/decisions/0024-rendering-mail-in-the-client-as-a-closed-document-tree.md), and that a system notification carrying a count and a kind is raised while nobody is looking at the window — and each is narrowed rather than taking the plugin's own default permission set: `frontend/src-tauri/capabilities/open-a-link.json` to the three URI schemes a message body may carry, and `frontend/src-tauri/capabilities/raise-a-notification.json` to the two permission commands the application calls, on the three desktop targets alone. Raising the notification is not among them, because the bundle asks this shell for it rather than the plugin: `frontend/src-tauri/src/notifications.rs` hands it to the plugin's Rust half, which raises it and reports a click on it on every desktop target, and answers that report by bringing the window forward — so `notify-rust` is a crate the plugin resolves rather than one this manifest pins. The closure is one graph across every target Cargo resolves for, so it names crates neither supported head compiles: the objc2 and core-foundation families are macOS's, the windows-* families are the Windows build's alone, and r-efi is reached only on a UEFI target nothing here builds. | Permissive throughout, read from each crate's own `license` expression through `cargo metadata` over the committed lock file. 311 crates offer `MIT OR Apache-2.0` in one spelling or another, 111 are MIT alone, and 11 offer `Unlicense OR MIT` — the `jiff` family, aho-corasick, byteorder, memchr, same-file, walkdir, and winapi-util — which are taken under MIT rather than under the public-domain dedication beside it, because a dedication is not a licence in every jurisdiction and the alternative costs nothing. The rest either offer a choice that includes MIT or Apache-2.0 and are taken under one of those — num_enum and num_enum_derive, brotli-decompressor, bytemuck, miniz_oxide, tinyvec, raw-window-handle, dunce, adler2, zerocopy and zerocopy-derive, which offer `BSD-2-Clause OR Apache-2.0 OR MIT` and arrive under the notification plugin's random-number crates, and the macOS objc2 family — or offer no choice at all, which is the short list that decides what a notice bundle carries: Apache-2.0 for tao 0.37.1, the window layer itself, and sync_wrapper 1.0.2; BSD-3-Clause for brotli's two allocator crates alloc-no-stdlib 3.0.0 and alloc-stdlib 0.3.0; Zlib for foldhash 0.2.0 and zlib-rs 0.6.7; ISC for libloading 0.7.4; and Unicode-3.0 for the eighteen ICU4X crates. Four crates state a **conjunction** rather than a choice, which is the one shape a notice bundle cannot collapse, because every named term applies at once instead of one of them being selectable: brotli 9.0.0 is `BSD-3-Clause AND MIT`, dpi 0.1.2 is `Apache-2.0 AND MIT`, unicode-ident 1.0.24 is `(MIT OR Apache-2.0) AND Unicode-3.0`, and target-lexicon 0.12.16 is `Apache-2.0 WITH LLVM-exception` with no alternative offered — a build dependency, so it reaches no binary. Five more offer the LLVM exception as one option among three and are taken under MIT: wasi 0.11.1, wasip2 1.0.4, and wit-bindgen 0.57.1, none of which the Linux build links, and rustix 1.1.4 and linux-raw-sys 0.12.1, which it does link — reached under `keyring`'s Linux path and under the opener plugin's process and event-loop crates. Linking them changes nothing, because the exception widens Apache-2.0's patent grant rather than adding a condition, and the MIT option beside it is what is taken either way. **No crate in the closure is offered under a copyleft license this project would have to accept**: the one that names one at all, r-efi 5.3.0 and 6.0.0, offers `MIT OR Apache-2.0 OR LGPL-2.1-or-later` and is taken under MIT. Five crates are MPL-2.0 and have no alternative: cssparser 0.37.0, cssparser-macros 0.7.1, dtoa-short 0.3.5, and selectors 0.38.0, which Tauri's own HTML rewriting reaches, and option-ext 0.2.0 behind `dirs`. | Allowed, and **live**: `.github/workflows/build-desktop-client.yml` builds this closure into a desktop application that a release attaches and a nightly leaves on its run, and `.github/workflows/build-android-client.yml` builds the same closure but for the notification plugin into the shared object inside the APK a nightly leaves beside it, so the crates it links are redistributed in both. What is owed is a notice bundle reproducing the MIT, Apache-2.0, BSD-3-Clause, Zlib, ISC, and Unicode-3.0 texts and their copyright lines, travelling beside `NOTICE` in the artifact rather than inside it — outstanding against a published artifact, on the same standing as the service's own and owned by the same issue #191. The five MPL-2.0 crates are the one term the acceptance policy above places in the review-before-use band, and this is that review: they are used unmodified from crates.io and statically linked into a binary, which MPL-2.0 sections 3.2 and 3.3 permit for a Larger Work distributed under other terms, on the condition that the Covered Software stays separately available and separately replaceable. Both hold — each is an unmodified published crate whose source is the upstream release the lock file names, and swapping one is an edit to `Cargo.toml` — so the obligation is discharged by naming them and their upstream in the notice bundle, and MailFathom's own source is unaffected. A modification to any of the five would have to be published under MPL-2.0, which is why none is vendored. **No AppImage is published, and that is this row's decision rather than a packaging preference.** An AppImage packages the host's own GTK and WebKitGTK shared libraries into the artifact, several of them LGPL-2.1-or-later, which would put this project in the position of redistributing them under section 6 and owing a source offer for a graph nothing here pins or reviews. A `deb` and an `rpm` depend on the distribution's copies instead and carry none of it, so `frontend/src-tauri/tauri.conf.json` bundles those two on Linux and no AppImage, and the format is what was dropped rather than the obligation waived. | <https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT>, <https://github.com/tauri-apps/tauri/blob/dev/LICENSE_APACHE-2.0>, <https://crates.io/crates/tauri/2.12.3>, <https://github.com/tauri-apps/wry>, <https://github.com/tauri-apps/tao>, <https://github.com/open-source-cooperative/keyring-rs/blob/main/LICENSE-MIT>, <https://github.com/open-source-cooperative/keyring-rs/blob/main/LICENSE-APACHE>, <https://crates.io/crates/keyring/4.2.0>, <https://github.com/tauri-apps/plugins-workspace/blob/v2/LICENSE_MIT>, <https://crates.io/crates/tauri-plugin-opener/2.7.1>, <https://crates.io/crates/tauri-plugin-notification/2.6.0>, <https://github.com/hoodie/notify-rust>, <https://github.com/hwchen/secret-service-rs>, <https://github.com/dbus2/zbus>, <https://github.com/servo/rust-cssparser/blob/main/LICENSE>, <https://www.mozilla.org/en-US/MPL/2.0/>, <https://www.unicode.org/license.txt> |
| The Android runtime closure the head's APK carries: `androidx.webkit:webkit` 1.14.0, `androidx.appcompat:appcompat` 1.7.1, `androidx.activity:activity-ktx` 1.10.1, `androidx.lifecycle:lifecycle-process` 2.10.0, and `com.google.android.material:material` 1.12.0 declared in `frontend/src-tauri/gen/android/app/build.gradle.kts`, and the **81 artifacts** Gradle resolves behind them onto the packaged runtime classpath, fixed by the `app/gradle.lockfile` committed beside those declarations — the rest of AndroidX under those five, the Kotlin standard library 2.2.10 with the `kotlinx-coroutines` 1.9.0 and `kotlinx-serialization` 1.7.3 families beside it, `com.fasterxml.jackson.core` 2.15.3 with `com.fasterxml.jackson.datatype:jackson-datatype-json-org` 2.15.3 beside it, which is how Tauri's Android library writes a plugin's JSON values, `com.google.errorprone:error_prone_annotations` 2.15.0, `com.google.guava:listenablefuture` 1.0, `org.jetbrains:annotations` 23.0.0, and `org.jspecify:jspecify` 1.0.0 | The Android head, which [ADR 0027](docs/decisions/0027-an-android-head-built-every-night-and-supported-by-nothing.md) makes one debug-signed APK left on the run that built it rather than a release artifact — the nightly `Build the Android client` job is what performs that run, and no release channel calls it. `androidx.webkit` is how the activity reaches the system WebView the client renders in, and the other four are what Tauri's own Android template compiles its activity against — including the Kotlin this repository writes into that project, which reaches `androidx.core` under `appcompat` for the window insets the committed activity takes off the page. Nothing here is a dependency this repository chose screen by screen: the five are the template's, and our own Kotlin compiles against what they already resolve rather than declaring anything beside them. The application source is the same bundle both other heads serve — this graph is the shell's, exactly as the crate closure above it is. | Apache-2.0 throughout, read from each artifact's own published POM: every `androidx.*` module and `com.google.android.material` declare *The Apache Software License, Version 2.0* on Google's Maven repository, and the Kotlin, `kotlinx`, Jackson, Error Prone, JSpecify, and JetBrains annotations artifacts declare the same on Maven Central. `com.google.guava:listenablefuture` 1.0 states none of its own and inherits it from `guava-parent` 26.0-android, which does. | Allowed. One permissive term across the whole closure, and the weakest notice position of the three artifacts here: an APK left on a run is not a release, so no release page and no store listing carries a notice for it, and ADR 0027 item 8 defers the [ADR 0016](docs/decisions/0016-third-party-licence-obligations-per-artifact.md) review of the APK as an artifact — the notice bundle a supported head would ship — to the decision that makes it one. What is not deferred is this row, because the components are pinned in files this repository now commits. Apache-2.0 section 4 obligations are what such a bundle would carry, and no term here restricts a field of use or reaches MailFathom's own licence. | <https://maven.google.com/web/index.html>, <https://developer.android.com/jetpack/androidx>, <https://github.com/material-components/material-components-android>, <https://github.com/JetBrains/kotlin>, <https://github.com/FasterXML/jackson-databind>, <https://github.com/FasterXML/jackson-datatypes-misc>, <https://github.com/google/guava> |

## Called on the machine an artifact runs on

Neither pinned nor bundled: a library the operating system already has, which a published artifact reaches on the
machine it runs on rather than carrying. Nothing here is restored or carried inside any artifact. Two shapes of that
sit below and their obligations differ, so each row says which it is: a library resolved by name at the first call,
which a machine without it simply does not have, and a library the build links against and the artifact then requires,
which is the LGPL's own shared-library case rather than a dependency that can be absent. A platform's own API — the
Win32 functions `mfctl` reaches for the same purpose, or anything else in the runtime a head is written to run on —
earns no row: it is the operating system rather than a component this project chose.

| Component and exact version | Use in this repository | License terms verified | Compatibility decision and obligations | Sources reviewed |
|---|---|---|---|---|
| `libsecret-1.so.0`, `libglib-2.0.so.0`, `libgio-2.0.so.0`, and `libgobject-2.0.so.0`, whichever versions the operator's distribution installed | The Secret Service side of where a credential is kept on Linux, reached from `backend/src/Cli/Credentials/SecretStores/SecretServiceKeyring.cs`, where `mfctl` keeps a profile's credential. It calls `secret_password_storev_sync`, `secret_password_lookupv_sync`, `secret_password_clearv_sync`, and `secret_password_free`, builds their attribute table with `g_hash_table_new`, `g_hash_table_insert`, and `g_hash_table_unref`, bounds each of them with a `GCancellable` from `g_cancellable_new`, `g_cancellable_cancel`, and `g_cancellable_is_cancelled`, and releases it with `g_object_unref`. The three beside `libsecret` are all the GLib project's own — one source tree, one licence, and already a dependency of `libsecret` itself — and each is here because `libsecret`'s own API asks for it: a `GHashTable` for the attributes, a `GCancellable` for a call that must not run forever, and GObject reference counting to release the second of those. All four are resolved by name at the moment of the first call, so a machine without them — a headless server, a jump host, a container — reports no store and the command seals the credential into its own file instead. The other platforms' halves of the same port call `advapi32` on Windows and the Security and CoreFoundation frameworks on macOS, and are the platform API case the paragraph above excludes | LGPL-2.1-or-later for all four. `COPYING` in each upstream repository is the LGPL version 2.1 text of February 1999, and each project's own source headers grant it "either version 2.1 of the licence or (at your option) any later version"; `glib`, `gio`, and `gobject` are three shared objects built from the one GLib source tree under the one `COPYING` | Allowed after review, and **nothing follows for a release**. LGPL-2.1 section 6 attaches to distributing a work that contains or links the library; MailFathom distributes none of them. No header, import library, or shared object is present at build time — the calls are source-generated P/Invokes resolved by soname at run time — and no `mfctl` binary, container image, or chart carries any of them. The operator's own distribution supplies them under its own terms, exactly as it supplies the C library. **What would reopen it** is bundling any of these libraries with an artifact, static linking, or vendoring any part of the source: each would make MailFathom a distributor and bring section 6's notice, licence-text, and relinking obligations with it. Taking a NuGet binding for the same job would be a different review again, because a managed wrapper is restored and redistributed where this is neither, which is why the port reaches both platforms by P/Invoke | <https://github.com/GNOME/libsecret/blob/main/COPYING>, <https://github.com/GNOME/glib/blob/main/COPYING>, the licence header of `libsecret/secret-password.c`, and the GitHub license API for both repositories |
| `libwebkit2gtk-4.1.so.0` and `libgtk-3.so.0`, with the GLib, GObject, GIO, and libsoup objects they are built on, whichever versions the operator's distribution installed | The engine the desktop client renders in, and the toolkit its window belongs to. `frontend/src-tauri/` links Tauri, whose `wry` and `tao` layers reach both through the `webkit2gtk`, `javascriptcore-rs`, `gtk`, `glib`, and `soup3` binding crates recorded in *Redistributed with MailFathom*; `.github/workflows/build-desktop-client.yml` installs the development packages on the runner so the Linux build can link, and `bundle.linux.deb.depends` in `frontend/src-tauri/tauri.conf.json` declares `libwebkit2gtk-4.1-0` and `libgtk-3-0` so a `.deb` installs the distribution's own copies with it. The Windows head reaches WebView2 instead, recorded in the row below, and macOS is not built | WebKitGTK is offered under the LGPL and the BSD licence together — `webkit.org/licensing-webkit` publishes both texts, and the source tree carries `Source/WebCore/LICENSE-LGPL-2.1`, whose text is the LGPL version 2.1 of February 1999, beside the BSD terms Apple's own files carry. GTK 3's per-file headers grant the GNU Lesser General Public License "either version 2 of the License, or (at your option) any later version", and the project's own documentation declares `LGPL-2.1-or-later`; GLib and its siblings are the same LGPL-2.1-or-later already reviewed in the row above | Allowed after review, and what follows for a release is **section 6(b) and nothing more**. No artifact published here carries any of these libraries: the `.deb` and the `.rpm` name or expect the distribution's copies, the Windows head does not use them, and the one bundle format that would have packaged them — an AppImage — is deliberately not published, which the crate closure's row states. What the desktop head does is exactly the mechanism section 6(b) describes: it links dynamically against a copy already on the user's system, and a user replacing that copy with a modified one gets their version without rebuilding anything here, because the shared object is resolved by soname at load time. **What is still owed** is the notice half of section 6 — that these libraries are used, that their use is covered by the LGPL, and a copy of that licence — which the notice bundle issue #191 owns carries beside `NOTICE` rather than any step in the desktop build. **What would reopen it** is publishing an AppImage or any other bundle that packages the host's copies, static linking, or vendoring part of either source tree: each would make MailFathom a distributor of the library itself and bring section 6's source-or-offer obligation with it | <https://webkit.org/licensing-webkit/>, <https://github.com/WebKit/WebKit/blob/main/Source/WebCore/LICENSE-LGPL-2.1>, <https://docs.gtk.org/gtk3/>, <https://github.com/GNOME/gtk/blob/gtk-3-24/COPYING>, and the licence header of `gtk/gtkwidget.c` |
| The Microsoft Edge WebView2 Runtime, Evergreen channel, whichever version the machine has | The engine the desktop client renders in on Windows. Nothing here links it at build time — Tauri reaches it through the loader in its own crate closure — and the NSIS installer this project publishes runs Microsoft's Evergreen Bootstrapper when the machine has no runtime, which is Tauri's default install mode and the online workflow Microsoft's own distribution page describes. Windows 11 carries the runtime as part of the operating system and the vast majority of Windows 10 machines already have it, so the fetch is the exception rather than the path | Microsoft's own terms for the WebView2 Runtime, which are the licence the runtime's installer presents rather than an open-source expression. The distribution page states the two supported ways an application may deploy it — downloading the bootstrapper at install time, or packaging the standalone installer — and this project takes the first | Allowed, and **nothing is redistributed**. No artifact published here contains any part of the runtime or its installer: the bootstrapper is downloaded from Microsoft's own servers by the installer on the user's machine, and the runtime it installs is shared with every other WebView2 application there. What that costs is stated rather than hidden — the Windows installer needs an internet connection on a machine without the runtime, which `docs/operations/desktop-client.md` says. **What would reopen it** is switching the install mode to package the standalone installer or a fixed-version runtime, either of which would make this project a distributor of Microsoft's binaries under those terms | <https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution>, <https://developer.microsoft.com/en-us/microsoft-edge/webview2/> |

## Build-time and design-time only

These are restored and executed while building, and none appears in the published output. They are recorded because they are third-party code the repository depends on, not because a release has to carry them.

| Component and exact version | Use in this repository | License terms verified | Compatibility decision and obligations | Sources reviewed |
|---|---|---|---|---|
| Microsoft.EntityFrameworkCore.Design 10.0.12, and its toolchain: Humanizer.Core 2.14.1, Microsoft.Build.Framework 18.0.2, Microsoft.CodeAnalysis.Analyzers 3.11.0, Microsoft.CodeAnalysis.Common 5.0.0, Microsoft.CodeAnalysis.CSharp 5.0.0, Microsoft.CodeAnalysis.CSharp.Workspaces 5.0.0, Microsoft.CodeAnalysis.Workspaces.Common 5.0.0, Microsoft.CodeAnalysis.Workspaces.MSBuild 5.0.0, Microsoft.Extensions.DependencyModel 10.0.12, Microsoft.VisualStudio.SolutionPersistence 1.0.52, Mono.TextTemplating 3.0.0, Newtonsoft.Json 13.0.4, System.CodeDom 6.0.0, System.Composition 9.0.0, System.Composition.AttributedModel 9.0.0, System.Composition.Convention 9.0.0, System.Composition.Hosting 9.0.0, System.Composition.Runtime 9.0.0, and System.Composition.TypedParts 9.0.0 | Migration scaffolding through the AppHost's `mailfathom-migrations` resource. Referenced by `backend/src/Host` and `backend/src/Infrastructure` with `PrivateAssets=all`; reachability over the host's own dependency graph confirms that every package listed here is reachable only through that reference, and a publish emits none of them. | MIT, declared as a license expression in each package's NuGet metadata | Allowed as a design-time dependency. Not redistributed, so no notice travels with an artifact. `Microsoft.EntityFrameworkCore.Analyzers` 10.0.12 reaches the build the same way, through the runtime EF Core packages. | <https://github.com/dotnet/dotnet>, <https://github.com/dotnet/roslyn>, <https://github.com/Humanizr/Humanizer>, <https://github.com/JamesNK/Newtonsoft.Json>, <https://github.com/mono/t4>, <https://github.com/microsoft/vs-solutionpersistence> |
| Microsoft.CodeAnalysis.NetAnalyzers 10.0.401, Microsoft.CodeAnalysis.BannedApiAnalyzers 5.6.0, Microsoft.VisualStudio.Threading.Analyzers 18.12.1 | Build-time enforcement of the .NET code-quality rules, the symbols listed in `.config/BannedSymbols.txt`, and the async and threading diagnostics | MIT, declared as a license expression in each package's NuGet metadata | Allowed as development-only analyzer dependencies with `PrivateAssets=all` and analyzer-scoped `IncludeAssets`. | <https://www.nuget.org/packages/Microsoft.CodeAnalysis.NetAnalyzers/10.0.401>, <https://www.nuget.org/packages/Microsoft.CodeAnalysis.BannedApiAnalyzers/5.6.0>, <https://www.nuget.org/packages/Microsoft.VisualStudio.Threading.Analyzers/18.12.1> |
| Roslynator.Analyzers 5.0.1, Roslynator.CodeAnalysis.Analyzers 5.0.1, Roslynator.Formatting.Analyzers 5.0.1 | Build-time analyzers for C# quality, Roslyn API usage, and formatting | Apache-2.0, declared as a license expression in each package's NuGet metadata | Allowed as development-only analyzer dependencies with `PrivateAssets=all` and analyzer-scoped `IncludeAssets`. Not redistributed, so no `NOTICE` travels with an artifact. | <https://github.com/dotnet/roslynator>, <https://www.nuget.org/packages/Roslynator.Analyzers/5.0.1> |
| `vite` 8.3.4, `@vitejs/plugin-react` 6.1.2, `tailwindcss` 4.3.3, `@tailwindcss/vite` 4.3.3, `typescript` 6.0.3, and the type declarations `@types/react` 19.3.0, `@types/react-dom` 19.3.0, and `@types/node` 26.6.5 | The client's build. Vite bundles `frontend/src/Client.App` into the directory of static files the stack ships; its React plugin compiles JSX and drives fast refresh; Tailwind's Vite plugin reads the `@theme` tokens in `src/styles.css`, which is where the whole of the styling configuration lives; TypeScript answers `pnpm typecheck` and nothing else, because Vite strips types rather than checking them. Pinned in `frontend/package.json` and `frontend/src/Client.App/package.json` at exact versions, with `frontend/pnpm-lock.yaml` fixing the closure they resolve to. | MIT for all eight, declared as a license expression in each package's npm metadata, except TypeScript, which is Apache-2.0 and declares it the same way. The type declarations are DefinitelyTyped packages and are MIT under that project's own grant. | Allowed as build tooling. None of the eight reaches the bundle: what Vite emits is this repository's own source plus the three redistributed packages above, and what Tailwind emits is CSS generated from those tokens rather than any part of Tailwind. One package in the closure behind them carries a condition and is decided here rather than left to its section: **lightningcss 1.32.0 and 1.33.0, with its `lightningcss-linux-x64-gnu` native binding, are MPL-2.0**, which the acceptance policy above places in the review-before-use band. It is Tailwind's own CSS transformer and Vite's, it runs on a build machine, it is used unmodified and is neither linked into nor copied into anything this repository publishes, and MPL-2.0's obligations attach to distributing its own source files — which nothing here does. The CSS it emits is generated output rather than Covered Software, so no artifact of MailFathom's inherits the term. Two further closure licenses are named for the same reason: caniuse-lite 1.0.30001810 is CC-BY-4.0 browser-support **data** read by the target resolution, redistributed nowhere, and minimatch 10.2.6 is BlueOak-1.0.0, which is permissive and OSI-approved. | <https://github.com/vitejs/vite/blob/main/LICENSE>, <https://github.com/vitejs/vite-plugin-react/blob/main/LICENSE>, <https://github.com/tailwindlabs/tailwindcss/blob/main/LICENSE>, <https://github.com/microsoft/TypeScript/blob/main/LICENSE.txt>, <https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/LICENSE>, <https://github.com/parcel-bundler/lightningcss/blob/master/LICENSE>, <https://github.com/browserslist/caniuse-lite> |
| `eslint` 10.12.0, `@eslint/js` 10.0.1, `typescript-eslint` 8.71.1, `eslint-config-prettier` 10.1.8, `eslint-plugin-react-hooks` 7.1.1, `eslint-plugin-react-refresh` 0.5.7, `jiti` 2.7.0, and `prettier` 3.9.10 | The client's half of what `dotnet format` and the analyzer set are to the service. `frontend/eslint.config.ts` composes the first six into one flat configuration, with jiti loading that TypeScript configuration file, and both verification gates run it as `pnpm lint` with `--max-warnings 0`; Prettier is the formatter, repairing in the fast loop and verifying in the full gate, and it reads the repository's own `.editorconfig` rather than a configuration file of its own. | MIT for all eight, declared as a license expression in each package's npm metadata. Parts of the ESLint closure are Apache-2.0 — `@eslint/core`, `@eslint/plugin-kit`, `@humanfs/*`, `@humanwhocodes/*`, `eslint-visitor-keys` — and parts are BSD-2-Clause, which is the parser and scope machinery ESLint has always carried: `espree`, `eslint-scope`, `esrecurse`, `estraverse`, `esutils`. Both are permissive and neither is redistributed. | Allowed as development and build tooling. Nothing here is referenced from the client's source, reaches the bundle, or ships in any artifact: these run over the source and emit diagnostics and rewrites. | <https://github.com/eslint/eslint/blob/main/LICENSE>, <https://github.com/typescript-eslint/typescript-eslint/blob/main/LICENSE>, <https://github.com/prettier/prettier/blob/main/LICENSE>, <https://github.com/prettier/eslint-config-prettier/blob/main/LICENSE>, <https://github.com/facebook/react/blob/main/LICENSE>, <https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/LICENSE> |
| @tauri-apps/cli 2.12.2, with the `@tauri-apps/cli-linux-x64-gnu` and per-platform binaries npm resolves beside it | The Tauri command line, which `pnpm desktop:dev` and `pnpm desktop:build` reach through `frontend/src-tauri/run-tauri.ts` rather than through a shell, so the declared version is merged into the configuration as one process on every platform. It resolves the configuration, runs the web build as the desktop build's `beforeBuildCommand`, drives Cargo, and produces the bundles. `pnpm android:init` reaches it the same way, and that command is the one place the CLI writes rather than builds: it generates the Android Gradle project this repository then commits, which is recorded under *Externally sourced source material*. Pinned exactly in `frontend/package.json`, with `frontend/pnpm-lock.yaml` fixing the closure. | Apache-2.0 OR MIT, declared as a license expression in the npm package metadata and carried as `LICENSE_APACHE-2.0` and `LICENSE_MIT` in the upstream repository, which is the same repository that publishes the crates above. | Allowed as build tooling, taken under MIT. The CLI is a build machine's tool: it is not linked into any head and nothing it ships reaches an artifact by being linked — what an application carries is the crate graph recorded above and this repository's own bundle. What it does put into an artifact is the Android project it generates, whose own terms are reviewed in the row this repository committed it under. The native binaries are npm optional dependencies selected by platform, so a machine installs one of them and the rest are absent rather than unused. | <https://github.com/tauri-apps/tauri/blob/dev/LICENSE_MIT>, <https://www.npmjs.com/package/@tauri-apps/cli/v/2.12.2> |
| Gradle 9.6.1, reached through the wrapper committed at `frontend/src-tauri/gen/android/gradlew` and pinned by the `distributionUrl` and the `distributionSha256Sum` in `gradle/wrapper/gradle-wrapper.properties` beside it; the Android Gradle Plugin `com.android.tools.build:gradle` 9.3.1 and the Kotlin Gradle plugin `org.jetbrains.kotlin:kotlin-gradle-plugin` 2.2.10, both pinned in that project's `build.gradle.kts` | The Android head's build. `pnpm android:build` hands off to the wrapper, which downloads the pinned Gradle distribution and verifies it against that checksum, applies the two plugins, compiles the activity, calls back into the Tauri CLI for each Rust target, and assembles the APK. None of the three is linked into anything: the APK carries the runtime closure recorded above and this repository's own bundle. The Android SDK, the NDK, and the JDK the build reads through `ANDROID_HOME`, `NDK_HOME`, and `JAVA_HOME` are deliberately absent from this register — nothing here pins a version of any of them, and a component this repository neither pins nor ships earns no row. `.github/workflows/build-android-client.yml` installs none of the three either: the GitHub-hosted runner image already carries them, and the workflow names which of the image's copies to use, which is why it pins the runner image rather than any of the three. A step that installed a chosen version instead is what would earn one a row here. | Apache-2.0 for all three: Gradle publishes it as the repository's own `LICENSE`, and the two plugins declare *The Apache Software License, Version 2.0* in their published POMs on Google's Maven repository and on Maven Central. | Allowed as build tooling. None of the three appears in the published output of any artifact, and neither verification gate reaches them — a developer's own `pnpm android:build` and the nightly `Build the Android client` job are the whole of what runs this build. The wrapper's own `gradle-wrapper.jar` is committed rather than downloaded, which is a redistribution of a Gradle file inside this repository and is recorded as such under *Externally sourced source material*. | <https://github.com/gradle/gradle/blob/master/LICENSE>, <https://maven.google.com/web/index.html#com.android.tools.build:gradle:9.3.1>, <https://central.sonatype.com/artifact/org.jetbrains.kotlin/kotlin-gradle-plugin/2.2.10> |

## Test-only

Referenced by the service's unit and integration suites, by the client's suite, and by nothing that ships.

| Component and exact version | Use in this repository | License terms verified | Compatibility decision and obligations | Sources reviewed |
|---|---|---|---|---|
| xunit.v3.mtp-v2 4.0.2, and the transitive xunit.v3.assert, xunit.v3.common, xunit.v3.core.mtp-v2, xunit.v3.extensibility.core, xunit.v3.runner.common, and xunit.v3.runner.inproc.console at 4.0.2, plus System.Security.AccessControl 6.0.1, which xunit.v3.runner.common brings | Unit-testing framework and its Microsoft Testing Platform v2 runner integration | Apache-2.0 for the xUnit packages and MIT for System.Security.AccessControl, each declared as a license expression in that package's NuGet metadata | Allowed for test projects. Not redistributed. | <https://github.com/xunit/xunit>, <https://www.nuget.org/packages/xunit.v3.mtp-v2/4.0.2>, <https://github.com/dotnet/runtime> |
| xunit.analyzers 2.2.0 | Build-time analyzer for xUnit test code | Apache-2.0, declared as a license expression in the NuGet package metadata | Allowed as a development-only analyzer dependency for test projects with `PrivateAssets=all` and analyzer-scoped `IncludeAssets`. | <https://www.nuget.org/packages/xunit.analyzers/2.2.0>, <https://github.com/xunit/xunit.analyzers> |
| xRetry.v3 1.0.0 | `[RetryFact]` for xUnit v3, carried by the provider-contract tests in `backend/tests/IntegrationTests/ProviderAdapters`, so a rate limit or an overload from a hosted model provider is retried rather than reported as an adapter defect. Referenced by that project only. | MIT, declared as a license expression in the NuGet package metadata and in the upstream repository | Allowed for test projects. Not redistributed. The 1.0.0 release is the line that supports xunit.v3 4.x, which its package declares as a dependency range on `xunit.v3.extensibility.core` from 4.0.0 up to 5. | <https://github.com/JoshKeegan/xRetry>, <https://www.nuget.org/packages/xRetry.v3/1.0.0> |
| Microsoft.Extensions.AI.Evaluation 10.11.0, Microsoft.Extensions.AI.Evaluation.Quality 10.11.0, Microsoft.Extensions.AI.Evaluation.Reporting 10.11.0, and the `aieval` command of Microsoft.Extensions.AI.Evaluation.Console 10.11.0 | The agent evaluation suite in `backend/tests/Evaluations`, referenced by that project alone, and the command `scripts/run-ai-evaluations.sh` renders its report and prunes its store with. The evaluation core scores an answer; Quality supplies the evaluators a judge model grades with; Reporting the scenario runs, the response cache, and the on-disk result store. The console tool is pinned in `.config/dotnet-tools.json` and restored by `dotnet tool restore`. `Microsoft.Extensions.AI.Evaluation.NLP` is not adopted, because no scenario scores an answer against a reference text, and neither is `Microsoft.Extensions.AI.Evaluation.Safety`, which needs the Microsoft Foundry evaluation service when nothing here runs on Azure. The Agent Framework brings the core package into every graph that reaches it, the host's included, at its own floor of 10.10.0, which is why the redistributed row above names that version; the central pin raises it to 10.11.0 in the evaluation suite alone, and the other two and the console tool reach that suite alone. | MIT, declared as a license expression in each package's NuGet metadata | Allowed for test projects and as development-only tooling. Not redistributed. The console tool sends Microsoft usage telemetry unless `DOTNET_AIEVAL_TELEMETRY_OPTOUT` is set, and the script sets it for every invocation. What a requested run sends a provider is the committed synthetic corpus alone, and the providers' own terms are the ones already recorded under *Hosted services*. | <https://github.com/dotnet/extensions>, <https://www.nuget.org/packages/Microsoft.Extensions.AI.Evaluation/10.11.0>, <https://www.nuget.org/packages/Microsoft.Extensions.AI.Evaluation.Quality/10.11.0>, <https://www.nuget.org/packages/Microsoft.Extensions.AI.Evaluation.Reporting/10.11.0>, <https://www.nuget.org/packages/Microsoft.Extensions.AI.Evaluation.Console/10.11.0>, <https://github.com/dotnet/extensions/blob/main/src/Libraries/Microsoft.Extensions.AI.Evaluation.Console/TELEMETRY.md> |
| TngTech.ArchUnitNET.xUnitV3 0.13.4 and the transitive TngTech.ArchUnitNET 0.13.4, Mono.Cecil 0.11.6, Newtonsoft.Json 13.0.4, JetBrains.Annotations 2026.2.0, CycleDetection 2.0.0, and System.ValueTuple 4.6.2 | The boundary rules in `backend/tests/Boundaries.UnitTests`, which read the compiled intermediate language of every assembly a release ships so that a dependency existing only inside a method body, and a library confined to one namespace of an assembly, are both assertable. Referenced by that project only. Mono.Cecil is the reader that makes a method body visible; the remaining four are ArchUnitNET's own graph. | Apache-2.0 for both TngTech packages and MIT for all five transitive ones, each declared as a license expression in that package's NuGet metadata and confirmed against the upstream repository for the two Apache-2.0 ones | Allowed for test projects. Not redistributed, so no notice travels with an artifact. Pinned at a `0.x` version deliberately: the line has shipped since 2018 and the analyzer package alone has over eight million downloads, and what a wrong verdict from it would cost is bounded by the rules being read as tests rather than as a gate on anything that ships. Newtonsoft.Json arriving here is worth naming, because nothing else in the repository takes it as a direct dependency and no production project resolves it through this path. | <https://github.com/TNG/ArchUnitNET>, <https://www.nuget.org/packages/TngTech.ArchUnitNET.xUnitV3/0.13.4>, <https://github.com/jbevain/cecil>, <https://github.com/JamesNK/Newtonsoft.Json>, <https://github.com/JetBrains/JetBrains.Annotations>, <https://github.com/RobertGiesecke/CycleDetection>, <https://github.com/dotnet/maintenance-packages> |
| Microsoft.Testing.Platform 2.5.1, Microsoft.Testing.Platform.MSBuild 2.5.1, Microsoft.Testing.Extensions.TrxReport.Abstractions 2.5.1, Microsoft.Testing.Extensions.Telemetry 2.5.1, and transitive Microsoft.ApplicationInsights 2.23.0, Microsoft.Win32.Registry 5.0.0, and System.Diagnostics.EventLog 6.0.0 | The test runner `global.json` selects, brought in by xUnit v3 | MIT, declared as a license expression in each package's NuGet metadata | Allowed for test projects. Not redistributed. `Microsoft.Testing.Extensions.Telemetry` and the Application Insights client it carries are the platform's own usage telemetry, which is a data-processing concern rather than a licensing one. Microsoft documents it as usage data alone — hashed executable, framework, and extension names, test counts, platform and runtime versions, and a three-octet IP address — with no code scanning and no file contents; setting `TESTINGPLATFORM_TELEMETRY_OPTOUT` or `DOTNET_CLI_TELEMETRY_OPTOUT` to `1` disables it on a developer machine or a build agent. | <https://github.com/microsoft/testfx>, <https://www.nuget.org/packages/Microsoft.Testing.Platform/2.5.1>, <https://learn.microsoft.com/en-us/dotnet/core/testing/microsoft-testing-platform-telemetry> |
| NSubstitute 6.2.0 and transitive Castle.Core 5.1.1 | Test doubles for the unit suites, and the one decorator the integration suite needs over a real IMAP client to hide a server capability; plus the dynamic-proxy library behind both | BSD-3-Clause for NSubstitute and Apache-2.0 for Castle.Core, both declared as license expressions in the NuGet package metadata | Allowed for test projects. Not redistributed, so neither notice travels with an artifact. | <https://github.com/nsubstitute/NSubstitute/blob/master/LICENSE.txt>, <https://github.com/castleproject/Core>, <https://www.nuget.org/packages/Castle.Core/5.1.1> |
| CsCheck 4.9.1 | The generator behind the property tests, which state an invariant over generated inputs instead of over a chosen one: the shared keyset cursor codec, the chunking boundaries, the daily recurrence around a clock change, and the redaction guarantee. Referenced by `backend/tests/Application.UnitTests`, `backend/tests/AI.UnitTests`, and `backend/tests/SharedSources.UnitTests`. Each property test class builds its generators against the `Gen` API directly and samples them only through `PropertyCheck` in `backend/tests/shared`, which fixes the thread count and the reproduction contract; `PropertyCheckTests` calls the sampler itself, which is the one exception `backend/tests/AGENTS.md` names, because the contract it asserts is that sampler's own. | Apache-2.0, declared as a license expression in the NuGet package metadata and confirmed against the upstream repository | Allowed for test projects. Not redistributed, so no notice travels with an artifact. It carries no dependency of its own on its `net8.0` target and couples to no test framework, so it adds one assembly to three unit-test projects and changes nothing about how the suite is discovered or run. | <https://github.com/AnthonyLloyd/CsCheck>, <https://www.nuget.org/packages/CsCheck/4.9.1> |
| Microsoft.Extensions.TimeProvider.Testing 10.10.0 | Deterministic `TimeProvider` doubles, which the repository requires instead of substituting the abstraction | MIT, declared as a license expression in the NuGet package metadata | Allowed for test projects. Not redistributed. | <https://www.nuget.org/packages/Microsoft.Extensions.TimeProvider.Testing/10.10.0>, <https://github.com/dotnet/extensions> |
| coverlet.MTP 10.1.0 and dotnet-reportgenerator-globaltool 5.5.11 | Microsoft Testing Platform coverage collection, and deterministic aggregation and report generation for both the 85% whole-code pull-request gate and the separate, unenforced integration-suite report over the classes marked `[RequiresIntegrationCoverage]` | MIT for Coverlet and Apache-2.0 for ReportGenerator, both declared as license expressions in the NuGet package metadata | Allowed as development-only tooling. Not redistributed. ReportGenerator is pinned in `.config/dotnet-tools.json` and restored by `dotnet tool restore`. | <https://www.nuget.org/packages/coverlet.MTP/10.1.0>, <https://github.com/coverlet-coverage/coverlet>, <https://www.nuget.org/packages/dotnet-reportgenerator-globaltool/5.5.11>, <https://github.com/danielpalme/ReportGenerator> |
| Aspire.Hosting.Testing 13.6.1 | The testing harness the integration suite starts the repository's own app model with, so the orchestration under test is the one a developer runs rather than a second container topology maintained beside it. Referenced by `backend/tests/IntegrationTests` only. | MIT, declared as a license expression in the NuGet package metadata and in the upstream repository | Allowed as a test-only orchestration package. Not redistributed. | <https://www.nuget.org/packages/Aspire.Hosting.Testing/13.6.1>, <https://github.com/microsoft/aspire> |
| Microsoft.AspNetCore.SignalR.Client 10.0.13 and the transitive Microsoft.AspNetCore.SignalR.Client.Core, Microsoft.AspNetCore.SignalR.Common, Microsoft.AspNetCore.SignalR.Protocols.Json, Microsoft.AspNetCore.Http.Connections.Client, Microsoft.AspNetCore.Http.Connections.Common, Microsoft.AspNetCore.Connections.Abstractions, and Microsoft.Extensions.Features, all at 10.0.13 | The hub connection the integration suite proves the signal backplane with, referenced by `backend/tests/IntegrationTests` alone. Nothing else in the service stack speaks the hub protocol: what the claim needs is a WebSocket standing open against one replica while another raises a signal, and a request feature can carry a route and a credential but not a connection. The client the product ships is `@microsoft/signalr`, recorded among the redistributed rows above; this one reaches no artifact. | MIT, declared as a license expression in each package's NuGet metadata | Allowed for test projects. Not redistributed. | <https://github.com/dotnet/aspnetcore>, <https://www.nuget.org/packages/Microsoft.AspNetCore.SignalR.Client/10.0.13> |
| Microsoft.Extensions.Hosting 10.0.12 | Referenced by `backend/tests/IntegrationTests` alone, and the full hosting package rather than the abstractions the production projects use: a test composes a real host so that infrastructure's hosted lifecycle events run | MIT, declared as a license expression in the NuGet package metadata | Allowed for test projects. Not redistributed; the host itself takes hosting from the shared framework. | <https://github.com/dotnet/dotnet>, <https://www.nuget.org/packages/Microsoft.Extensions.Hosting/10.0.12> |
| Microsoft.Extensions.Configuration 10.0.12, Microsoft.Extensions.Configuration.Abstractions 10.0.12, Microsoft.Extensions.Configuration.FileExtensions 10.0.12, Microsoft.Extensions.Configuration.Json 10.0.12, Microsoft.Extensions.DependencyInjection 10.0.12, Microsoft.Extensions.DependencyInjection.Abstractions 10.0.13, Microsoft.Extensions.Diagnostics 10.0.12, Microsoft.Extensions.Features 10.0.12, Microsoft.Extensions.FileProviders.Abstractions 10.0.12, Microsoft.Extensions.FileProviders.Physical 10.0.12, Microsoft.Extensions.FileSystemGlobbing 10.0.12, Microsoft.Extensions.Http 10.0.12, Microsoft.Extensions.Logging.Configuration 10.0.12, Microsoft.Extensions.ObjectPool 10.0.12, Microsoft.Extensions.Primitives 10.0.13, and Microsoft.Bcl.AsyncInterfaces 6.0.0 | The framework libraries a project that is not a web application resolves as ordinary packages. Nothing prunes what the ASP.NET Core shared framework would otherwise supply, which is why these appear in the test locks and nowhere in the host's own graph. Where two locks resolve one of them at two versions, the version named is the higher: `Microsoft.Extensions.DependencyInjection.Abstractions` and `Microsoft.Extensions.Primitives` are 10.0.13 in the locks where `Microsoft.Extensions.AI` or the 10.0.13 `Microsoft.Extensions.Logging.Abstractions` pin raises them, and lower in the rest. Three of them — `Microsoft.Extensions.Diagnostics`, `Microsoft.Extensions.Http`, and `Microsoft.Extensions.ObjectPool` — are also in `backend/src/Infrastructure`'s lock, reached through `Microsoft.Extensions.Http.Resilience`, and that changes nothing about what ships: the host is the web application, its own lock lists none of the three, and the shared framework supplies them to the published artifact. | MIT, declared as a license expression in each package's NuGet metadata | Allowed. Not redistributed: every copy is either a test project's or pruned at publish in favour of the shared framework. | <https://github.com/dotnet/dotnet>, <https://github.com/dotnet/runtime> |
| `vitest` 5.0.4, `@vitest/coverage-v8` 5.0.4, `jsdom` 30.1.2, `@testing-library/react` 16.3.3, and `@testing-library/dom` 10.4.2 | The client's suite, which `pnpm test` is the whole of. Vitest is the runner and shares the workspace's own resolver and TypeScript configuration rather than a second one; its v8 coverage provider measures that suite on every run, reading the counters the runtime already keeps and remapping them through the two istanbul libraries Vitest publishes under its own scope, which hold the coverage map and the reporters that print it; jsdom is the document `frontend/src/Client.App`'s tests render into, and `frontend/src/Client.Backend`'s run without it; React Testing Library queries that document the way a person reads a screen. The provider is pinned to the runner's own version, because Vitest publishes the two in lockstep. Pinned in `frontend/package.json` and `frontend/src/Client.App/package.json` at exact versions, with `frontend/pnpm-lock.yaml` fixing the closure they resolve to. | MIT for all five, declared as a license expression in each package's npm metadata. Their closure is permissive throughout and adds no term the client did not already carry: Apache-2.0 for `aria-query` 5.3.0, `expect-type` 1.4.0, and `xml-name-validator` 5.0.0; BSD-2-Clause for `entities` 8.0.0 and `webidl-conversions` 8.0.1; BSD-3-Clause for `tough-cookie` 6.0.2; ISC for `saxes` 6.0.0; BlueOak-1.0.0 for `lru-cache` 11.5.3; and CC0-1.0 for `mdn-data` 2.27.1, which is the browser-compatibility **data** jsdom's CSS parsing reads, dedicated to the public domain and redistributed nowhere. **Two packages in it declare one term and ship another**: `@vitest/istanbul-lib-coverage` 1.0.2 and `@vitest/istanbul-lib-report` 1.0.2, the libraries the coverage provider remaps through, are Vitest's own continuation of `istanbul-lib-coverage`, `istanbul-lib-report`, and `istanbul-reports`, and each names MIT in its npm metadata — which is what the census counts them under — while the `LICENSE` file it publishes is the upstream's three-clause BSD text. They are read here as BSD-3-Clause, the text being what a licensor granted and the one of the two that asks for more; both are permissive, so the reading changes nothing this row decides. The runner also carries code it does not resolve: `vitest` inlines thirty-two packages into its own files and reproduces each one's terms in the `LICENSE.md` it ships — MIT for twenty-nine, BSD-3-Clause for `@sinonjs/commons` and `@sinonjs/fake-timers`, and ISC for `flatted` — so those appear in no lock file and in no census, and that file is their record. | Allowed as test-only tooling. None of it is referenced from the client's source, reaches the bundle, or ships in any artifact: `frontend/src/Client.App/dist/` is built by Vite from the source alone, and a `.test.ts` or `.test.tsx` file is never an entry of it. The coverage report the provider writes is an artifact of a test run under the ignored `artifacts/` directory rather than of a build, and nothing distributes it. jsdom is a document implemented in JavaScript rather than a browser, so nothing here downloads or embeds one; the tool that does download one is the row below, reviewed on its own terms. | <https://github.com/vitest-dev/vitest/blob/main/LICENSE>, <https://github.com/vitest-dev/vitest/blob/main/packages/coverage-v8/README.md>, <https://github.com/vitest-dev/istanbuljs/blob/main/packages/istanbul-lib-coverage/LICENSE>, <https://github.com/vitest-dev/istanbuljs/blob/main/packages/istanbul-lib-report/LICENSE>, <https://github.com/jsdom/jsdom/blob/main/LICENSE.txt>, <https://github.com/testing-library/react-testing-library/blob/main/LICENSE>, <https://github.com/testing-library/dom-testing-library/blob/main/LICENSE>, <https://github.com/salesforce/tough-cookie/blob/master/LICENSE>, <https://github.com/mdn/data/blob/main/LICENSE> |
| @playwright/test 1.64.0 and @playwright/cli 0.1.22, with the transitive playwright and playwright-core at 1.64.0 and at 1.64.0-alpha-1790635538000, and fsevents 2.3.3 | The client's browser tooling, and two distinct uses rather than one. `@playwright/test` is the runner behind `pnpm test:browser`, which builds the bundle and drives it in a real browser — the checks `pnpm test` structurally cannot make, and the ones `.github/workflows/build-test-frontend.yml` gates a pull request on. `@playwright/cli` is the agent-oriented command line: it holds a browser open across invocations so an agent or a person can drive the running client and read the requests it issued, which is a development instrument and not a suite. The two are separate packages and are not interchangeable. Both are pinned in `frontend/package.json` at exact versions, with `frontend/pnpm-lock.yaml` fixing the closure they resolve to; `fsevents` is an optional macOS-only file watcher that resolves on no other platform. | Apache-2.0 for `@playwright/test`, `@playwright/cli`, and both versions of `playwright` and `playwright-core`, declared as a license expression in each package's npm metadata and carried as `LICENSE` in the upstream repository. `fsevents` is MIT. The closure adds no other package and no other term. | Allowed as development and test-only tooling. Nothing here is referenced from the client's source, reaches the bundle, or ships in any artifact. Two things are named rather than left to be discovered. **The browser is downloaded rather than packaged**: `playwright install chromium` fetches a Chromium build and a headless shell into `~/.cache/ms-playwright` on a developer machine or a pipeline runner, under Chromium's own BSD-3-Clause grant and the notices that travel inside that build; nothing in this repository vendors, modifies, or redistributes one, and no artifact MailFathom publishes contains a browser. Only the Chromium entry is installed, so the separately downloadable FFmpeg build Playwright uses for video capture is never fetched — which is also why the browser suite records no video. **A prerelease resolves in the closure**: `@playwright/cli` is a `0.x` package and depends on `playwright` and `playwright-core` at `1.64.0-alpha-1790635538000`, so an alpha of the same Apache-2.0 project sits beside the stable 1.64.0 the runner takes. The terms are identical and the exposure is bounded by the package being a development instrument no build reads and no artifact carries. | <https://github.com/microsoft/playwright/blob/main/LICENSE>, <https://www.npmjs.com/package/@playwright/test/v/1.64.0>, <https://www.npmjs.com/package/@playwright/cli/v/0.1.22>, <https://chromium.googlesource.com/chromium/src/+/main/LICENSE>, <https://github.com/fsevents/fsevents/blob/master/LICENSE> |
| tauri-driver 2.0.6 | The desktop suite's driver, and the one component of it that is neither a pinned package nor a file in this repository. `pnpm test:desktop` builds the desktop shell and drives its WebView over the W3C WebDriver protocol, and this is what answers that protocol: it translates the session the suite opens into one the platform's native driver understands — `WebKitWebDriver` on Linux, Microsoft Edge Driver on Windows — and launches the shell binary the session names. It is installed by `cargo install tauri-driver --version 2.0.6 --locked`, which the `Drive the desktop head` job of `.github/workflows/build-test-frontend.yml` runs and which a developer runs once, so it is pinned at that command and in that workflow rather than in a manifest — it is a binary a machine holds, like the browser Playwright downloads in the row above, not a dependency either lock file resolves. Two system components are installed beside it on a Linux runner and are named here rather than left to be found: `webkit2gtk-driver`, which is the WebDriver binary of the same WebKitGTK already recorded in *Called on the machine an artifact runs on*, and `xvfb`, X.Org's virtual framebuffer, which is what gives the shell's window a display to open on. | `Apache-2.0 OR MIT`, declared as a license expression in the crate's own published metadata and carried as `LICENSE-APACHE-2.0`, `LICENSE-MIT`, and `LICENSE.spdx` in the crate's own directory of the upstream repository as well as at its root. Taken under MIT, the same way the Tauri crates the shell links are. | Allowed as test-only tooling. Not redistributed, and nothing about it reaches the bundle, the image, the chart, or either desktop bundle: it is a separate process that starts the application under test and exits with the run. It moves no census in *The client's three dependency closures*, because it is in none of them — it is absent from `frontend/pnpm-lock.yaml` and from `frontend/src-tauri/Cargo.lock` alike, which is the whole reason it needs a row of its own rather than a line in the crate closure above. `scripts/update-dependencies.sh` does not read it: the families it surveys are `nuget`, `npm`, `crates`, `tools`, `sdk`, `actions`, and `images`, and a version written into a `cargo install` line in a workflow is none of them. So it is surveyed by hand against crates.io, in the dependency change that moves it, and the two places it is written — the workflow step and `frontend/tests/AGENTS.md` — move with it. `xvfb` and `webkit2gtk-driver` are the runner's own distribution packages, executed and never distributed, and WebKitGTK's LGPL reading in *Called on the machine an artifact runs on* is unchanged by a driver binary being run beside it. | <https://crates.io/crates/tauri-driver/2.0.6>, <https://github.com/tauri-apps/tauri/tree/dev/crates/tauri-driver>, <https://webkit.org/licensing-webkit/>, <https://gitlab.freedesktop.org/xorg/xserver> |
| MailKit 4.18.1 and MimeKit 4.18.1 | Referenced directly by `backend/tests/IntegrationTests` as well, which seeds the orchestrated mailbox over SMTP and reads remote flag state back over its own IMAP connections, so an observation reports the server's answer rather than the adapter's belief about it, and by `backend/tests/SyntheticMail.UnitTests`, which substitutes MailKit's own published `ISmtpClient`, `IImapClient`, and `IMailFolder` to assert which socket option a submission and a mailbox session open with, that a submission states its envelope explicitly, and that nothing the mailbox session asks for could set `\Seen` | Recorded with the redistributed entries above | Allowed; the same decision as the redistributed entries. | See the redistributed entries. |

## Local orchestration and container images

Two kinds of thing, kept in one section because both are container images and their obligations are read together.

Most are started by the Aspire AppHost on a developer machine or by the integration-test script, and are neither derived from nor published by this repository. The two `mcr.microsoft.com/dotnet` rows are different: `deploy/docker/Dockerfile` builds MailFathom's own image on them, so the runtime base is redistributed inside every image a deployment runs, on the release channel and the nightly one alike. The obligation attaches to the derived image rather than to the registry it is pushed to, which is why it is recorded here rather than beside a publication step.

| Component and exact version | Use in this repository | License terms verified | Compatibility decision and obligations | Sources reviewed |
|---|---|---|---|---|
| Aspire.Hosting.AppHost 13.6.1, Aspire.AppHost.Sdk 13.6.1, Aspire.Hosting.PostgreSQL 13.6.1, and the runtime-identifier-specific Aspire.Dashboard.Sdk and Aspire.Hosting.Orchestration packages at 13.6.1 that the SDK selects | Development-time local orchestration of MailFathom and PostgreSQL. Those two runtime-identifier packages are why `AppHost` and `IntegrationTests` carry no lock file: the SDK chooses them from the restoring machine's runtime identifier. They are also where the dashboard and the orchestrator a run starts come from: `AppHost.csproj` leaves `AspireUseCliBundle` off, so neither is taken from the bundle of whichever Aspire CLI a machine has installed, and what runs is what this row reviewed. | MIT, declared as a license expression in each package's NuGet metadata and in the upstream repository | Allowed as development-time orchestration packages. Not redistributed. | <https://www.nuget.org/packages/Aspire.Hosting.AppHost/13.6.1>, <https://www.nuget.org/packages/Aspire.AppHost.Sdk/13.6.1>, <https://www.nuget.org/packages/Aspire.Hosting.PostgreSQL/13.6.1>, <https://github.com/microsoft/aspire> |
| Aspire.Hosting.EntityFrameworkCore 13.6.1-preview.1.26506.6 | The EF Core migration resource in the AppHost. It runs `dotnet-ef` against the orchestrated PostgreSQL resource and exposes the Add Migration, Update Database, Drop Database, and Reset Database commands, and its `PublishAsMigrationScript` declaration is what `aspire publish` reads to write the schema artifact a release attaches. Referenced by `backend/src/AppHost` only. | MIT, declared as a license expression in the NuGet package metadata and in the upstream repository | Allowed as a development-time and build-time orchestration package. This is the only prerelease pin in the repository: Aspire 13 removed the `aspire exec` command earlier versions offered, and its replacement ships no stable build. Generating the schema artifact through it widens what the pin reaches from the local orchestration to both publishing channels and to the `Apply migrations` job a pull request touching a migration runs, and no further: the package is in no deployed assembly, and what it produces is SQL generated from this repository's own migrations. Recheck the license and move to the stable release once one exists. | <https://www.nuget.org/packages/Aspire.Hosting.EntityFrameworkCore/13.6.1-preview.1.26506.6>, <https://github.com/microsoft/aspire> |
| Aspire.Hosting.Valkey 13.6.1 | The RESP resource the AppHost starts for the optional signal backplane, referenced by `backend/src/AppHost` only. It is off in an ordinary developer run and is added under two switches: `Backplane:Enabled`, which also starts a second MailFathom host resource so a developer can watch a signal cross between two replicas, and the integration-testing topology, where the suite proves that crossing. Deploying a RESP server is no part of this package's job: what the Helm chart under `deploy/helm/`, the Compose file under `deploy/compose/`, and the optional unit source under `deploy/quadlet/` render when a deployment asks for a backplane is the Valkey container image recorded below, each from its own asset, and nothing of this package reaches any artifact. It replaced `Aspire.Hosting.Garnet` at the same version when the deployed server became Valkey; the two are siblings in the same Aspire release and neither carries the server itself. | MIT, declared as a license expression in the NuGet package metadata and in the upstream repository | Allowed as a development-time orchestration package. Not redistributed. | <https://www.nuget.org/packages/Aspire.Hosting.Valkey/13.6.1>, <https://github.com/microsoft/aspire> |
| PostgreSQL server | Database server for metadata, raw MIME content, full-text search, synchronization state, chunks, and embeddings | PostgreSQL License, described by PostgreSQL as liberal and similar to BSD or MIT; the project FAQ states there is no fee even for commercial software products | Allowed. Permissive database license supports commercial and open-source deployment. A bundled server image or installer carries its own packaging obligations, which issue #123 must verify for whatever base image it selects. | <https://www.postgresql.org/about/licence/>, <https://www.postgresql.org/about/press/faq/> |
| pgvector PostgreSQL extension and the `pgvector/pgvector:0.8.7-pg18` container image | Vector search extension, and the PostgreSQL image used in seven places that deliberately share one pin: the development-time database the AppHost starts, the database service of the Docker Compose deployment in `deploy/compose/`, the database container of the Podman Quadlet deployment in `deploy/quadlet/`, the PostgreSQL StatefulSet the Helm chart in `deploy/helm/` deploys unless a deployment points it at its own server, the throwaway retrieval database the `AI evaluations` workflow starts when a dispatch asks for it, which reads its pin out of the Compose file rather than holding one of its own, the throwaway database the `Apply migrations` job applies the schema artifact to, which reads its pin out of the app model, and the throwaway database `scripts/compare-migration-squash.sh` proves a release's migration fold against, which reads it the same way. No MailFathom image derives from it, and neither the chart nor the unit sources bundle it — the image is pulled from its own registry by the cluster or by the host running the unit. | The extension's upstream `LICENSE` is the PostgreSQL License verbatim, carrying the PostgreSQL Global Development Group and Regents of the University of California copyright lines. The GitHub license API reports `NOASSERTION` for it, which reflects those added copyright lines rather than different terms. The image combines PostgreSQL and pgvector under permissive PostgreSQL-family licensing. | Allowed for local development orchestration, for the deployment assets, and as test infrastructure. It is run rather than redistributed; preserve the PostgreSQL and pgvector notices if a derived image is ever distributed. | <https://github.com/pgvector/pgvector/blob/master/LICENSE>, <https://hub.docker.com/r/pgvector/pgvector/tags?name=0.8.7-pg18> |
| `mcr.microsoft.com/dotnet/aspnet:10.0.13-noble-chiseled-extra` container image | The runtime base of `deploy/docker/Dockerfile`, and therefore what a deployed MailFathom actually runs on. It is redistributed inside every image built here. `-extra` is the variant carrying ICU and tzdata, which MailFathom needs for internationalized headers, case folding, and time zones and which the plain chiseled image omits; chiseled means the image carries no shell and no package manager. | MIT for the image definitions and the .NET components in them, declared in `dotnet/dotnet-docker` and confirmed through the GitHub license API. The image additionally carries Ubuntu 24.04 (Noble) operating-system packages under their own upstream licenses, which Microsoft documents as governing those components; the chiseled variant reduces that set to the minimum the runtime needs. | Allowed. Permissive licensing supports commercial closed-source distribution and open-source publication. Preserve the .NET and operating-system notices when distributing a derived image; both ship inside the image at the paths Microsoft documents. The tag is an explicit patch version rather than a floating `10.0`, so a rebuild resolves what the change was reviewed against. | <https://github.com/dotnet/dotnet-docker/blob/main/LICENSE>, <https://hub.docker.com/_/microsoft-dotnet-aspnet>, <https://learn.microsoft.com/en-us/dotnet/core/docker/container-images> |
| `mcr.microsoft.com/dotnet/sdk:10.0.401-noble` container image | The build base of `deploy/docker/Dockerfile`: it restores and publishes the application and is then discarded. Nothing from that stage reaches the runtime image. | MIT for the image definitions and the .NET components in them, declared in `dotnet/dotnet-docker` and confirmed through the GitHub license API, plus the Ubuntu 24.04 packages the image carries under their own licenses. | Allowed as a build-time base. It is not redistributed, because the stage it defines is discarded and only the publish output is copied out of it. | <https://github.com/dotnet/dotnet-docker/blob/main/LICENSE>, <https://hub.docker.com/_/microsoft-dotnet-sdk> |
| `docker.io/library/node:24.20.0-bookworm-slim` container image | The client stack's build base in `deploy/docker/Dockerfile`: it installs the pnpm workspace under `frontend/` and runs `pnpm build`, and is then discarded. What is copied out of it is the directory of static files that becomes the image's web root — no Node runtime, no npm, no pnpm, and no `node_modules` reaches the runtime image. It carries Node.js 24.20.0 with npm 11.19.0, and Yarn and Corepack the official image bundles and this build invokes neither of. The tag is an exact patch version on the active LTS line, which is the same pinning rule every image here follows; `frontend/package.json` states a floor rather than this version, because that field is what a developer's own machine is judged against and the two questions are separate. | MIT for the image definitions, from the `LICENSE` of `nodejs/docker-node` and confirmed through the GitHub license API, and MIT for Node.js itself, whose own `LICENSE` is that grant followed by the notices of what it embeds — OpenSSL, V8, libuv, zlib and the rest, each permissive. The image additionally carries Debian 12 (Bookworm) operating-system packages under their own upstream licences, and the two package managers it bundles under theirs: npm is Artistic-2.0 and Yarn is BSD-2-Clause. | Allowed as a build-time base. Nothing from it is redistributed, because the stage it defines is discarded and only the bundle it produced is copied out — which is what keeps the condition the web head was adopted under: no Node process joins any deployment shape. The licences of the two bundled package managers and of the Debian userland are therefore reviewed and owed a notice by no artifact this repository publishes. | <https://github.com/nodejs/docker-node/blob/main/LICENSE>, <https://github.com/nodejs/node/blob/main/LICENSE>, <https://hub.docker.com/_/node>, <https://github.com/nodejs/docker-node/blob/main/24/bookworm-slim/Dockerfile> |
| `ghcr.io/data-privacy-stack/presidio-analyzer:2.2.364` container image, and the spaCy `en_core_web_lg` English model it loads — version 3.8.0 at review time, resolved by spaCy's own compatibility table when the image was built | The personal-data analyzer MailFathom asks over HTTP when the `SensitiveContent:Pii` switch is on, and nothing when it is off. The same pin appears in four places deliberately: the analyzer Deployment the Helm chart renders only when `personalDataScanning.enabled` is set, the profile-gated `presidio-analyzer` service of the Compose deployment, the optional `mailfathom-presidio.container` unit source of the Quadlet deployment, and the resource the AppHost starts under the integration-testing topology so the suite exercises the image a deployment pulls. Detection beyond fixed-format identifiers needs a language model, and per issue #597 MailFathom loads none into its own process — which is what makes this a container beside the service rather than a package in the graph. No MailFathom image derives from it and no deployment asset bundles it: the image is pulled from its own registry by the cluster, the host, or the test run. | MIT for Presidio, from the `LICENSE` at tag `2.2.364` of the upstream repository under `Copyright (c) Presidio Contributors.`, and confirmed through the GitHub license API. The repository is `data-privacy-stack/presidio`, which is where `microsoft/presidio` moved; the image is published to GHCR from there, and the MCR images the older documentation names are no longer updated. Its `NOTICE` carries the third-party notices Presidio itself incorporates. The model is separate and is MIT as well, declared as `"license": "MIT"` in its own metadata in `explosion/spacy-models`; the corpora it was trained on carry their own terms, which that metadata lists — OntoNotes 5 commercially licensed by Explosion, WordNet 3.0 under the WordNet license, the ClearNLP conversion cited for reference with no code packaged, and Explosion's vectors under CC0. | Allowed, and the acceptance question the adopting issue asks is answered explicitly: **the model carries no non-commercial and no field-of-use restriction**, so no owner approval was needed for it. Both are run rather than redistributed — nothing here is vendored, linked, or copied into an image, a chart, or a binary, and the training corpora are inside a model this repository does not ship. Preserve the Presidio and spaCy notices if a derived image is ever distributed. The tag is exact rather than `latest` in all four places, because moving the analyzer changes what is detected: every finding carries the analyzer profile it was produced under for that reason, and the chart's schema refuses `latest` outright. One further use is a copy rather than a call: `backend/tests/SyntheticMail.UnitTests/Generation/SensitiveDecoys/SensitiveDecoyCatalogTests.cs` restates six of this image's recogniser patterns to assert that the development tool fabricates identifiers the analyzer would accept, so MIT's notice condition applies to those expressions as it does to any copied source. No release has to discharge it, because the development tool those tests cover ships in no artifact. The check-digit arithmetic beside them is not taken from here — Luhn, the ISO 7064 remainder, the PESEL weighted sum, and the DEA rule are published specifications. | <https://github.com/data-privacy-stack/presidio/blob/2.2.364/LICENSE>, <https://github.com/data-privacy-stack/presidio/blob/2.2.364/NOTICE>, <https://github.com/data-privacy-stack/presidio/releases/tag/2.2.364>, <https://github.com/data-privacy-stack/presidio/blob/2.2.364/presidio-analyzer/presidio_analyzer/conf/default.yaml>, <https://github.com/explosion/spacy-models/blob/master/meta/en_core_web_lg-3.8.0.json>, <https://github.com/explosion/spaCy/blob/master/LICENSE> |
| `greenmail/standalone:2.1.14` container image | The IMAP and SMTP server the integration suite synchronizes against, started by the AppHost only under the integration-testing topology. It hands out a real per-folder UIDVALIDITY that changes when a folder is recreated, and implements UIDPLUS, IDLE, MOVE, QUOTA, and EXPUNGE, which is what makes the specification's UIDVALIDITY and expunge scenarios observable at all. The AppHost never starts it under a developer's ordinary orchestration, and `docs/operations/local-development.md` documents starting the same image by hand as the mailbox `backend/tools/SyntheticMail` fills, so nobody has to point a development run at a real account. | Apache-2.0, declared in the upstream repository and confirmed through the GitHub license API for `greenmail-mail-test/greenmail` | Allowed as a test-only container image. Preserve the Apache-2.0 notice if the image or a derived image is ever distributed, which this repository does not do. | <https://github.com/greenmail-mail-test/greenmail>, <https://github.com/greenmail-mail-test/greenmail/releases/tag/release-2.1.14>, <https://hub.docker.com/r/greenmail/standalone/tags?name=2.1.14> |
| `valkey/valkey` container image, at two references that are deliberately not one: the `9.1.2` tag named in `backend/src/AppHost/Program.cs`, and the digest `sha256:418652cfb58ef879d4978c33553735d7147016032d5aefaa14c828e611eb9dfd` every deployment asset names as `docker.io/valkey/valkey`, which is Valkey 9.1.2 | The RESP server MailFathom's optional signal backplane is carried over, in the places this repository names one. The AppHost starts it when a developer asks for a backplane and when the integration suite proves a signal crossing between two hosts; the Helm chart renders a StatefulSet and two Services for it when `signalBackplane.enabled` and `signalBackplane.valkey.deploy` are both set, and one further instance per `signalBackplane.valkey.replication.replicas` inside that same set; the Compose file carries a `valkey` service behind a profile of its own; and the Quadlet deployment carries `mailfathom-valkey.container`, optional by being left uninstalled. All of them are off in a default start, and a deployment pointing at an endpoint its operator already runs renders nothing in any of them. Every reference is written out rather than left to `Aspire.Hosting.Valkey`'s own default or to a chart default, which is what puts each of them in front of `scripts/update-dependencies.sh --only images` with every other container here. **Why the deployment digest and the AppHost tag are not the same string**, unlike the pgvector, Presidio, and Silo pins that each appear once across four assets: the AppHost's is a tag on a developer's machine, where following the patch line is the point, and a deployment asset is a deployment artifact, where a digest is the only reference a registry cannot repoint — and Valkey publishes its release tags beside moving `9`, `9.1`, and `latest` ones, so nothing here can be both. They name the same release today, and keeping them together is a dependency update rather than a change to what either asset does. No MailFathom image derives from it and nothing published here bundles it: each asset names a registry coordinate, and the pull is the operator's cluster, the operator's host, or the machine running the orchestration. Valkey is the RESP server this project maintains its assets and its pins against, which is what makes it the default both a developer and a deployment get — the section an operator writes takes any RESP endpoint, and Redis, Garnet, Valkey, or a managed equivalent all answer the `PUBLISH`, `SUBSCRIBE`, and `PSUBSCRIBE` the backplane uses. | BSD-3-Clause for Valkey itself, from the `COPYING` in `valkey-io/valkey`, confirmed through the GitHub license API. The image declares only `org.opencontainers.image.source` and no license label, so the terms were read from the repository rather than from the artifact. It is built on a Debian trixie base, whose userland is covered by the packages' own licenses, plus the `tzdata`, `libssl3t64`, and `tini` packages its Dockerfile installs — `tini` being MIT and the image's own entrypoint. The server itself is compiled in a build stage of that Dockerfile and copied in rather than installed from a distribution package, which is why `dpkg` knows nothing about it. | Allowed: the terms are permissive and the way it is used does not change between a developer's machine and a rendered deployment — run rather than redistributed, with nothing vendored, linked, or copied into an image, a chart, or a binary. Four properties of the image were measured rather than assumed before any asset rendered it, because the chart's pod meets the restricted Pod Security Standard and the other two run it with no capability and no way to gain a privilege: it carries an unprivileged `valkey` account at uid and gid 999; its `/data` is world-writable with the sticky bit, so an in-memory volume over it needs no ownership fix and no `fsGroup`; its entrypoint drops to that account only when it starts as root and otherwise execs directly, so a pod that states the account never runs a privileged step at all; and `protected-mode` is already `no` in the image's own defaults, so nothing has to be relaxed to let a replica in another pod connect. What travels over it is personal data in transit that rests nowhere, which is why the rendered pods carry no claim, the Compose service no volume, and the unit source neither — and why every asset passes `--save ""` explicitly, this server snapshotting on a schedule unless told not to. **How the password reaches the server differs by asset and the credential does not**: the chart expands it into `--requirepass` from a Secret the kubelet reads, and Compose and Quadlet start the server with `--aclfile` over a one-line ACL document, because neither runtime expands a variable into a container's arguments and a command-line password would otherwise sit in `docker inspect`, in `podman inspect`, and in the host's process list. | <https://github.com/valkey-io/valkey>, <https://github.com/valkey-io/valkey/blob/unstable/COPYING>, <https://github.com/valkey-io/valkey-container>, <https://hub.docker.com/r/valkey/valkey> |
| `docker.io/pgsty/silo:RELEASE.2026-09-16T00-00-00Z` container image | The S3-compatible endpoint MailFathom writes message content to when the object-storage backend is selected, and nothing when it is not. The same pin appears in four places deliberately: the object-store StatefulSet the Helm chart renders only when `contentStorage.objectStorage.deploy.enabled` is set, at one pod or at `contentStorage.objectStorage.deploy.replicas` of them where a deployment asked for a distributed pool, the profile-gated `silo` service of the Compose deployment, the optional `mailfathom-silo.container` unit source of the Quadlet deployment, and the resource the AppHost starts under the integration-testing topology so the suite exercises the server a deployment runs. Every one of those is off in a default rendering, and a deployment selecting the object backend may equally point at a bucket its operator rents; what the three deployment shapes add is somewhere to point when there is nothing. Silo is PGSTY's maintained fork of the open-source MinIO server, keeping one release line alive after upstream ended community distribution; it preserves the S3 API, the `MINIO_*` variables, the `minio_*` metrics, and the on-disk format. It is here because a mock accepts the requests it was written to accept: what a substitute proves is that MailFathom composed the request it meant to, and what a real implementation proves is that the requests MailFathom makes are ones an S3 server accepts — which is the claim `IEmailContentStore`'s object backend rests on against every vendor. The behaviour that is claimed was verified against this exact tag before it was adopted, with the same `AWSSDK.S3` version the adapter uses: path-style addressing against a custom endpoint; `x-amz-checksum-sha256` verified and echoed, and a mismatching one refused with `400 XAmzContentChecksumMismatch` rather than stored; `If-None-Match: *` refused with `412 PreconditionFailed` on a second write under a key already taken; an absent key answered with `404 NoSuchKey` on a read and accepted on a delete; `ListObjectsV2` paging under a prefix, reporting `IsTruncated` with a continuation token and answering an empty object list as absent rather than empty; `PutBucket` on a bucket already owned answered with `409 BucketAlreadyOwnedByYou`; and a 64 KiB payload byte-identical on the way back. No MailFathom image derives from it and no deployment asset bundles it: an asset names a registry coordinate, and the image is pulled from PGSTY's own registry by the cluster, the host, or the test run. **Every stored payload is written to it**, which is why each deployment asset keeps it inside the deployment's own trust boundary — no route out of the host or off the cluster, TLS on the one port anything reaches, its administrative console not served in any default rendering, and the bucket reached by an access key scoped to that bucket rather than by the server's root credential, which no asset ever puts on a path MailFathom's own process can read. | AGPL-3.0-or-later, from the `LICENSE` in `pgsty/silo`, confirmed through the GitHub license API as `agpl-3.0`, and declared by the image itself in `org.opencontainers.image.licenses`. The image carries the terms rather than leaving them to be looked up: `/licenses/LICENSE` is the AGPLv3 text, `/licenses/NOTICE` reproduces MinIO's own notice (© 2015–2025 MinIO, Inc.) beside PGSTY's modification notice (© 2025–2026) and its statement that the project is unaffiliated with and unendorsed by MinIO, Inc., and `/licenses/CREDITS` reproduces the licences of the Go modules linked into the binary. The base is Red Hat Universal Base Image 9 Micro (`ubi9/ubi-micro`, version 9.8), whose userland is covered by Red Hat's UBI end-user licence terms that the image's own `com.redhat.license_terms` label points at. | **Allowed under the owner's explicit approval, recorded on [issue #1131](https://github.com/fathomise/MailFathom/issues/1131) for the integration suite and extended on [issue #1132](https://github.com/fathomise/MailFathom/issues/1132) to the deployment assets**, which is what the acceptance policy above requires before an AGPL component is used at all. One reading covers both, because what the second change moved is who starts the process rather than what anybody distributes: Silo is a separate process reached over a documented network protocol, pulled from its own registry, and nothing here vendors it, links it statically or dynamically, copies it into an image, a chart, or a binary, or redistributes it in any form. A deployment asset that names it carries a registry coordinate and no part of the program — the chart, the Compose file, and the unit source are configuration text, and the pull is the operator's cluster or host talking to PGSTY. MailFathom neither modifies Silo nor conveys it nor offers it to anybody, so the AGPL's section 13 network clause, which binds whoever conveys or operates a *modified* program, has nothing here to attach to and no source offer is owed — by this project, and equally by an operator running the unmodified image these assets name. MailFathom's own distribution terms are untouched, which is the whole reason both approvals could be given: neither of the acceptance policy's two distribution models carries this component, because no artifact does. What would change that is deriving an image from it, vendoring it, or packaging the server inside something this project publishes, and none of those is done. The tag is exact rather than `latest`, like every image here, and it is one decision across the four places above. | <https://github.com/pgsty/silo>, <https://github.com/pgsty/silo/blob/main/LICENSE>, <https://github.com/pgsty/silo/releases/tag/RELEASE.2026-09-16T00-00-00Z>, <https://hub.docker.com/r/pgsty/silo>, <https://silo.pgsty.com/>, <https://www.gnu.org/licenses/agpl-3.0.html>, <https://catalog.redhat.com/software/containers/ubi9/ubi-micro/615bdf943f6014fa45ae1b58>, <https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI> |

## Continuous integration

Executed on GitHub-hosted runners. Nothing here is redistributed by this repository.

| Component and exact version | Use in this repository | License terms verified | Compatibility decision and obligations | Sources reviewed |
|---|---|---|---|---|
| actions/checkout v7, actions/setup-dotnet v6, actions/setup-node v7, actions/cache v6 — its `restore` and `save` actions, which the workflows and the composite actions under `.github/actions/` run in place of the combined one — actions/upload-artifact v7, and actions/download-artifact v8, each as a moving major tag in every workflow that runs it | The steps the `CI`, `Build, test, format, and migrations`, `Integration tests`, `Protected paths`, `Typo check`, `CodeQL`, `Release`, `Nightly`, `Publish container image`, `Publish Helm chart`, `Build the schema artifact`, and `Fathom review` workflows are built from: checking out the branch, installing the SDK pinned in `global.json`, installing the Node version `frontend/package.json` declares in `engines` for the client's browser suite, restoring the pnpm store, the Playwright browser, the Cargo and Gradle downloads, and apt's downloaded packages so a client change does not re-download them on every run, with only `main` saving them, publishing the coverage and test-result artifacts, carrying the release's schema artifact from the job that builds it to the job that attaches it, and — in `Fathom review` — carrying the collected change from the job that froze it to the reader jobs that read it and the readers' reports back to the job that judges them | MIT in each upstream repository, confirmed through the GitHub license API, all carrying `Copyright (c) 2018 GitHub, Inc. and contributors` | Allowed as continuous-integration tooling. The GitHub Actions services these steps call, including the artifact and cache stores, are governed by GitHub's terms rather than by this license and are not redistributable dependencies. A moving `v<major>` means the exact commit a run executes is chosen at run time, and that is the update mechanism rather than a gap in one: nothing in this repository proposes an action update, so the tag is how an upstream fix arrives at all, and a major — the change that can rename an input or drop a runner — arrives only when somebody moves the reference and this row with it. `checkout` v7 refuses to check out fork pull-request code from a `pull_request_target` or `workflow_run` workflow unless the step opts in; `Fathom review` is the only workflow here on either event and it checks out the base commit, so the refusal is a guard rather than a behaviour change | <https://github.com/actions/checkout/blob/main/LICENSE>, <https://github.com/actions/setup-dotnet/blob/main/LICENSE>, <https://github.com/actions/setup-node/blob/main/LICENSE>, <https://github.com/actions/cache/blob/main/LICENSE>, <https://github.com/actions/upload-artifact/blob/main/LICENSE>, <https://github.com/actions/download-artifact/blob/main/LICENSE> |
| actions/configure-pages v6, actions/upload-pages-artifact v5, and actions/deploy-pages v5, each as a moving major tag | The deployment job of the `Publish the documentation` workflow, which is the only place any of the three appears and which a pull request skips. `configure-pages` reads the repository's Pages configuration and exposes the site's own base URL, origin, and path; it enables nothing, because its `enablement` input defaults to false and turning it on would need a token the workflow does not hold, and this workflow reads none of its outputs either, since the site is composed before the deployment job runs. `upload-pages-artifact` archives that composed tree and uploads it under the artifact name `github-pages`, which it does by calling `actions/upload-artifact` pinned inside it by commit, so the action in the row above is reached transitively here as well as directly there. `deploy-pages` creates the Pages deployment from that artifact and waits for GitHub to report the site live | MIT in each upstream repository, confirmed through the GitHub license API. The notices differ from the row above and from one another: `Copyright (c) 2021 GitHub, Inc.` for `configure-pages` and `deploy-pages`, `Copyright (c) 2022 GitHub, Inc.` for `upload-pages-artifact`, and none of the three carries the `and contributors` the older actions do | Allowed as continuous-integration tooling. None is redistributed, and nothing from them reaches an image, a chart, or a binary. They are a row of their own rather than three more names in the row above because their exposure differs rather than their license: they run in the only job in this repository that holds `pages: write`, beside the `id-token: write` the deployment is claimed with, and what they produce is a deployment to GitHub's Pages service, which is governed by GitHub's terms rather than by this MIT grant and is recorded under hosted services below. What is submitted is the built documentation site — generated from `docs/` and the XML documentation comments in `backend/src/`, both already public here — together with the workflow's own OIDC identity; no credential travels with it, and nothing is written back to the repository. A moving `v<major>` means the exact commit a run executes is chosen at run time, and for these three a major is the change that can rename the artifact the deployment expects or alter what the deployment claims, so it arrives only when somebody moves the reference and this row with it | <https://github.com/actions/configure-pages/blob/main/LICENSE>, <https://github.com/actions/upload-pages-artifact/blob/main/LICENSE>, <https://github.com/actions/deploy-pages/blob/main/LICENSE> |
| github/codeql-action v4, released as v4.37.4, and the two things a run downloads through it: the CodeQL CLI, and the query packs and QL libraries built from `github/codeql`. The bundle current at review time is `codeql-bundle-v2.26.2` | The `CodeQL` workflow, which extracts a C# database while `dotnet build` runs under tracing, reads the client's TypeScript and the desktop shell's Rust without a build, and evaluates GitHub's security queries for each language against its database. This is the only analysis in the repository that follows a value across a project boundary; the build's own analyzers are per-file and per-method, and Trivy answers a different question about a different artifact | MIT for the action and for `github/codeql`, both confirmed through the GitHub license API, carrying `Copyright (c) 2020 GitHub` and `Copyright (c) 2019 GitHub` respectively. The CLI is separate and is **not** open source: `github/codeql-cli-binaries` carries the *GitHub CodeQL Terms and Conditions*, which the license API reports as `NOASSERTION`. Those terms permit performing analysis on an Open Source Codebase, and permit generating databases during automated analysis, CI, or CD where the codebase is hosted on GitHub.com; they prohibit use in connection with a codebase that is not an Open Source Codebase, and prohibit redistributing, sublicensing, or hosting the software for others | Allowed as continuous-integration tooling, with the CLI reviewed as the custom license the acceptance policy admits after review — the same treatment the Claude Code CLI gets, and for the same reason: it is executed on an ephemeral runner and discarded, nothing is linked into MailFathom, and no artifact carries it. One condition is worth stating because it is a condition on the project rather than on a file. The CLI's grant here rests on MailFathom being an AGPL-3.0-only codebase hosted publicly on GitHub.com; a closed-source commercial distribution built from this source is outside it, and running CodeQL against that would need a paid GitHub Code Security entitlement rather than this permission. That bears on how MailFathom may be analysed, never on how it may be distributed, and the acceptance policy's two distribution models are untouched by it. The alerts a run produces are stored by GitHub under its own terms | <https://github.com/github/codeql-action/blob/main/LICENSE>, <https://github.com/github/codeql-cli-binaries/blob/main/LICENSE.md>, <https://github.com/github/codeql/blob/main/LICENSE>, <https://github.com/github/codeql-action/releases/tag/v4.37.4> |
| docker/setup-buildx-action v4, as the moving `v4` tag in both workflows that run it | The manually dispatched `Container image` workflow, where it creates a Buildx builder able to produce a `linux/amd64` and `linux/arm64` build, and the `Publish container image` workflow, where the same builder produces the manifest list that is pushed | Apache-2.0, confirmed through the GitHub license API | Allowed as continuous-integration tooling. It is not redistributed. In the manually dispatched workflow it publishes nothing and needs no privilege beyond the read-only default; in the publishing workflow it runs in a job that holds `packages: write`, which is what makes the major this row records worth confirming when it moves. No QEMU action accompanies it: the two-architecture build executes nothing under emulation, so none is needed. | <https://github.com/docker/setup-buildx-action/blob/master/LICENSE> |
| docker/login-action v4 and docker/build-push-action v7, each as a moving major tag | The `Publish container image` workflow, and `docker/login-action` additionally in `Nightly` and `Publish Helm chart`. The first authenticates to GHCR with the workflow token and to Docker Hub with the `DOCKERHUB_TOKEN` secret; the second builds the image for gating, then builds and pushes the `linux/amd64` and `linux/arm64` manifest list, with every reference in both registries in one tag list, carrying its BuildKit-produced SBOM and provenance | Apache-2.0 in each upstream repository, confirmed through the GitHub license API | Allowed as continuous-integration tooling. Neither is redistributed. Both run in jobs that hold `packages: write` and a registry credential, which is where an action's behaviour matters most here and why the major each reference follows is confirmed against the upstream release notes when it moves rather than taken on the version number. One of the two registries they authenticate against is GitHub's and the other is Docker's, each governed by its own terms rather than by these licenses; both are recorded under hosted services below. | <https://github.com/docker/login-action/blob/master/LICENSE>, <https://github.com/docker/build-push-action/blob/master/LICENSE> |
| peter-evans/dockerhub-description v5, as a moving major tag | The release channel of the `Publish container image` workflow, where it replaces the Docker Hub repository page's overview with `deploy/docker/README.md` and its short description with the published image's own `org.opencontainers.image.description`. GHCR needs no equivalent: its package page renders the repository the image's `org.opencontainers.image.source` label names | MIT in the upstream repository, confirmed through the GitHub license API, carrying `Copyright (c) 2019 Peter Evans` | Allowed as continuous-integration tooling. It is not redistributed. It runs in the job holding the Docker Hub credential, which is the reason this row is re-read whenever its major moves. Docker publishes no first-party action for this, and this is the established one for it; the credential it uses is the same `DOCKERHUB_TOKEN` the push uses, whose delete scope this step is part of the reason for. What it sends is the repository's own README, which is public. | <https://github.com/peter-evans/dockerhub-description/blob/main/LICENSE>, <https://github.com/peter-evans/dockerhub-description/releases/tag/v5.0.0> |
| oras-project/setup-oras v2, as a moving major tag, and the ORAS CLI it installs at run time | The `Publish Helm chart` workflow, where ORAS reads the published chart's digest and pushes the Artifact Hub ownership-claim artifact under the reserved `artifacthub.io` tag in the chart's own OCI repository | Apache-2.0 for both the action and the `oras-project/oras` CLI it installs, confirmed through the GitHub license API | Allowed as continuous-integration tooling. Nothing from it is linked into MailFathom or redistributed: the CLI is installed on an ephemeral runner, run against this project's own registry repository, and discarded. The action is published by the ORAS project itself rather than by a third party republishing it, which is the provenance that justifies a dependency with a small star count; ORAS is a CNCF project. It runs in a job holding `packages: write`, which is what makes a move of its major worth reading upstream before it lands. | <https://github.com/oras-project/setup-oras/blob/main/LICENSE>, <https://github.com/oras-project/oras/blob/main/LICENSE>, <https://github.com/oras-project/setup-oras/releases/tag/v2.0.1> |
| aquasecurity/trivy-action v0.36.0, referenced as that exact tag because the project publishes no moving major tag — its `v0` line is exact versions only — and the Trivy scanner it installs at run time, whose version follows the action's own default | The vulnerability gate of the `Publish container image` workflow. It scans the built image before it is pushed; a `HIGH` or `CRITICAL` finding with a fix available refuses to publish a release and is reported without blocking on the nightly channel | Apache-2.0 for both the action and `aquasecurity/trivy` itself, confirmed through the GitHub license API. The vulnerability database the scanner downloads is published from `aquasecurity/trivy-db`, also Apache-2.0, and aggregates advisory data from upstream sources that carry their own terms | Allowed as continuous-integration tooling. Nothing from it is linked into MailFathom or redistributed: the scanner is installed on an ephemeral runner, run against one image, and discarded, and the database it fetches is read rather than shipped. The database is served from a container registry at run time, so the exact advisory data a scan uses is whatever is current when it executes; that is the point of the gate rather than a reproducibility problem, and no scan result is stored in this repository. | <https://github.com/aquasecurity/trivy-action/blob/master/LICENSE>, <https://github.com/aquasecurity/trivy/blob/main/LICENSE>, <https://github.com/aquasecurity/trivy-db/blob/main/LICENSE> |
| actions/attest-build-provenance v4, as a moving major tag | The `Publish container image` and `Publish Helm chart` workflows, where it signs a statement that a published digest was built by this workflow from a named commit. The image is attested once per repository name, because a subject is a name and a digest together and an operator verifies the registry they pulled from; both registries answer the OCI referrers API, so a release carries the attestation in each of them as a referrer. A nightly does not, because its versions are pruned and the referrer would outlive its subject | MIT in the upstream repository, confirmed through the GitHub license API, carrying `Copyright (c) 2024 GitHub, Inc. and contributors` | Allowed as continuous-integration tooling. It is not redistributed. It holds `id-token: write` and `attestations: write` and submits the image digest and the workflow's own identity to GitHub's attestation service and the Sigstore transparency log recorded below; it submits no repository content, and the artifact it describes is MailFathom's own. | <https://github.com/actions/attest-build-provenance/blob/main/LICENSE> |
| crate-ci/typos v1.48.0, referenced as that exact tag rather than as the moving `v1`, and the `typos` v1.48.0 binary its entrypoint downloads at run time | The checking step of the `Typo check` workflow, which reads the files a pull request changes and reports each word its corrections dictionary recognizes as a misspelling, against the accepted vocabulary `.config/typos.toml` records and the step names through the action's `config` input | `MIT OR Apache-2.0`, declared as the license expression in the upstream `Cargo.toml` and carried as both `LICENSE-MIT` and `LICENSE-APACHE` in the repository | Allowed as continuous-integration tooling. Nothing from it is linked into MailFathom or redistributed: the binary is downloaded onto an ephemeral runner, run over a list of paths, and discarded with the runner. The step holds nothing beyond the read-only default token and reaches no service other than the GitHub release it fetches. One version covers both halves, because the action's entrypoint hard-codes the release it downloads — which makes the reference decide the dictionary a run is judged against, where the moving `v1` could turn a green pull request red with no commit on either side. | <https://github.com/crate-ci/typos/blob/master/LICENSE-MIT>, <https://github.com/crate-ci/typos/blob/master/LICENSE-APACHE>, <https://github.com/crate-ci/typos/releases/tag/v1.48.0> |
| ShellCheck 0.11.0, actionlint 1.7.12, and zizmor 1.32.0, each the Linux x86_64 binary of its own GitHub release, pinned by version and by the SHA-256 of the release archive in `scripts/lint-shell-and-workflows.sh` | The `Shell and workflow lint` job of `Repository contracts`, and the same script on a developer machine. The script downloads each archive, refuses one whose checksum differs from the pinned one, and keeps the binaries in a cache directory outside the repository. ShellCheck reads every tracked `*.sh` file; actionlint reads every workflow and hands the shell in each `run:` block to that same ShellCheck binary; zizmor reads the workflows and the composite actions for template injection, credentials, and triggers, offline. Their configuration is `.github/actionlint.yaml` and `.github/zizmor.yml` | GPL-3.0 for ShellCheck, from the `LICENSE` in `koalaman/shellcheck`, carried as `LICENSE.txt` in its release archive, confirmed through the GitHub license API as `GPL-3.0`. MIT for actionlint, from the `LICENSE.txt` in `rhysd/actionlint` and in its release archive, and MIT for zizmor, from the `LICENSE` in `zizmorcore/zizmor`, each confirmed through the GitHub license API | Allowed as continuous-integration and development tooling, **ShellCheck under the owner's explicit approval, recorded on [issue #3042](https://github.com/fathomise/MailFathom/issues/3042)**, which the acceptance policy above requires before a GPL component is used at all. Each runs as a separate program over this repository's files and reports what it finds; nothing from any of them is linked into MailFathom, vendored, copied into an image, a chart, or a binary, or redistributed, so ShellCheck's copyleft attaches to nothing this project conveys. The binaries are downloaded onto an ephemeral runner or into a developer's cache, run, and discarded with it. `scripts/update-dependencies.sh --only linters` reads each pin against its upstream and never rewrites one, because a new version needs the new archive's checksum beside it | <https://github.com/koalaman/shellcheck/blob/master/LICENSE>, <https://github.com/koalaman/shellcheck/releases/tag/v0.11.0>, <https://github.com/rhysd/actionlint/blob/main/LICENSE.txt>, <https://github.com/rhysd/actionlint/releases/tag/v1.7.12>, <https://github.com/zizmorcore/zizmor/blob/main/LICENSE>, <https://github.com/zizmorcore/zizmor/releases/tag/v1.32.0> |
| vegardit/fast-apt-mirror.sh 1.4.5, referenced as that exact tag rather than as the moving `v1` | The `Choose a fast apt mirror` step that precedes every apt install: the desktop toolchain installs of `Build the desktop client`'s Linux leg and `Drive the desktop head`, and the Playwright `install-deps` installs of the browser's system libraries in `Build the client and drive it in a browser` and `End-to-end client`, each of which runs through `.github/actions/cached-apt-install`. It reads the Ubuntu mirror list `mirrors.ubuntu.com` returns for the runner's location, health-checks and speed-tests a sample of those mirrors, writes the fastest over the first line of the runner's `/etc/apt/apt-mirrors.txt`, and refreshes the package lists, with the author's default inputs | Apache-2.0, carried as `LICENSE.txt` in the upstream repository and declared in the SPDX header of `action.yml` and `fast-apt-mirror.sh`, `© Vegard IT GmbH and contributors` | Allowed as continuous-integration tooling. Nothing from it is linked into MailFathom or redistributed: it is a composite action running one Bash script on an ephemeral runner. It runs with `sudo` and rewrites the runner's apt sources, which is why the reference is an exact tag — the tag decides which code holds root on the runner, where the moving `v1` could change it with no commit here. Which mirror it picks cannot change what is installed, because apt verifies every archive against Ubuntu's signed `InRelease`; the step holds nothing beyond the read-only default token and reaches only the mirror list and the mirrors themselves. | <https://github.com/vegardit/fast-apt-mirror.sh/blob/v1/LICENSE.txt>, <https://github.com/vegardit/fast-apt-mirror.sh/releases/tag/1.4.5> |
| dorny/paths-filter v4 | The `Detect changes` job of the `CI` workflow and the `Detect changed languages` job of the `CodeQL` workflow, which decide whether the build and formatting jobs have anything to do and which languages a pull request's code scanning analyzes. It reads a pull request's changed files through the GitHub REST API, and a push to `main` with `git diff` against the commit the branch carried before the merge | MIT in the upstream repository, confirmed through the GitHub license API; `v4` is the moving major tag, released as `v4.0.2` | Allowed as continuous-integration tooling. The action runs with a read-only `GITHUB_TOKEN` scoped to `contents: read` and `pull-requests: read`, sends nothing outside the GitHub API, and is not a redistributable dependency. | <https://github.com/dorny/paths-filter/blob/master/LICENSE>, <https://github.com/dorny/paths-filter/releases/tag/v4.0.2> |
| anthropics/claude-code-action v1, released as v1.0.183, and the tooling it installs at run time: oven-sh/setup-bun pinned to commit `0c5077e` (v2.2.0), Bun 1.3.14, and the Claude Code CLI `@anthropic-ai/claude-code`, whose version is whatever npm serves when a run executes and was 2.1.220 at review time; and the same repository's `base-action` at the same `v1` tag, which installs Node with actions/setup-node pinned to commit `48b55a0` (v6.4.0), Bun as above, and the CLI at the version it pins, 2.1.302 at review time | The model steps of the `Fathom review` workflow, which runs on a published pull request and on a maintainer's request: one per reader job, each reading one group of the change, and one in the job that judges what they return. Each installs the CLI on its own runner and runs its prompt against the base commit and the change collected as data; the workflow's own step, not the action, submits the review. And the one model step of the `Duplicate definitions` workflow, which runs `base-action` because the action itself refuses a `merge_group` event, on a merge-queue entry whose two sides both add a definition | MIT for the action itself, `Copyright (c) 2025 Anthropic, PBC`, and MIT for `setup-bun`, both confirmed through the GitHub license API. Bun is MIT in a `LICENSE.md` that also reproduces the notices of the components it bundles, which is why the license API reports `NOASSERTION` for it. The Claude Code CLI is not open source: its npm metadata declares `SEE LICENSE IN README.md`, and that README points at Anthropic's Commercial Terms of Service and Privacy Policy | Allowed as continuous-integration tooling, with the CLI reviewed as the custom commercial license the acceptance policy admits after review. Nothing here is linked into MailFathom or redistributed by it; the CLI is installed on an ephemeral runner, executed, and discarded. The CLI runs with no shell, no editor, and no network tool, and the branch under review is never checked out, so nothing from a pull request is executed on the runner. `Duplicate definitions` checks out the merge group's combined tree and runs its prefilter from it, which is the trust a `merge_group` run already gives that tree's copy of the workflow file itself; everything under `.github/` is a protected path refused from any author but the owner, so a queued change to the prefilter, its prompt, or its schema is the owner's. Its model step denies every tool, so the session reads only the prompt it is handed. `v1` is a moving major tag and the CLI is fetched unpinned, so the exact revisions a run executes are chosen at run time; the hosted service they call is recorded separately below | <https://github.com/anthropics/claude-code-action/blob/main/LICENSE>, <https://github.com/anthropics/claude-code-action/releases/tag/v1.0.183>, <https://github.com/oven-sh/setup-bun/blob/main/LICENSE>, <https://github.com/oven-sh/bun/blob/main/LICENSE.md>, <https://registry.npmjs.org/@anthropic-ai/claude-code>, <https://github.com/anthropics/claude-code/blob/main/README.md> |
| actions/create-github-app-token v3, released as v3.2.0 | The first step of two workflows and a step of two board jobs. In `Fathom review`'s reviewing job it exchanges the `Fathom reviewer` GitHub App's client id and private key for an installation token scoped to this repository, which the submission step publishes the review with instead of the workflow token; in `Contributor licence` it does the same for the `Fathom license` App, whose token publishes the `license/cla` status, asks for the agreement, and appends to the signature register; in `Fathom review`'s `announce` job and `Apply pull request rules`' board job it does the same for the `Fathomise projects` App, whose token writes the roadmap board's `Status` | MIT in the upstream repository, confirmed through the GitHub license API, carrying `Copyright (c) 2023 Gregor Martynus` and `Copyright (c) 2023 Parker Brown` | Allowed as continuous-integration tooling. It is not redistributed and reaches no service other than GitHub's own API, which is governed by GitHub's terms rather than by this license. All three Apps are the owner's and each carries the narrowest permission set its workflow needs — `Pull requests: Read and write` for the reviewer, that plus `Commit statuses: Read and write` and `Contents: Read and write` for the licence App, the last scoped in practice by the `cla-signatures` ruleset, which admits that App and nothing else, and `Projects: Read and write` on the organization plus `Issues: Read` for the board App. Each token is scoped to this repository, the board App's also reaches every project of the `fathomise` organization, and each expires with the job, which is what lets the workflow token stay read-only. `v3` is a moving major tag, so the exact commit a run executes is chosen at run time. | <https://github.com/actions/create-github-app-token/blob/main/LICENSE>, <https://github.com/actions/create-github-app-token/releases/tag/v3.2.0> |

## Developer command-line tooling

What this repository pins, invokes, or ships assets for, and distributes in nothing: a tool restored from `.config/dotnet-tools.json`, built from source under `backend/tools/` or `backend/tests/`, run by a script, a workflow, or a skill, or named as the runtime the deployment assets here are written for. Each row below says which of the three earns it, because this is the section where the question is live — a tool a contributor installs for their own machine and nothing here runs has no row, however often a setup document names it. Each recorded version is the reviewed one; recheck package metadata before adopting a newer one.

| Component and exact version | Use in this repository | License terms verified | Compatibility decision and obligations | Sources reviewed |
|---|---|---|---|---|
| dotnet-ef 10.0.12 | **Earned by pin.** Generates and scripts EF Core migrations for `scripts/add-migration.sh` and `scripts/script-migration.sh`, and answers `has-pending-model-changes` in the `Pending model changes` job. Pinned in `.config/dotnet-tools.json` and restored by `dotnet tool restore` rather than installed globally, because it runs in continuous integration as well as on a developer machine. Not installed into the container image: `deploy/docker/Dockerfile` restores and publishes and does nothing else. | MIT, declared as a license expression in the NuGet package metadata | Allowed as development and build tooling. Not redistributed. | <https://www.nuget.org/packages/dotnet-ef/10.0.12> |
| docfx 2.81.0 | **Earned by pin.** Generates the published documentation site from `docs/` and the XML documentation comments in `backend/src/`, driven by `scripts/build-docs-site.sh` on a developer machine and by the `Publish the documentation` workflow, which deploys the result to GitHub Pages. Pinned in `.config/dotnet-tools.json` and restored by `dotnet tool restore` for the same reason `dotnet-ef` is: it runs in continuous integration as well as locally. Not installed into the container image, and referenced by no project. | MIT, declared as a license expression in the NuGet package metadata and carried as `LICENSE` in the upstream repository, which is a .NET Foundation project | Allowed as development and build tooling. Not redistributed. What it produces is: the site published to GitHub Pages carries docfx's `default` and `modern` templates, which ship inside the tool under that same MIT grant, along with the browser libraries those templates bundle — among them Bootstrap, Bootstrap Icons, lit-html, lunr.js, highlight.js, anchor-js, MathJax, and Mermaid with DOMPurify inside it. Each bundle docfx emits retains its upstream `@license` banner, which is how those permissive terms are attributed on a site assembled from minified assets. The published site is a static artifact rather than a distribution of MailFathom, and none of it reaches an image, a chart, or a binary. `docfx/template/` adds MailFathom's own CSS and JavaScript on top and vendors nothing. | <https://www.nuget.org/packages/docfx/2.81.0>, <https://github.com/dotnet/docfx/blob/main/LICENSE> |
| dotnet-stryker 5.0.0 | **Earned by pin.** Mutation testing over `backend/src/Domain` and `backend/src/Application`, driven by `scripts/mutation-score.sh` on a developer machine and by the `Mutation score` workflow, which the manually dispatched `Diagnostics` channel calls for a report nothing gates on. Pinned in `.config/dotnet-tools.json` and restored by `dotnet tool restore` for the same reason `dotnet-ef` is: it runs in continuous integration as well as locally. Not installed into the container image, and referenced by no project. | Apache-2.0, carried as a `LICENSE` file inside the NuGet package — the nuspec declares `<license type="file">LICENSE</license>` rather than an expression, and its `licenseUrl` is the deprecation placeholder — and confirmed through the GitHub license API for `stryker-mutator/stryker-net` | Allowed as development and build tooling. Not redistributed. What it produces is: an HTML report left on a workflow run, which embeds Stryker's own `mutation-testing-elements` browser bundle under that same Apache-2.0 grant, minified with no per-library banner left in it, which is why the bundle is named here rather than left to attribute itself. Nothing from either reaches an image, a chart, a binary, or the documentation site — the report is downloadable from the run that produced it and is deleted with the run's artifacts. The tool itself bundles its own dependency graph, among it Roslyn, Buildalyzer, LibGit2Sharp, MessagePack, and the AWS and Azure storage clients its baseline providers use, none of which this repository configures or reaches. It carries no usage telemetry; the only outbound call an ordinary run makes is its check for a newer version, which `scripts/mutation-score.sh` disables with `--skip-version-check`. Its dashboard reporter and its cloud baseline providers are the parts that would submit results anywhere, and neither is configured — reporting is `Html`, `Json`, and `Dots`, all of which write to disk. |  <https://www.nuget.org/packages/dotnet-stryker/5.0.0>, <https://github.com/stryker-mutator/stryker-net>, <https://github.com/stryker-mutator/mutation-testing-elements> |
| Aspire.Cli 13.6.1 | **Earned by invocation.** The orchestration commands `docs/operations/local-development.md` requires to work, including the migration resource that applies and resets the local database. It also runs in continuous integration: the shared `Build the schema artifact` workflow installs it at the version pinned for `Aspire.Hosting.AppHost` and runs `scripts/build-schema-artifact.sh`, whose `aspire publish` writes the schema artifact. Both publishing channels call that workflow, so a `Nightly` run installs and runs the same pinned tool a `Release` run does, and the `Apply migrations` job installs it the same way to build the artifact it applies. Not installed into the container image. | MIT, declared as a license expression in the NuGet package metadata | Allowed as development and build tooling. Not redistributed, and nothing it produces carries any part of it: the artifact is SQL generated from this repository's own migrations. | <https://www.nuget.org/packages/Aspire.Cli/13.6.1> |
| Helm on a developer machine, and the copy preinstalled on the GitHub-hosted runner image, whose version follows that image rather than the version recorded here | **Earned by invocation.** The tool the chart under `deploy/helm/` is linted, rendered, and packaged with, as `docs/operations/deployment-kubernetes.md` describes. The `Publish Helm chart` workflow uses the runner's preinstalled copy to lint, render, package, and push the released chart, and the `Helm chart` job of `CI` uses the same copy through `scripts/render-helm-manifests.sh` to lint the chart and hold what it renders against the manifests committed beside it; nothing installs a second one, which is why no action appears for it above. No version is pinned for that comparison either: the rendering is normalized before it is compared, and Helm 3.19.0 and Helm 4.2.3 — the versions this repository has been read with — produce identical normalized renderings of this chart. | Apache-2.0, confirmed through the GitHub license API for `helm/helm`. | Allowed as development and build tooling. The binary is not vendored, bundled, or redistributed, and the chart it packages is MailFathom's own. Taking the runner's copy means the exact version a release packages with is chosen by the runner image rather than pinned here; a packaged chart is a tar archive of this repository's own files with the version flags applied, so what a Helm version decides is the archive's framing rather than its content. | <https://github.com/helm/helm/blob/main/LICENSE> |
| MailKit 4.18.1, MimeKit 4.18.1, and System.CommandLine 2.0.12, referenced by `backend/tools/SyntheticMail` | **Earned by pin.** The development command that fabricates mail and delivers a batch of it over SMTP to a development mailbox, so a developer never has to point a local run at their own correspondence. Its exchange mode reaches that mailbox over IMAP as well, through the same library: it searches for the copy a submission was delivered as, reads the identifier the server assigned to it out of the envelope, and appends the mailbox's own half of a thread to its Sent folder. It is built from source rather than installed, it is in no release artifact and no container image, and no project under `backend/src/` references it — `the_development_tooling_never_reaches_a_published_artifact` in `scripts/test-agent-workflow.sh` is what holds all three. `backend/tests/IntegrationTests` references it so the suite composes its mail through the same generator. | Recorded with the redistributed entries above | Allowed, and nothing further follows: the same three packages are already reviewed for the artifacts that do ship, and this project distributes nothing, so it adds no notice obligation of its own. | See the redistributed entries. |
| BenchmarkDotNet 0.15.8 and the transitive BenchmarkDotNet.Annotations 0.15.8, CommandLineParser 2.9.1, Gee.External.Capstone 2.3.0, Iced 1.21.0, Microsoft.CodeAnalysis.CSharp 4.14.0, Microsoft.CodeAnalysis.Common 4.14.0, Microsoft.CodeAnalysis.Analyzers 3.11.0, Microsoft.Diagnostics.Runtime 3.1.512801, Microsoft.Diagnostics.NETCore.Client 0.2.510501, Microsoft.Diagnostics.Tracing.TraceEvent 3.1.21, Microsoft.DotNet.PlatformAbstractions 3.1.6, Perfolizer 0.6.1, Pragmastat 3.2.4, System.Management 9.0.5, System.CodeDom 9.0.5, and System.Reflection.TypeExtensions 4.7.0, referenced by `backend/tests/Benchmarks` | **Earned by pin.** The harness the dispatched hot-path report is measured with: how long chunking, ranking, MIME extraction, and the reading pane's render of a message carrying its own pictures take, and what each of them allocates. Referenced by that project alone, which asserts nothing and gates nothing — `diagnostics.yml` runs it with failures swallowed and no job waiting on the result. It is adopted rather than hand-rolled because a timing loop written here would produce a number with no warm-up discipline, no outlier handling, and no allocation column, which is worse than no report. The graph behind it is what a benchmark harness needs to do its work and is unusually broad for a development dependency: a compiler to emit the run, a disassembler and a tracer for the diagnostics that are not switched on here, and the statistics library the summary is computed with. None of it is referenced from MailFathom's own code, and no project under `backend/src/` references this one. Four of the transitive packages appear in the design-time row above as well, at the versions that toolchain resolves rather than these: Microsoft.CodeAnalysis.Common and Microsoft.CodeAnalysis.CSharp are 5.0.0 there and 4.14.0 here, and System.CodeDom is 6.0.0 there and 9.0.5 here. | MIT, declared as a license expression in every package's own NuGet metadata except three: CommandLineParser 2.9.1 and Microsoft.DotNet.PlatformAbstractions 3.1.6 predate the expression field and carry the deprecated license URL instead, and both upstream repositories are MIT — confirmed through the GitHub license API for `commandlineparser/commandline` and, for the second, `dotnet/runtime`, which is where `dotnet/core-setup` was folded. Gee.External.Capstone is MIT as a package and bundles the native Capstone disassembler, whose own `LICENSE.TXT` is BSD-3-Clause. | Allowed as development tooling. Not redistributed: nothing under `backend/src/` references this project, no release artifact or container image carries it, and the report it produces is a table of numbers. The BSD-3-Clause notice the bundled Capstone binary would oblige therefore travels with nothing — it is named here so a later decision to publish a benchmark artifact starts from an accurate reading rather than from the package's own MIT expression. | <https://github.com/dotnet/BenchmarkDotNet>, <https://www.nuget.org/packages/BenchmarkDotNet/0.15.8>, <https://github.com/commandlineparser/commandline/blob/master/License.md>, <https://github.com/dotnet/runtime/blob/main/LICENSE.TXT>, <https://github.com/9ee1/Capstone.NET/blob/master/LICENSE>, <https://github.com/capstone-engine/capstone/blob/master/LICENSE.TXT>, <https://github.com/AndreyAkinshin/perfolizer>, <https://github.com/AndreyAkinshin/pragmastat>, <https://github.com/dotnet/roslyn>, <https://github.com/Microsoft/clrmd> |
| GitHub CLI (`gh`), the copy preinstalled on the GitHub-hosted runner image and the copy a developer machine provides, neither version being one this repository records | **Earned by invocation.** The GitHub API calls seven workflows make from the runner: the changed-file query in `Protected paths`, the review gate and its file reads in `Fathom review`, the newest release's schema artifact the `Apply migrations` job downloads, the issue listing, timeline reads, and closes `Housekeeping` makes through `scripts/close-completed-parents.sh`, and the release, nightly, and typo-check workflows' own queries. Nothing installs it and no action pins it, which is why none appears for it above. The skills under `.agents/skills/` run it on a developer machine as well, for the issue, board, and pull-request calls the workflow they describe makes, and that copy is whatever the distribution or GitHub's own package repository provided. Both copies are invoked by this repository. | MIT in the upstream repository, confirmed through the GitHub license API for `cli/cli` | Allowed as development tooling. The binary is not vendored, bundled, or redistributed in either place, so no release obligation follows from which version either copy is. The GitHub hosted service it talks to is governed by GitHub's terms rather than by this license. | <https://github.com/cli/cli/blob/trunk/LICENSE> |
| CPython (`python3`) and its standard library, the copy preinstalled on the GitHub-hosted runner image and the copy a developer machine's distribution provides, neither version being one this repository decides; reviewed against the 3.14 line | **Earned by invocation.** Two places read a structured file with it rather than with a regular expression: the `Collect the files this pull request changes` step of the `Typo check` workflow reads the exclusions `.config/typos.toml` declares through `tomllib`, so a pull request applies the same list a whole-checkout run does, and `scripts/test-agent-workflow.sh` reads the workflow and composite-action files it asserts on, on the runner through `Workflow contracts` and on a developer machine through the full gate. Nothing is installed with `pip`, no package is resolved, and no virtual environment is created — which is why no pin and no action appears for it anywhere above; the one module outside the standard library either reaches is PyYAML, recorded in the next row. A floor is not stated either: `tomllib` arrived in 3.11 and both copies are far past it. | Python Software Foundation License Version 2 for Python itself and its documentation, and permissive terms for the third-party code the interpreter incorporates, each listed beside the code it covers in the upstream `LICENSE`. The GitHub license API answers `NOASSERTION` for the repository, which reflects that file carrying the historical CWI, CNRI, and BeOpen agreements beside the current one rather than different terms for the release in use. | Allowed as development tooling. The interpreter is not vendored, bundled, or redistributed in either place, and nothing it produces here is an artifact — it reads a file and prints lines — so no release obligation follows from which version either copy is. PSF-2.0 is permissive and GPL-compatible, permits commercial use, and requires only that its copyright notice be retained in a distribution of Python; this repository distributes none. Its clause 7 withholds permission to use PSF trademarks to endorse a product, which nothing here does. | <https://github.com/python/cpython/blob/main/LICENSE>, <https://docs.python.org/3/license.html> |
| PyYAML (`yaml`), the copy preinstalled on the GitHub-hosted runner image and the copy a developer machine's distribution provides, neither version being one this repository decides; reviewed against 6.0.3 | **Earned by invocation.** `scripts/test-agent-workflow.sh` parses every workflow under `.github/workflows/` and every composite action under `.github/actions/` with `yaml.safe_load` to assert what a job declares — its timeout, how it installs system packages, where it saves a cache, what the review workflow listens for — on the runner through `Workflow contracts` and on a developer machine through the full gate. Nothing installs it and no file pins it. | MIT, confirmed through the GitHub license API for `yaml/pyyaml` | Allowed as development tooling. The module is not vendored, bundled, or redistributed, and nothing it produces here is an artifact — it parses a file the suite then asserts on — so no release obligation follows from which version either copy is. MIT is permissive, permits commercial use, and requires only that its notice travel with a distribution of PyYAML; this repository distributes none. | <https://github.com/yaml/pyyaml/blob/main/LICENSE> |
| `mcp-publisher` 1.8.1 at review time | **Earned by invocation.** The official command `$prepare-release` uses after a release succeeds to authenticate through GitHub, validate `server.json`, publish its latest stable version to the official MCP Registry, and verify that publication through the public API. The repository does not install or pin it; the owner obtains the current official binary through the Registry's publishing instructions, so a later release rechecks the version and terms before use. | The upstream repository is in a licensing transition: new code and relicensed contributions are Apache-2.0, remaining earlier contributions are MIT, and its documentation is CC-BY-4.0. All three are permissive for this use; MailFathom copies none of the documentation or source. | Allowed as release-time developer tooling. The binary is executed on the owner's machine and is not vendored, bundled, linked, or redistributed. It exchanges a GitHub credential for a short-lived Registry token; neither credential is written to this repository or included in `server.json`. The hosted Registry and the metadata submitted to it are reviewed separately below. | <https://github.com/modelcontextprotocol/registry/releases/tag/v1.8.1>, <https://github.com/modelcontextprotocol/registry/blob/main/LICENSE>, <https://modelcontextprotocol.io/registry/quickstart> |
| `jq`, the copy preinstalled on the GitHub-hosted runner image and the copy a developer machine's distribution provides, neither version being one this repository decides | **Earned by invocation.** Every JSON answer this repository reads on the command line: `scripts/review-obligations.sh`, `scripts/capture-plan.sh` with the two capture scripts that source it, and `scripts/test-agent-workflow.sh` locally, eight workflows including `Protected paths`, `Fathom review`, and both publishing channels, the board and API calls the skills make, and the setup record `get-started-contributors` writes and reads. Nothing installs or pins it, which is why no action appears for it above. | MIT for jq itself, carrying `jq is copyright (C) 2012 Stephen Dolan` in the upstream `COPYING`. The GitHub license API answers `NOASSERTION` for the repository, which reflects the further notices that file carries rather than different terms for jq: David M. Gay's `dtoa.c` and `g_fmt.c` under Lucent Technologies' permissive notice, and the project's own documentation under CC BY 3.0. | Allowed as development tooling. The binary is not vendored, bundled, or redistributed in either place, and nothing it produces here is an artifact — it reads JSON and prints fields, so no release obligation follows from which version either copy is. The CC BY 3.0 documentation is a website this repository does not copy. | <https://github.com/jqlang/jq/blob/master/COPYING> |
| `yq` by Mike Farah, the copy preinstalled on the GitHub-hosted runner image, whose version follows that image rather than one this repository records, or whichever copy a developer running the evaluation script has installed | **Earned by invocation.** Two readers of the dispatch's `evaluation` block. The `AI evaluations` workflow reads its `Ref` and `Sections` keys before the repository is checked out, so no reader of this repository's own exists to read them with yet, and `scripts/run-ai-evaluations.sh` reads `Sections` before the build, so an unknown section fails the run before any of the suite exists to read it. Nothing installs or pins it, which is why no action appears for it above; the evaluation suite reads every other key of the same block with the host's own YAML reader. | MIT, confirmed through the GitHub license API for `mikefarah/yq`. | Allowed as build tooling. The binary is not vendored, bundled, or redistributed, and what it produces is a ref name the workflow resolves to a commit and the section names the script turns into a test filter, so no release obligation follows from which version a machine carries. | <https://github.com/mikefarah/yq/blob/master/LICENSE> |
| ImageMagick, the copy a developer machine's distribution provides, which is not a version this repository decides; reviewed against 7.1.2-18 | **Earned by invocation.** `scripts/compare-captures.sh` compares a design capture against a client capture with it: one difference image, the count of differing pixels read off it, and the connected components of that image as the regions a session then opens. Nothing installs or pins it and no workflow runs it — the design-parity loop is a tool an agent reaches for and gates nothing — which is why no action appears for it above. | The ImageMagick License, an Apache-2.0-derived permissive grant published at <https://imagemagick.org/license/> and carried as `LICENSE` upstream. The GitHub license API answers `NOASSERTION` for the repository, which reflects SPDX carrying this as its own identifier rather than different terms. | Allowed as development tooling. The binary is not vendored, bundled, or redistributed, and nothing it produces for the comparison is an artifact: the captures and the difference image it writes stay in a session's scratch directory or under `artifacts/`, which the repository ignores. It also drew twenty-two of the images the evaluation suite commits and degraded the eleven generated beside them, whose own rows under *Externally sourced source material* record that its licence claims nothing over what it draws or transforms. Its attribution and trademark clauses bind a redistribution of ImageMagick software, of which this repository makes none. | <https://github.com/ImageMagick/ImageMagick/blob/main/LICENSE>, <https://imagemagick.org/license/> |
| Podman 5.0 or later, and the Quadlet generator it installs at `/usr/lib/systemd/system-generators/podman-system-generator`; reviewed against the 6.0.2 release | **Earned by shipment.** The container runtime the deployment assets under `deploy/quadlet/` are written for, and the generator that turns a `.container`, `.network`, or `.volume` file into a systemd service. Nothing in this repository invokes either: the unit sources are text an operator copies onto their own host, exactly as `deploy/compose/compose.yaml` is, and the generator's `--dryrun` mode is the local reading `AGENTS.md` names for reviewing a change there. 5.0 is the floor rather than the reviewed version because it is where `Notify=healthy` and `Ulimit=` reached Quadlet, which `docs/operations/deployment-quadlet.md` records as a requirement of the shape. | Apache-2.0, confirmed through the GitHub license API for the upstream repository, which now answers as `podman-container-tools/podman` — the historical `containers/podman` path redirects to it rather than naming a different project. | Allowed. No Podman component is vendored, bundled, or redistributed, and MailFathom links against nothing of it. The relationship is the one Docker Engine has to the Compose deployment: an operator installs the runtime and this repository ships the files it reads. | <https://github.com/podman-container-tools/podman/blob/main/LICENSE>, <https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html> |
| Docker Engine 29.6.2 | **Earned by invocation.** The container runtime the Aspire orchestration and `scripts/run-integration-tests.sh` require. Invoked as an installed daemon; no Docker component is vendored, bundled, or redistributed. | Apache-2.0 for Docker Engine, published as Moby. | Allowed as development tooling. Docker Hub, from which the PostgreSQL and GreenMail images are pulled, is a hosted service governed by Docker's terms, including its image pull-rate limits. | <https://github.com/moby/moby/blob/master/LICENSE> |
| Node.js, the version `frontend/package.json` requires in `engines` — `>=22.12.0` — and pnpm 11.24.0 | **Earned by pin.** The runtime and the package manager the client stack is built with. `packageManager` in `frontend/package.json` names the pnpm version `frontend/pnpm-lock.yaml` was written by, which is load-bearing rather than decorative: corepack no longer ships with Node, so a contributor's pnpm comes from a global install and that field is what says which one. Both verification gates invoke `pnpm` for any change reaching the client stack and fail by name when it is absent, and `.github/workflows/build-test-frontend.yml` installs both on a runner — Node from `engines` through `actions/setup-node`, and pnpm from `packageManager` through `npm install --global`. Neither is vendored. `deploy/docker/Dockerfile` builds the bundle in a Node stage of its own, and that stage is discarded: no Node runtime, no pnpm, and no `node_modules` reaches the runtime image, which is its own row among the images above. | MIT for both. Node.js's own `LICENSE` is MIT followed by the notices of what it embeds, among them OpenSSL, V8, libuv, and zlib, each permissive; pnpm declares MIT as a license expression in its npm metadata and carries the same text upstream. | Allowed as development and build tooling. Nothing from either is redistributed: what the toolchain produces is a directory of static files, and no Node process joins any deployment shape — which is the condition the client rebuild was adopted under. Node's floor is stated rather than a version pinned **for a developer's machine and for the runner**, because it is the range Vite 8 and the rest of the closure declare in their own `engines`; the reviewed terms do not move between patch releases of it. The container build is the one place a version is pinned instead of floored, and that pin is its own row among the images above. | <https://github.com/nodejs/node/blob/main/LICENSE>, <https://github.com/pnpm/pnpm/blob/main/LICENSE>, <https://www.npmjs.com/package/pnpm/v/11.24.0> |

## Externally sourced source material

| Component and exact version | Use in this repository | License terms verified | Compatibility decision and obligations | Sources reviewed |
|---|---|---|---|---|
| The Android Gradle project under `frontend/src-tauri/gen/android/`, written by `tauri android init` from the template `@tauri-apps/cli` 2.12.2 carries — cargo-mobile2's Android template, at whatever revision that CLI version ships — and committed here rather than regenerated per build. It is 25 files beside the 15 launcher icons — the template's own count less the five resources nothing referenced, deleted rather than committed, and plus the adaptive definition the round icon needed and the dependency lock file: the Gradle build scripts and `settings.gradle`, the `buildSrc` Kotlin plugin that drives Cargo per ABI, `MainActivity.kt`, the manifest, the theme resources, and the Gradle wrapper — `gradlew`, `gradlew.bat`, and `gradle/wrapper/gradle-wrapper.jar`, which are Gradle's own files rather than Tauri's. The launcher icons beside them are not third-party at all: `pnpm exec tauri icon` regenerates every `mipmap-*` from `assets/icon-1254.png`, so what a device shows is MailFathom's own mark under the row for that artwork below. | The Android head's build, and the four decisions this repository states inside it that Tauri's configuration has no key for: the permission set, `android:allowBackup` and the data-extraction rules ADR 0027 requires, and the two `.gitignore` files that keep every build output out of the tree. `tauri android init` writes no file that already exists, so a project left ungenerated would silently revert to the template on every clean machine — which is why it is committed and why `frontend/AGENTS.md` § *The Android head* makes regenerating it part of moving the Tauri pin. | Apache-2.0 for both halves, each read from the publishing repository's own licence file: the template from cargo-mobile2, which the Tauri organisation publishes under Apache-2.0 beside MIT exactly as it publishes the CLI, and the wrapper from Gradle. | Allowed. Both are permissive and neither reaches an artifact by being linked — the wrapper scripts and the jar run on a build machine, and the Gradle files are read rather than packaged. Two obligations follow from the files being committed rather than downloaded: the Apache-2.0 section 4 notice obligations attach to this repository's own source distribution, which the root `LICENSE` and `NOTICE` already answer for; and the copies are frozen at the revision that generated them, so a moved `@tauri-apps/cli` pin is read against this row and the project regenerated rather than the pin moved alone. | <https://github.com/tauri-apps/cargo-mobile2/blob/dev/LICENSE-APACHE>, <https://github.com/gradle/gradle/blob/master/LICENSE>, <https://www.npmjs.com/package/@tauri-apps/cli/v/2.12.2> |
| Apache Software Foundation Individual Contributor License Agreement, the `icla.pdf` published at <https://www.apache.org/licenses/icla.pdf>, reviewed on 2026-08-21 | The structure and several clauses of `CLA.md`, MailFathom's own contributor licence agreement: the definitions of Contribution and submission, the shape of the copyright and patent grants, and the representations about entitlement and about an employer's rights. The text is adapted rather than reproduced — MailFathom's grant is deliberately wider than the ASF's, because clause 2.1 permits relicensing and the ASF's does not — and the agreement carries the attribution to it that this row records | Apache-2.0. The Apache Software Foundation publishes the material on its website under the Apache License, Version 2.0, stated in the footer of <https://www.apache.org/licenses/> and of the ASF site generally | Allowed. Apache-2.0 permits a derivative work of the document under any terms that preserve the notices, so the adapted agreement may carry terms of its own whatever licence MailFathom's code is under, and MailFathom being AGPL-3.0-only puts nothing here in tension. The attribution obligation is discharged in `CLA.md` itself, at the foot of the file, where a reader of the agreement meets it — a document read on its own is the case `NOTICE` cannot cover. This is a document rather than code: it ships in no artifact, reaches no build, and the Apache Software Foundation's trademarks are not used to describe it | <https://www.apache.org/licenses/icla.pdf>, <https://www.apache.org/licenses/> |
| Aspire.ProjectTemplates 13.6.1, service-defaults template | The scaffold for health checks, telemetry, resilience, and service discovery, vendored and then rewritten as `backend/src/Host/ServiceDefaultsExtensions.cs`. The file is maintained as MailFathom source and has diverged from the template it started as. | MIT, declared as a license expression in the NuGet package metadata and in the upstream repository. The emitted template carries no copyright header of its own, and Microsoft publishes no exception waiving MIT's notice condition for template output. | Allowed. MIT permits use and modification under both distribution models, and the resulting file is redistributed as part of `MailFathom.Host`. Treat the notice obligation as live rather than waived: a missing header in generated output is not a grant, so a distribution must carry Aspire's MIT copyright notice and license text for whatever substantial portion of the template survives in this file. The notice bundle resolves it either by including that notice or by citing an explicit upstream exception; leaving it out on the strength of the missing header alone is the one option this row rules out. MailFathom's own AGPL-3.0-only header now sits on the file, which states MailFathom's grant over what it has become and settles nothing about the MIT-licensed portion it started as. | <https://www.nuget.org/packages/Aspire.ProjectTemplates/13.6.1>, <https://github.com/microsoft/aspire/blob/main/LICENSE.TXT> |
| gitleaks rule data, release v8.30.1, the 113 entries listed in `backend/src/Infrastructure/SensitiveContent/Secrets/GitleaksSecretRules.cs` | The third-party half of the secret corpus. The detection engine recorded above ships a corpus oriented on Microsoft credential formats and exactly two third-party patterns, and a mailbox receives forge tokens, cloud access keys, payment keys, and model-provider keys instead; these expressions close that gap. Only entries that recognise a credential by its own shape are taken, and each keeps the corpus's own rule name so a suppression an operator writes reads the same as the entry they looked up. Four mechanical transformations are applied — a POSIX character class becomes its explicit range, a `(?P<name>)` group becomes `(?<name>)`, the trailing delimiter alternation most entries end in becomes a negative lookahead over the alphabet the credential is spelled in, and the group the corpus reports as the secret is renamed `refine` wherever the match is still wider than the credential. The third is the one that changes what an expression matches rather than only how it is written: gitleaks establishes the end of a credential by requiring a quotation mark, whitespace, a semicolon, or the end of the text after it, which is where one ends in a file and not where one ends in a message. Both the upstream release and the revision of these transformations are recorded in that file, and both travel with every finding. | MIT, from the `LICENSE` at tag `v8.30.1` of the upstream repository, under `Copyright (c) 2019 Zachary Rice`, and declared as the repository license through the GitHub license API | Allowed. This is copied material rather than a linked package, so the obligation is MIT's notice condition on the expressions themselves: a distribution must carry that copyright notice and license text. The notice bundle issue #191 owns is where it is discharged, alongside the other components whose notices have to travel with an artifact. The regular expressions carry no runtime dependency on gitleaks and the tool itself is neither invoked, vendored, nor redistributed. Refreshing to a later release is a reviewable diff of that one file, and the row's version moves with it. Four of those expressions are restated a second time, in `backend/tests/SyntheticMail.UnitTests/Generation/SensitiveDecoys/SensitiveDecoyCatalogTests.cs`, where they assert that the development tool's fabricated decoys are values this corpus actually matches; that is the same taken material read twice rather than a further extract, and it moves with the row. | <https://github.com/gitleaks/gitleaks/blob/v8.30.1/LICENSE>, <https://github.com/gitleaks/gitleaks/blob/v8.30.1/config/gitleaks.toml> |
| The application icon, `assets/icon-900.png`, `assets/icon-1254.png`, `assets/icon-180.png`, `assets/icon-180-10KB.png`, and the copies of the last of those at `frontend/src/Client.App/src/assets/mailfathom-mark.png` and `design/mailfathom-logo.png`, generated with OpenAI's ChatGPT image generation on the owner's consumer account | MailFathom's visual identity: the README heading, the Helm chart's `icon`, the container image's `io.artifacthub.package.logo-url`, the documentation site's logo and favicon, and — through the copy under `frontend/` — the mark the client draws at the top of its navigation rail and beside its sign-in claim, which is a copy rather than a reference because a package reaching outside its own root for an asset is the boundary `frontend/src/AGENTS.md` refuses — and, through the copy under `design/`, the same mark on the artboards, which is a copy because `design/` is published as a site of its own and a path out of it would not resolve there. The second is the generated artwork at 1254 pixels square; the next two are that artwork downsampled to 900 and to 180 pixels square, the smaller so that a page on the documentation site does not carry a megabyte of icon it displays at a fraction of the size. The last is that same 180-pixel form reduced to an 8-bit palette so it weighs under ten kilobytes, for a surface that pays for every byte of an icon it renders. None of the four is generated from another at build time, and the three sizes are held rather than derived for that reason. Nothing MailFathom distributes carries the artwork itself, which changes nothing about the terms below: it is MailFathom's own and ships under the root `LICENSE` like every other file here. | OpenAI's Terms of Use, in the Europe edition that governs a consumer account in the EU. Its *Ownership of content* section states that the user owns the Output and that OpenAI assigns to the user all of its right, title, and interest, if any, in it; the adjacent *Similarity of content* section states that Output may not be unique, that another user can receive similar output, and that the assignment reaches neither that output nor Third Party Output. Read from the published policy pages listed at right, whose text is reachable through search from this environment but not by direct retrieval. | Allowed, and not a third-party component in the sense the rest of this register uses. The assignment leaves the artwork MailFathom's own, so it carries no upstream notice, no attribution requirement, and no separate license file, and the root `LICENSE` covers it exactly as it covers every other file here. The row exists because the acceptance policy treats a generated asset as separately reviewable, and the review's answer is that nothing is owed. Two conditions travel with it and neither is a licensing obligation: the assignment is of OpenAI's interest *if any*, so it warrants nothing about whether the artwork is protectable, and similar artwork may reach another user. Both bear on whether MailFathom could later defend a mark, which is a decision no row here makes, and on nothing about distributing the file. | <https://openai.com/policies/eu-terms-of-use/>, <https://openai.com/policies/row-terms-of-use/> |
| `backend/tools/SyntheticMail/corpora/office-en.zip`, one corpus of 100 messages in 26 exchanges, generated on 2026-09-07 against OpenAI's `gpt-5.6-luna` through the API at <https://api.openai.com/v1>. What the model wrote is the content alone — each subject, each plain-text and HTML body, and the 20 readable text attachments; the envelope around it is this repository's own deterministic generator's, which drew every participant, address, thread, date, MIME shape, and attachment size, and filled the five opaque `.bin` parts with its own bytes. `corpus.json` inside the archive records the exact invocation. | Filling a development mailbox with realistic mail, replayed by `dotnet run --project backend/tools/SyntheticMail -- replay`. Generating it costs money and cannot be repeated — a model's words are the one thing the seed does not reproduce — so it is generated once and committed, which is also the only way a pipeline runner reaches it. It is development tooling and reaches no published artifact, on the terms `the_development_tooling_never_reaches_a_published_artifact` in `scripts/test-agent-workflow.sh` holds for everything under `backend/tools/`. | The OpenAI Services Agreement, read on 2026-09-07: *"As between Customer and OpenAI, to the extent permitted by applicable law, Customer retains all ownership rights in Input and owns all Output"*, with OpenAI assigning *"all OpenAI's right, title, and interest, if any, in and to Output"*. The same clause stands in the Terms of Use, which adds that output may not be unique and that the assignment reaches neither another user's output nor Third Party Output. | Usable under AGPL-3.0-only and under the closed-source arm the acceptance policy above is read against, because the terms assign the output rather than licensing it: nothing here is a grant that could be withdrawn or that binds what this repository is distributed under. No attribution obligation follows and nothing reaches `NOTICE`, the corpus travelling in no artifact. Two things bound what may be committed rather than what may be used, and the export refuses a run that breaks either: every address is under the reserved `.test` domain, and the corpus carries no fabricated credential or personal identifier — `--sensitive-percentage 0` is required for an export. The archive was read before it was committed and holds no card, IBAN, key, or credential shape, and every link in it is under the reserved `.example` domain. | <https://openai.com/policies/services-agreement/>, <https://openai.com/policies/row-terms-of-use/>. Both were read through search rather than direct retrieval, `openai.com/policies/*` answering a fetch with HTTP 403. |
| `backend/tools/SyntheticMail/corpora/hard-shapes-en.zip`, 15 messages in two exchanges written by hand for this repository in the archive shape `CorpusArchive` reads, with `corpus.json` inside it recording that it was not generated | The shapes of real mail the model-written corpus carries none of, which the evaluation suite reads after it: a later message correcting an earlier one, a thread of twelve turns among three people, a quoted exchange between two other speakers, a relative date, and two correspondents with similar names. Test and development input only, reaching no published artifact on the terms `the_development_tooling_never_reaches_a_published_artifact` in `scripts/test-agent-workflow.sh` holds for everything under `backend/tools/`. | Nothing external is in it: every word, name, and date was written for the suite, and every address is under the reserved `.test` domain. | Allowed, and owing nothing: the archive is this repository's own and ships under the root `LICENSE` like every other file here. It carries no credential, card, IBAN, or personal identifier shape, and names nobody real. | None; no upstream source exists. |
| `backend/tools/SyntheticMail/corpora/office-pl.zip`, 25 messages in eleven exchanges written by hand in Polish for this repository in the archive shape `CorpusArchive` reads, with `corpus.json` inside it recording that it was not generated | The Polish mail the evaluation suite's language cases read, apart from the English corpora and, where a case searches, beside them: an invoice paid by a promised day, a ticket fixed and closed, a settled itinerary, a date a later message corrects, a quotation chased, a commitment withdrawn, and a newsletter. Test and development input only, reaching no published artifact on the terms `the_development_tooling_never_reaches_a_published_artifact` in `scripts/test-agent-workflow.sh` holds for everything under `backend/tools/`. | Nothing external is in it: every word, name, and date was written for the suite, and every address is under the reserved `.test` domain. | Allowed, and owing nothing: the archive is this repository's own and ships under the root `LICENSE` like every other file here. It carries no credential, card, IBAN, or personal identifier shape, and names nobody real. | None; no upstream source exists. |
| The twenty-two images under `backend/tests/Evaluations/Descriptions/Images/` — `receipt.png`, `shapes.png`, `chart.png`, `notice.png`, `system-turn.png`, `disclosure.png`, `boarding.png`, `whiteboard.png`, `invoice.png`, `sign.png`, `table.png`, `rotated.png`, `pie.png`, `traffic.png`, `house.png`, `blurred-notes.png`, `label.png`, `receipt-pl.png`, `upside-down.png`, `tracking.png`, `bilingual-sign.png`, and `voucher.png` — drawn for this repository with ImageMagick from filled shapes and text set in the DejaVu Sans, DejaVu Sans Bold, and DejaVu Sans Mono Bold faces a Linux distribution ships, two of them blurred past reading on purpose | The input the image-attachment describer is measured on, which the synthetic corpus carries none of: a receipt, two shapes, a bar chart, documents and signs whose text is the point, pictures with no text at all, two images whose text cannot be read, three cards whose text asks to be obeyed: a notice, a forged system turn, and a request to repeat the describer's instructions, and five whose text has to be read exactly: a Polish receipt, a label printed upside down, a long tracking number, a sign in two languages, and a code mixing characters a font draws alike. Every word and figure on them was written for the suite, so they show nobody real and no real document. They are test-only and reach no published artifact: the evaluation project copies them into its own output and nowhere else. | Nothing external is in them but the glyph shapes. ImageMagick's licence governs ImageMagick software and claims nothing over an image it draws. The DejaVu fonts are under the Bitstream Vera and Arev font licences, both of which license the *Font Software* and are silent on a document or image set in it; no font file is committed or embedded. | Allowed, and owing nothing: the images are this repository's own, carry no upstream notice or attribution requirement, and ship under the root `LICENSE` like every other file here. Their font restrictions — renaming a modified font, not selling it on its own — bind the font files, of which none is copied. | <https://imagemagick.org/license/>, <https://dejavu-fonts.github.io/License.html> |
| The eleven images under `backend/tests/Evaluations/Descriptions/Images/` whose names begin `scan-`, `handwritten-`, `mixed-`, `photo-`, `receipt-crumpled-`, and `logo-` — `scan-contract-skewed.jpg`, `scan-invoice-faded.jpg`, `scan-letter-fax.png`, `handwritten-note-en.jpg`, `handwritten-note-pl.jpg`, `mixed-delivery-note.jpg`, `mixed-form-pl.jpg`, `receipt-crumpled-photo.jpg`, `photo-red-toy-car.jpg`, `photo-crushed-parcel.jpg`, and `logo-nordvale.png` — generated on 2026-09-24 against OpenAI's `gpt-5.4-image-2` through OpenRouter's API at <https://openrouter.ai/api/v1>, from prompts written for this repository, and then greyscaled, rotated, blurred, faded, thresholded, speckled, or resized with ImageMagick into the scans real mail carries | The input the describer is measured on where it matters most and ImageMagick could not draw it: a printed page scanned crooked, a faded scan and a faxed letter, handwriting in English and in Polish, two printed forms filled in by pen, a creased receipt photographed on a table, two photographs without text, and a bare logo. Every word, name, number, and code on them was written for the suite, so they show nobody real and no real document. They are test-only and reach no published artifact: the evaluation project copies them into its own output and nowhere else. | OpenRouter's Terms of Service, read on 2026-09-24, defer the question to the model: *"Your ownership rights in the Output are set forth in the Model Terms for each Model you use."* For an OpenAI model that is the OpenAI Services Agreement recorded above for `office-en.zip`, under which the customer *"owns all Output"* and OpenAI assigns *"all OpenAI's right, title, and interest, if any, in and to Output"*. ImageMagick's licence claims nothing over an image it transforms. | Allowed, and owing nothing: the terms assign the output rather than licensing it, so no grant could be withdrawn or bind what this repository is distributed under, and the images ship under the root `LICENSE` like every other file here. No attribution obligation follows and nothing reaches `NOTICE`. The generator was told the exact text of each document and each was read against it before it was committed; it drops Polish diacritics from most of what it prints, which the cases record as printed. | <https://openrouter.ai/terms>, <https://openai.com/policies/services-agreement/> |
| `design/runtime/support.js`, the generated `dc-runtime` bundle Claude Design writes into a project beside the screens it renders, copied out of the project the design's artboards were first drawn in. Its own first line names it as generated from `dc-runtime/src/*.ts` and carries no other notice; nothing else under `design/` is third-party, the artboards, their data, logic and styles there being the owner's own design written in this repository | Booting an artboard. A `.dc.html` page is a component this runtime renders rather than a document a browser draws, so neither opening a page nor `scripts/capture-design.sh` renders anything without it — and that script is the design half of the parity pair every client screen is judged against, in a contributor's clone as much as in the owner's | No licence notice in the file, and none published for `dc-runtime` separately: it is output the design tool writes into the customer's own project, so what governs it is the owner's agreement with Anthropic for that service rather than a licence of its own. Read on 2026-09-09 from the bundle itself and from the project it was copied out of | Terms are missing rather than incompatible, which *Operational rules for future changes* refuses to merge on without the owner's explicit approval — given on 2026-09-09, for this file and for the refreshes that replace it, on the reading above. It is design-time material and reaches no published artifact: no image, package, chart, or binary contains it, and nothing MailFathom builds links against it. The one place it is served from beyond the repository is the Pages site, where `scripts/compose-pages-site.sh` copies `design/` unchanged so the artboards open at `https://fathomise.github.io/MailFathom/design/` — the same file the public repository already carries, served beside the pages that need it, which the owner asked for on 2026-10-07 when the design was put on the site. It carries no licensing header for the reason `AGENTS.md` states — it is generated and replaced whole rather than edited — and a change that replaces it re-reads this row | The bundle's own text, and Anthropic's terms for the design service |
| The twelve design skills of `Leonxlnx/taste-skill` at commit `b482f7a970abb98c4108d4a9f761e458c64cefc8`, every one under the upstream `skills/` directory except `taste-skill-v1`, each copied into `.agents/skills/<name>/` under the `name` its own frontmatter declares: `brandkit`, `design-taste-frontend`, `full-output-enforcement`, `gpt-taste`, `high-end-visual-design`, `image-to-code`, `imagegen-frontend-mobile`, `imagegen-frontend-web`, `industrial-brutalist-ui`, `minimalist-ui`, `redesign-existing-projects`, and `stitch-design-taste` with the `DESIGN.md` beside it. The text is the upstream's except in two places: the frontmatter gains `license: MIT` and a `metadata` block naming the upstream author and repository, and trailing whitespace is trimmed, which `git diff --check` in both gates refuses | Agent skills for design and frontend work, loaded from `.agents/skills/` by every runtime the repository serves — Claude Code through the `.claude/skills` symlink, Codex and Qwen directly — so each session reaches the same versions rather than a per-machine install. The product names the skills mention are the upstream author's text, not a use of those marks by MailFathom | MIT, from the upstream `LICENSE` at that commit, under `Copyright (c) 2026 Leonxlnx`, and declared as the repository licence through the GitHub license API | Allowed. This is copied material rather than a linked package, so the obligation is MIT's notice condition on the copies: each skill directory carries the upstream licence text as `LICENSE.txt`, so the notice travels with a skill copied out on its own. The skills reach no published artifact — no image, package, chart, or binary contains them. `every_skill_declares_its_license` in `scripts/test-agent-workflow.sh` accepts a skill naming a foreign repository only while this row names that repository, and a refresh to a later upstream commit is a reviewable diff of these directories that moves the commit recorded here | <https://github.com/Leonxlnx/taste-skill/blob/b482f7a970abb98c4108d4a9f761e458c64cefc8/LICENSE> |

## Hosted services

These are called, not linked. None is a redistributable dependency, and none supplies code that ships.

| Service | Use in this repository | Terms verified | Decision and operating conditions | Sources reviewed |
|---|---|---|---|---|
| nuget.org | The only package source `NuGet.config` declares, after clearing the machine's inherited source list | Microsoft Visual Studio Marketplace and NuGet.org Terms of Use, last updated January 2025. Microsoft licenses no package intellectual property and is not a party to a package's own terms: each offering is governed by the terms included with it, which is why every package's own license is recorded above rather than inferred from the feed. | Allowed. The feed grants nothing beyond delivery, so a package's license is the only thing that authorizes its use. Adding a second source is a licensing and supply-chain decision and must be recorded here. | <https://www.nuget.org/policies/Terms>, <https://learn.microsoft.com/en-us/nuget/nuget-org/policies/data-requests> |
| .NET release metadata, at `builds.dotnet.microsoft.com/dotnet/release-metadata/` | The channel index `scripts/update-dependencies.sh` reads to say which .NET SDK the channel `global.json` pins a floor for currently releases. One unauthenticated GET of a public JSON document, made from a developer machine when somebody runs that survey; nothing in a build, a workflow, or a deployment reaches it, and no other question in this repository is answered from it | The document is `release-notes/releases-index.json` of `dotnet/core`, which is MIT and confirmed through the GitHub license API; the address above is the Microsoft-operated build storage that serves the same file, under Microsoft's own terms for it | Allowed. Nothing is submitted: the request carries no credential, no repository content, and no identifier of this project, and what comes back is a version number the survey prints beside a pin. It is read rather than redistributed, and no artifact carries any part of it. The floor in `global.json` is never moved by a machine on the strength of this answer — [`docs/operations/local-development.md`](https://github.com/fathomise/MailFathom/blob/main/docs/operations/local-development.md) records why that pin is surveyed and left alone | <https://github.com/dotnet/core/blob/main/release-notes/releases-index.json>, <https://github.com/dotnet/core/blob/main/LICENSE.TXT> |
| Microsoft Artifact Registry, at `mcr.microsoft.com` | **Earned by invocation.** The registry API `scripts/update-dependencies.sh` reads to say what the two `mcr.microsoft.com/dotnet` pins in `deploy/docker/Dockerfile` could move to: `GET /v2/<repository>/tags/list` for the published tags, and `HEAD /v2/<repository>/manifests/latest` for the `Docker-Content-Digest` header a digest pin is compared against. Both are unauthenticated and both are made from a developer machine when somebody runs that survey; no build, workflow, or deployment reaches this API, and pulling the images themselves is `docker` and Podman's business rather than this script's | Microsoft Artifact Registry Terms of Use, last updated May 2022. They define the Services as the discovery web properties **and the MAR APIs**, so the calls above are governed by them rather than by the images' own terms; they grant access and use "for your personal and internal business purposes"; they state that no Microsoft account is needed to access and browse the Services; and under *Throttling* they reserve the right to limit the number of requests without publishing a figure. Each artifact is governed by the terms included with it rather than by the registry, which is why the two `mcr.microsoft.com/dotnet` image rows above record what those images are licensed under and this row records nothing about them. The service carries no SLA | Allowed as a read-only metadata source. Nothing is submitted: the requests carry no credential, no repository content, and no identifier of this project, and what comes back is a list of tag names and a digest the survey prints beside a pin. Neither pin is ever rewritten by a machine on the strength of it — the images are one of the two families the survey reports and refuses to write. The throttling clause is the operating condition, and the traffic under it is one request per image: the tag list for a tag pin or the manifest head for a digest pin, never both, because the survey resolves whichever kind of reference the pin actually is. Two pins here are `mcr.microsoft.com` ones and both are tag pins, so a whole survey is two requests, made by a person running a command rather than by anything on a schedule, and retried at most twice each when the host does not answer. Circumventing a usage limit is not a thing this could do at that rate, which the restriction against enabling access "in a manner intended to circumvent usage limits or quotas" is what makes worth saying | <https://mcr.microsoft.com/en-us/terms-of-use>, <https://github.com/microsoft/containerregistry>, <https://github.com/microsoft/containerregistry/blob/main/docs/mcr-endpoints-guidance.md> |
| Official MCP Registry | The public discovery service at `registry.modelcontextprotocol.io`. `$prepare-release` publishes the latest stable `server.json` after the release artifacts exist and then reads the public record back. What leaves the repository is only public project metadata: the server identifier and version, description, repository and documentation URLs, and the deployment-specific remote URL template. No mailbox content, operator deployment hostname, credential, or shared public MCP endpoint is submitted. | Official MCP Registry Terms of Service, effective 2 September 2025. The service is in preview and may make breaking changes or reset data; it is provided as is and may monitor use for quality and compliance. Submitted Registry Data is public and dedicated to the public domain under CC0 1.0, including personal data present in it such as the GitHub username, and downstream registries may enrich or scan it. | Allowed as an optional discovery surface. The maintainer owns or is authorized to submit the concise project metadata in `server.json` and accepts its CC0 dedication; that dedication reaches Registry Data only and does not relicense MailFathom's code or published packages. Availability or removal of the listing changes discovery only: every installation path and the deployment-specific `/mcp` endpoint remain independent. The GitHub login exchanges a credential for a short-lived Registry token, and both stay outside the repository. | <https://modelcontextprotocol.io/registry/terms-of-service>, <https://modelcontextprotocol.io/registry/quickstart>, <https://github.com/modelcontextprotocol/registry> |
| GitHub | Repository hosting, issues, the roadmap project board, pull requests, Actions, the artifact and cache stores, and the GitHub Container Registry the `Release` and `Nightly` workflows publish MailFathom's own image to and that `Release` publishes the Helm chart to, at `ghcr.io/fathomise/charts/mailfathom`, the Pages site the documentation is published to at `https://fathomise.github.io/MailFathom/docs/` and the design at `https://fathomise.github.io/MailFathom/design/`, four repository security features: code scanning, secret scanning with push protection, validity checks, and non-provider patterns, Dependabot alerts, and private vulnerability reporting, and one read of somebody else's package: `scripts/update-dependencies.sh` exchanges an anonymous pull token at `ghcr.io/token` and reads the tags and manifest of `ghcr.io/data-privacy-stack/presidio-analyzer` to say what that pin could move to | GitHub Terms of Service, with the Additional Product Terms covering Actions and Packages and the Privacy Statement covering data handling. Actions usage is subject to the documented limits on job duration, concurrency, and artifact and cache retention, and is free for a public repository on standard GitHub-hosted runners; Packages storage and data transfer are metered against the account's included amounts, and a private package's traffic is billed where a public one's is not. Code scanning, secret scanning alerts, push protection, validity checks, and non-provider patterns carry no licence cost on a public repository, and the organization's enforced `Public repositories` code security configuration turns all of them on. Pages falls under those same Additional Product Terms, which state that it is intended primarily as a showcase for personal and organizational projects and is not allowed to be used as free web hosting to run an online business, an e-commerce site, or any other site primarily directed at facilitating commercial transactions or at providing commercial software as a service. Its documented limits are a published site of no more than 1 GB, a soft bandwidth limit of 100 GB a month, and a soft limit of ten builds an hour that the documentation exempts a site published by a custom Actions workflow from, which is how this one is published. | Allowed. Nothing beyond the repository's own content, its workflow logs, its coverage and test-result artifacts, and the images it publishes is sent. The security features add no new category to that: they read the same public source. Code scanning stores the alerts a run uploads, secret scanning reads the content of a push before it is accepted, Dependabot reads the manifests and lock files, and a vulnerability report arrives as a draft advisory the reporter wrote. Most workflows run with a read-only token; the publishing ones hold `packages: write` and the attestation permissions, and `CodeQL` holds `security-events: write` and nothing else. A package's visibility is a setting of its own rather than one the repository confers, and it is configured to follow the repository's access. The documentation site adds no new category to what is sent either: it is built from files this repository already publishes, and it collects nothing from a reader, which is what also keeps it clear of the service's documented unsuitability for sensitive transactions. The one condition worth stating for it is a field-of-use one rather than a licensing one, and it matters here because the acceptance policy's two distribution models include a commercial closed-source one: Pages may carry this project's documentation as a showcase, and may not become the delivery surface of a commercial offering built on MailFathom. Crossing that line would mean hosting the site elsewhere, and would change nothing about the terms MailFathom's own code is distributed under. | <https://docs.github.com/en/site-policy/github-terms/github-terms-of-service>, <https://docs.github.com/en/site-policy/github-terms/github-terms-for-additional-products-and-features>, <https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement>, <https://docs.github.com/en/pages/getting-started-with-github-pages/github-pages-limits> |
| Sigstore, through GitHub's artifact attestation service | Signing the provenance statement each published image and each published Helm chart carries. Those two artifacts are the whole of it: the released `mfctl` binaries carry no attestation and no code signature, and the checksum file published beside them is what verifies one instead. What is submitted is the artifact's digest and the workflow's own identity — repository, workflow file, commit, and runner — and no repository content | Sigstore is operated as a public-good service under the Linux Foundation; GitHub's artifact attestation documentation states that a public repository's attestations are signed through the public-good instance and recorded in its public transparency log, while a private repository's are signed and stored within GitHub | Allowed as continuous-integration tooling. The signature covers MailFathom's own artifact, so nothing here is a redistributable dependency. The condition worth stating is disclosure rather than licensing: each published digest and the workflow identity that produced it become a public transparency-log entry, which is what makes the attestation verifiable by someone who trusts neither the registry nor this project. | <https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds>, <https://www.sigstore.dev/> |
| Anthropic Claude, through a Claude Code OAuth token on the owner's subscription | The `Fathom review` workflow. What a run submits is everything the reviewer is given, which is the review prompt; the pull request's title, body, author login, and commit identifiers; the patches of its changed files; the resulting content of those files; the inline review comments, reviews, and issue comments already on it that the organization's members and the reviewer wrote, with their authors; the title and body of the issue it closes; and the repository files at the base commit that the reviewer opens while judging the change, which include `AGENTS.md`, the skills, the specifications, the ADRs, and the pre-change version of each changed file. Author logins and free-text comment bodies are personal data of contributors and reviewers. No mailbox data of any kind is involved: the workflow runs on source, and MailFathom stores no mail in this repository. The `Duplicate definitions` workflow as well, which submits a fixed prompt, the paths and names of the definitions a merge-queue entry adds and of those that landed on `main` while it was being written, and every line either side added in the files defining them, comments and licensing headers included — and no author login, no commit message, and no pull-request conversation | Consumer Terms of Service, effective 8 October 2025, which govern the Free, Pro, and Max plans; the Commercial Terms govern an API key instead. Anthropic's published Claude Code data-usage policy states that data from a consumer account, including Claude Code usage, trains future models when the account's data-improvement setting is on, and is retained for five years; with the setting off, retention is thirty days and the data is not used for training. Prompts and outputs travel over TLS and are encrypted at rest | Allowed as an optional review service. Two operating conditions follow and neither is a licensing one. First, the plan's data-improvement setting among the Claude account's privacy controls decides whether what a run submits trains future models. The source itself is published under this repository's own license, so training on it discloses nothing; the author logins and free-text comment bodies that travel with it are contributors' personal data and are not the project's to offer, which is the reason to keep the setting off. It belongs to the account rather than to this repository, so it cannot be enforced from here. Second, the review reports no status check and no required check waits on it, so an unavailable service or an exhausted subscription quota costs the review and its approval, which a maintainer's approval or the organization owner's ruleset bypass, the one bypass both rulesets admit, then stands in for. `Duplicate definitions` is a required check, and there an unavailable service or a spent quota reads as *no verdict* and leaves it green, so it costs that check its answer rather than a merge. It does approve a pull request whose author holds write access, from a branch in this repository it found nothing in, and that approval satisfies the one approving review the `main` ruleset requires; it never approves a pull request from a fork or from an author holding no write access here, which then needs an approval from an account with write access to the repository. `REQUEST_CHANGES` is never submitted, so the service cannot block a merge either. A pull request from a fork is reviewed only after a member of the organization asks for it in a `fathom-review` comment, so a third party's code is submitted by a deliberate act rather than automatically, and nobody outside the organization has their comments submitted at all | <https://www.anthropic.com/legal/consumer-terms>, <https://www.anthropic.com/legal/commercial-terms>, <https://code.claude.com/docs/en/data-usage>, <https://www.anthropic.com/legal/privacy> |
| OpenAI, through the platform API | Two endpoints an operator may declare, each separately and neither implying the other. The embedding endpoint in the `Embeddings` chain sends nothing unless a deployment declares it and an operator activates a profile, and what is sent then is the prepared passage text of the mail this instance holds, which is personal data of the operator's own correspondents. The chat endpoint in the `Chat` section sends whatever conversation a caller composes, and the `ask_mail` tool composes one: a person's question together with the mail passages the run retrieved to answer it — the same category of data, arriving through a second route. Which of the provider's two request APIs that conversation is sent to is an operator setting and changes nothing about what leaves the deployment. The responses API would keep each request as a response object the account can retrieve and read in the provider's console; every call MailFathom conducts over it refuses that, so the abuse-monitoring copy described at right is the only retention that remains and it is not one this project can turn off. What comes back from the chat endpoint is generated text written from both. No credential, no identifier, and no repository content travels with either. The headers of a request are the endpoint's own credential, whichever headers the operator declared for that endpoint, and a `User-Agent` of one token naming the adapter library and its version; the description of the .NET runtime, the operating system, and the processor architecture that the client library adds by default is switched off for every endpoint this deployment reaches, so no provider is told what a deployment runs on | Services Agreement and Business Terms for API use, with the platform data-usage policy layered on them. API inputs and outputs are not used to train or improve OpenAI models unless the account opts in, and are retained for up to thirty days to run the service and identify abuse, after which they are deleted unless retention is legally required. Zero data retention is available for eligible endpoints on request for a qualifying use case | Allowed as an optional provider, chosen by the operator rather than by this project, and it supplies no code that ships — the SDK it is reached through is recorded above. Three operating conditions follow, all of them the deploying operator's rather than this repository's. First, mail text leaves the deployment: the operator is the controller of that data and is the party who has to have a lawful basis and a processor agreement for it, which is why declaring a provider is an explicit act and activating an embedding profile costs an explicit confirmation on top of it. A chat endpoint carries no second step, because declaring one starts no spending on its own — nothing calls it until a caller composes a conversation. Second, the thirty-day abuse-monitoring retention is a copy of mail content outside the deployment's own storage-limitation controls, and zero data retention is the way to remove it. Third, the account's own training opt-in must stay off, because opting in would offer correspondents' mail to model training and that is not the operator's to offer | <https://openai.com/policies/services-agreement/>, <https://openai.com/policies/business-terms/>, <https://platform.openai.com/docs/guides/your-data>, <https://openai.com/enterprise-privacy/> |
| Azure OpenAI, through a Microsoft Foundry resource | The same two endpoints, reached through an Azure resource instead. What is sent is identical: the prepared passage text for an embedding endpoint, and the composed conversation for a chat endpoint, and nothing else — including the refusal of a stored response, which one client construction sends to either provider | Microsoft Product Terms and the Products and Services Data Protection Addendum, with the service's own data, privacy, and security documentation layered on them. Prompts, completions, and embeddings are not available to OpenAI or to other customers, are not used to improve Microsoft or third-party models, and are not stored in the model. Abuse monitoring may store a sample of prompts and completions for human review, in the resource's own geography, for up to thirty days; a customer approved for modified abuse monitoring has that storage and that review turned off, which is verifiable from the resource's `ContentLogging` capability | Allowed as an optional provider, on the same terms as the entry above and with the same three conditions belonging to the deploying operator. Two differences are worth stating. The data protection addendum makes Microsoft a processor under a written agreement the operator already holds if they run other Azure services, which is a materially easier position than negotiating one; and abuse-monitoring storage is removed by an approval rather than by a per-endpoint setting, so an operator who needs it must apply for it before mail content is sent | <https://learn.microsoft.com/en-us/azure/ai-foundry/responsible-ai/openai/data-privacy>, <https://learn.microsoft.com/en-us/azure/ai-foundry/openai/concepts/abuse-monitoring>, <https://aka.ms/DPA>, <https://www.microsoft.com/licensing/terms/> |
| OpenRouter, through its OpenAI-compatible API | An endpoint an operator may declare at an `openrouter.ai` address, in either section, which forwards each request to one of several upstream providers of the routed model. What is sent is what the OpenAI entry above describes for the same endpoint, and nothing else, with one addition that is OpenRouter's alone: a chat model declaring `StickySessions` sends every request of one Agent conversation with an `x-session-id` header, whose value is a SHA-256 hash of that conversation's identifier. OpenRouter uses it as the key it keeps the conversation on one upstream provider by, for ten minutes after the last request and separately per model. It links the requests of one conversation to each other, which is its whole purpose, and carries no content, no person, and no identifier this deployment uses anywhere else | Terms of Service, last updated 31 August 2026, and the Privacy Policy, with the data-collection and provider-logging pages of its documentation. OpenRouter stores no prompt or response unless the account opts in to private input and output logging or to OpenRouter's own use of inputs and outputs, and keeps metadata such as token counts and latency. Each upstream provider has its own logging, retention, and training policy under its own model terms, which the account accepts by using that model; the account can refuse routing to providers that may train on its data, and a request can be restricted to providers with a given data policy. Reselling API access or building a competing service is prohibited | Allowed as an optional provider, chosen by the operator rather than by this project, and it supplies no code that ships. The conditions the OpenAI entry states hold here too and are still the operator's, with two more that follow from a gateway standing in front of other providers. Both of OpenRouter's opt-ins must stay off, because either one keeps correspondents' mail at OpenRouter under a licence the operator is not entitled to grant. And the upstream provider is chosen per request, so the retention and training terms that apply are whichever provider answered: the operator restricts routing to providers that neither train nor retain, through the account's settings or a `provider` block in the model's `AdditionalProperties` | <https://openrouter.ai/terms>, <https://openrouter.ai/privacy>, <https://openrouter.ai/docs/guides/privacy/data-collection>, <https://openrouter.ai/docs/guides/privacy/logging>, <https://openrouter.ai/docs/guides/best-practices/prompt-caching> |
| OpenRouter, through its Decisions API | A decision model an operator may declare under `Decisions:Models`, reached at `POST /api/alpha/decisions` under `https://openrouter.ai` or a regional host, which answers a closed question about mail with probabilities. What is sent is the mail the question is about, the questions and their criteria, the declared model, and a routing block the adapter always writes, `"provider": { "zdr": true, "data_collection": "deny" }`, which restricts routing to upstream endpoints that retain nothing and refuses providers that may train; no configuration key can loosen it, and no session, user, or trace identifier is ever sent, so nothing links one request to another at the router. The answer's `cost` member is read by nothing | Terms of Service and Privacy Policy, both last updated 31 August 2026, with the data-collection, provider-logging, zero-data-retention, and in-region-routing pages of its documentation and the OpenAPI document, all read on 2026-10-25. The endpoint is alpha: its path is `/api/alpha/`, the OpenAPI document states no deprecation policy, and the terms say nothing about pre-release features, so its shape can change without notice. OpenRouter's own retention and opt-ins are those the entry above records. A model with no zero-retention endpoint is refused by the router rather than routed elsewhere. In-region routing needs a Business or Enterprise plan and a regional host, and on 2026-10-25 the European host listed no decision model | Allowed as an optional provider, chosen by the operator rather than by this project, and it supplies no code that ships. The conditions of the entry above hold here too: both of OpenRouter's opt-ins must stay off. The routing block discharges the upstream half of that entry's conditions on every request, so the operator's remaining obligation is to declare a model that has a zero-retention endpoint, and to leave the capability undeclared where mail has to stay in a region no regional host yet serves. [ADR 0041](https://github.com/fathomise/MailFathom/blob/main/docs/decisions/0041-asking-a-model-a-closed-question-through-openrouters-decisions-api.md) records the review | <https://openrouter.ai/terms>, <https://openrouter.ai/privacy>, <https://openrouter.ai/openapi.json>, <https://openrouter.ai/docs/guides/privacy/data-collection>, <https://openrouter.ai/docs/guides/privacy/provider-logging>, <https://openrouter.ai/docs/guides/features/zdr>, <https://openrouter.ai/docs/guides/features/in-region-routing> |
| Docker Hub | Two unrelated uses. It is the source of the `pgvector/pgvector`, `greenmail/standalone`, and `pgsty/silo` images, and it is the second registry the `Release` and `Nightly` workflows publish MailFathom's own image to, at `docker.io/fathomise/mailfathom`, carrying the same manifest list and the same digest as GHCR. The release run also replaces that repository's overview with `deploy/docker/README.md`, the page written for a reader who already has an image reference. A third use is read-only and is not a pull: `scripts/update-dependencies.sh` reads `hub.docker.com/v2/repositories/<repository>/tags/` unauthenticated, for the tag list and the `digest` field, to say what the `pgvector/pgvector`, `greenmail/standalone`, and `pgsty/silo` pins could move to | Docker Terms of Service and Subscription Service Agreement. Image pulls are rate limited to 100 per six hours per IPv4 address or IPv6 /64 subnet when unauthenticated, and 200 per six hours on an authenticated Personal account. | Allowed for development and integration testing, and as a publication target. The rate limit is an availability condition on the integration suite and on operators pulling the image rather than a licensing one; each pulled image's own license is recorded with the orchestration entries above, and the published one is MailFathom's own. Two operating conditions follow from publishing here. The credential is a personal access token held as the `DOCKERHUB_TOKEN` repository secret, scoped to read, write, and delete, unavailable to pull-request and fork execution, and needed in full: write pushes, delete prunes the nightly channel, and the overview sync requires all three. Neither registry is authoritative over the other — the digest is the artifact's identity and the run verifies that both answer with it — so an operator whose environment can reach only one is not thereby cut off from a release. | <https://www.docker.com/legal/docker-terms-service/>, <https://www.docker.com/legal/docker-subscription-service-agreement/>, <https://docs.docker.com/docker-hub/usage/>, <https://docs.docker.com/docker-hub/repos/manage/hub-images/oci-artifacts/> |
| Shields.io | Three of the six badges in the root `README.md`: the license badge and the documentation badge, both static text, and the version badge, which reads this repository's latest release through `github/v/release`. Nothing is submitted to it by the project — the badge is a URL in a Markdown file, and the request is made by whoever renders the page. The other three badges are GitHub's own workflow-status images and belong to the GitHub entry above | Shields.io is a community-run free service, dual licensed MIT and Apache-2.0, publishing no separate terms of service or service-level agreement. Its production-hosting documentation records that `img.shields.io` sits behind a Cloudflare CDN, and its own README states the instance serves over 1.6 billion images a month | Allowed as an optional presentation surface. No credential of this project's reaches it and no repository content beyond the badge parameters is sent; the version badge reads the same public releases API anyone can read. Two conditions are worth stating and neither is a licensing one. First, availability: an outage or a rate limit degrades a badge to a broken image and affects nothing else, which is why the badges are decoration on the README rather than a source any document relies on. Second, disclosure: GitHub proxies README images through its own `camo` service, so a reader on github.com never contacts Shields.io directly, but a surface that renders the README without proxying — any site that copies the Markdown — exposes that reader's IP address to the Cloudflare CDN. No registry is such a surface today: Docker Hub and every chart listing each render a page of their own under `deploy/`, and neither carries a badge. That is the reader's exposure rather than the project's, and it is the reason the badge count stays small. | <https://github.com/badges/shields>, <https://github.com/badges/shields/blob/master/LICENSE-APACHE>, <https://github.com/badges/shields/blob/master/doc/production-hosting.md> |
| Windows Package Manager community repository | The channel the two Windows `mfctl` binaries are offered through, at `MailFathom.mfctl`, and one a release does not currently reach: the submission job is paused in `.github/workflows/release.yml` while submissions already open wait for that repository's review, so nothing is submitted until it resumes, while the credential, the fork, and the manifests stay configured. When it runs, the `Submit the winget manifest` workflow opens a pull request against microsoft/winget-pkgs carrying three manifest files this project renders — the package's name, description, license, URLs, and one SHA-256 per released Windows binary. Nothing else is submitted: no source, no binary, and no repository content beyond those three files, since winget downloads the installer from this project's own release assets rather than from anything Microsoft hosts | The repository is MIT-licensed and its published policies govern what a manifest may contain and how a submission is validated. Its pull-request template asks a contributor to have signed Microsoft's open-source Contributor License Agreement, which is what places a submitted manifest under that repository's own license; it is a one-time act by the account submitting, and it reaches this project's manifests alone. Microsoft licenses no package intellectual property and is not a party to a package's own terms — MailFathom stays licensed under its own `LICENSE`, and the manifest describes it rather than relicensing it | Allowed as a distribution channel, and it supplies no code that ships. The condition worth stating is that acceptance is somebody else's decision: a submission is reviewed and validated by the community repository, so a version reaches winget after it reaches this project's releases page, and a refusal costs a Windows operator the packaged path to a command they can still download. The credential is `WINGET_PKGS_TOKEN`, a classic personal access token with the `public_repo` scope, and it is the one credential in this repository whose reach extends beyond MailFathom's own artifacts — nothing narrower can open a pull request against a repository this account does not own. `docs/operations/release-procedure.md` records that trade where the pipeline is read | <https://github.com/microsoft/winget-pkgs/blob/master/LICENSE>, <https://github.com/microsoft/winget-pkgs/blob/master/doc/Policies.md>, <https://cla.opensource.microsoft.com/> |
| Context7, operated by Upstash | The mirror this documentation is indexed into, listed as `/fathomise/mailfathom`, and `context7.json` at the repository root, which bounds what the service reads to the user guide, the operations pages, and the feature reference. Nothing is submitted to it: it crawls the public default branch on its own schedule, so what it reads is what anyone can already read, and no credential of this project's reaches it. Traffic runs the other way — a query arrives from whoever is asking, and it is the reformulated lookup an MCP client composes rather than that reader's own prompt | The Context7 Addendum to the Upstash Terms of Service and Privacy Policy, effective August 2025, which controls over both wherever they conflict. Indexed content is *Documentation Data* and stays subject to the original project's own license and intellectual property rights; the addendum states that Context7 grants no rights beyond those already available in the source. A query is *Customer Input*: Upstash states it will not use Inputs for marketing, model training, or resale without explicit consent, and limits processing to delivering the service and maintaining its security. Indexing runs on infrastructure Upstash documents as SOC 2 compliant. The service is best-effort and provided as is, with no warranty of completeness or accuracy, and liability capped at the fees paid in the preceding twelve months or 100 USD where it is provided at no charge, which is the case here. Upstash may amend the addendum at its sole discretion, with continued use as acceptance | Allowed as an optional discovery surface, on the same footing as Artifact Hub below: it hosts nothing of this project's, supplies no code that ships, and no artifact of a release depends on it. Apache-2.0 already permits what it does with these pages, so the indexing needs no further grant and the addendum claims none. Three operating conditions follow and none of them is a licensing one. First, **the mirror is never the canonical channel**: it indexes the default branch, so it cannot bind an answer to the release a reader is running, and retrieval over chunks returns a rule stripped of the pointer that completes it — the root `README.md` and `docs/operations/documentation-site.md` both say so, which is what keeps a lagging answer readable as one. Second, **its scope is `context7.json` rather than the crawler's default**, which would otherwise index the decision records and the workflow contract every `AGENTS.md` carries as though each were product documentation. Third, **the addendum's usage restrictions forbid processing sensitive personal information**, and that bound is live for this project's own audience rather than boilerplate: a question about the mail a deployment holds belongs to that deployment's own tools, and mail content never belongs in a documentation lookup | <https://upstash.com/trust/context7addendum.pdf>, <https://upstash.com/trust/terms.pdf>, <https://context7.com/docs/security/overview>, <https://context7.com/docs/library-updates>, <https://github.com/upstash/context7/blob/master/packages/mcp/schema/context7.json> |
| Artifact Hub | The listing the published Helm chart is discoverable through, registered against `oci://ghcr.io/fathomise/charts/mailfathom`. Artifact Hub hosts nothing: it indexes that OCI repository on its own schedule, and the release run pushes a metadata artifact under the reserved `artifacthub.io` tag so the listing is verifiably this project's | Artifact Hub is a CNCF project; the hosted instance at artifacthub.io is operated under the CNCF's terms and the project's own code is Apache-2.0 | Allowed as an optional discovery surface. Nothing is submitted to it: it pulls from a public registry, so what it reads is what anyone can already read, and no credential of this project's reaches it. The `repositoryID` committed at `deploy/helm/mailfathom/artifacthub-repo.yml` is a public identifier rather than a secret — it grants nothing, and the ownership claim holds because only someone able to push to the chart repository can place it there. The listing is advisory: `helm install` against the OCI reference works whether or not Artifact Hub has indexed it. | <https://artifacthub.io/docs/topics/repositories/>, <https://github.com/artifacthub/hub/blob/master/LICENSE> |

## Trademark and brand use

This section reviews something none of the tables above covers. A license grants rights in *code*; a trademark protects
a *name*, and the two are decided separately — which is what the acceptance policy means by saying that a permissive SDK
license never approves the tradem