Fixes the three findings from the licence audit. None is a conflict — the audit came back clean on everything that could have been one — but all three are ways the AGPL fails to stick in practice.
What the audit found clean
LICENSE is the full AGPL-3.0 text, §13 present; root package.json and the README agree.
All 182 installed packages are permissive: MIT 155, ISC 13, Apache-2.0 5, BSD-2/3 4, plus MIT-0, 0BSD, Unlicense (wouter), CC-BY-4.0 (caniuse-lite, build data) and dompurify's MPL-2.0 OR Apache-2.0. No GPL-2.0-only, no LGPL, no SSPL/BUSL, nothing proprietary or unlicensed.
Relicensing authority holds — only John Ellis and LINUXexpert.org have authored code, so GPL-3.0 → AGPL-3.0 was yours to make.
Third-party attribution already ships: 137 @license/copyright comments survive in the built JS and 168 LICENSE files ride along in the image. (I assumed this was a gap and was wrong.)
The three fixes
1. The offer was hard-coded to this repository. §13 asks whoever runs a modified version to offer that version's source. Every deployment carrying a patch was pointing at the wrong tree, and would have kept doing so unless its operator noticed and edited AboutSettings.tsx. SOURCE_URL now sets it, beside APP_NAME, and both the sign-in page and About read it. Documented in .env.example, docker-compose.yml and the README.
2. The offer was only visible after signing in. Whoever is looking at the sign-in form is interacting with the program over a network too. The footer carries it now, fetched from /api/config so it is right even before there is a session.
3. The two workspace packages declared no licence. Private, so npm never minded, but anything reading the tree saw a blank where the rest of the project says AGPL-3.0-or-later.
npm run typecheck, npm test — 224 web + 88 server passing.
Left alone, deliberately
The runtime image ships devDependencies — npm ci without --omit=dev, then the whole node_modules is copied. Not a licence problem (all permissive, notices included) but vite, typescript and vitest are sitting in production. That is a size and attack-surface question rather than a legal one, so it belongs in its own change.
Per-file AGPL headers are still absent. Not required; the FSF only recommends them.
Fixes the three findings from the licence audit. None is a conflict — the audit came back clean on everything that could have been one — but all three are ways the AGPL fails to stick in practice.
## What the audit found clean
- `LICENSE` is the full AGPL-3.0 text, §13 present; root `package.json` and the README agree.
- **All 182 installed packages are permissive**: MIT 155, ISC 13, Apache-2.0 5, BSD-2/3 4, plus `MIT-0`, `0BSD`, `Unlicense` (wouter), `CC-BY-4.0` (caniuse-lite, build data) and dompurify's `MPL-2.0 OR Apache-2.0`. No GPL-2.0-only, no LGPL, no SSPL/BUSL, nothing proprietary or unlicensed.
- **Relicensing authority holds** — only `John Ellis` and `LINUXexpert.org` have authored code, so GPL-3.0 → AGPL-3.0 was yours to make.
- **Third-party attribution already ships**: 137 `@license`/copyright comments survive in the built JS and 168 `LICENSE` files ride along in the image. (I assumed this was a gap and was wrong.)
## The three fixes
**1. The offer was hard-coded to this repository.** §13 asks whoever *runs* a modified version to offer **that** version's source. Every deployment carrying a patch was pointing at the wrong tree, and would have kept doing so unless its operator noticed and edited `AboutSettings.tsx`. `SOURCE_URL` now sets it, beside `APP_NAME`, and both the sign-in page and About read it. Documented in `.env.example`, `docker-compose.yml` and the README.
**2. The offer was only visible after signing in.** Whoever is looking at the sign-in form is interacting with the program over a network too. The footer carries it now, fetched from `/api/config` so it is right even before there is a session.
**3. The two workspace packages declared no licence.** Private, so npm never minded, but anything reading the tree saw a blank where the rest of the project says `AGPL-3.0-or-later`.
## Verified both ways round
| `SOURCE_URL` | sign-in page | Settings › About |
| --- | --- | --- |
| `https://example.org/my-fork` | `AGPL-3.0 source → example.org/my-fork` | `example.org/my-fork` |
| unset | `AGPL-3.0 source → github.com/LINUXexpert-org/ihasmail` | `github.com/LINUXexpert-org/ihasmail` |
`npm run typecheck`, `npm test` — 224 web + 88 server passing.
## Left alone, deliberately
The runtime image ships **devDependencies** — `npm ci` without `--omit=dev`, then the whole `node_modules` is copied. Not a licence problem (all permissive, notices included) but vite, typescript and vitest are sitting in production. That is a size and attack-surface question rather than a legal one, so it belongs in its own change.
Per-file AGPL headers are still absent. Not required; the FSF only recommends them.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
**Merged** 2026-08-25 as coffey-labs/ihasmail@62242493f88d
<sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes the three findings from the licence audit. None is a conflict — the audit came back clean on everything that could have been one — but all three are ways the AGPL fails to stick in practice.
What the audit found clean
LICENSEis the full AGPL-3.0 text, §13 present; rootpackage.jsonand the README agree.MIT-0,0BSD,Unlicense(wouter),CC-BY-4.0(caniuse-lite, build data) and dompurify'sMPL-2.0 OR Apache-2.0. No GPL-2.0-only, no LGPL, no SSPL/BUSL, nothing proprietary or unlicensed.John EllisandLINUXexpert.orghave authored code, so GPL-3.0 → AGPL-3.0 was yours to make.@license/copyright comments survive in the built JS and 168LICENSEfiles ride along in the image. (I assumed this was a gap and was wrong.)The three fixes
1. The offer was hard-coded to this repository. §13 asks whoever runs a modified version to offer that version's source. Every deployment carrying a patch was pointing at the wrong tree, and would have kept doing so unless its operator noticed and edited
AboutSettings.tsx.SOURCE_URLnow sets it, besideAPP_NAME, and both the sign-in page and About read it. Documented in.env.example,docker-compose.ymland the README.2. The offer was only visible after signing in. Whoever is looking at the sign-in form is interacting with the program over a network too. The footer carries it now, fetched from
/api/configso it is right even before there is a session.3. The two workspace packages declared no licence. Private, so npm never minded, but anything reading the tree saw a blank where the rest of the project says
AGPL-3.0-or-later.Verified both ways round
SOURCE_URLhttps://example.org/my-forkAGPL-3.0 source → example.org/my-forkexample.org/my-forkAGPL-3.0 source → github.com/LINUXexpert-org/ihasmailgithub.com/LINUXexpert-org/ihasmailnpm run typecheck,npm test— 224 web + 88 server passing.Left alone, deliberately
The runtime image ships devDependencies —
npm ciwithout--omit=dev, then the wholenode_modulesis copied. Not a licence problem (all permissive, notices included) but vite, typescript and vitest are sitting in production. That is a size and attack-surface question rather than a legal one, so it belongs in its own change.Per-file AGPL headers are still absent. Not required; the FSF only recommends them.
🤖 Generated with Claude Code
Merged 2026-08-25 as coffey-labs/ihasmail@62242493f8
Rebuilt from: git history, session transcript.