Proton WebClients: nine browser apps sharing one pnpm workspace and one version number
Monorepo hosting the proton web clients
At a glance
- What is it?
- This monorepo holds Proton's browser front ends for Mail, Calendar, Drive, Account, VPN, Pass, Wallet, Lumo and Meet, plus the shared component packages they import. Two details decide whether you can run it locally: pnpm workspaces with unified versioning, and the fact that the VPN app has to be served twice.
- Who is it for?
- Work in this repository if you are changing Proton's own web interfaces or the shared packages behind them, and expect a pnpm workspace with a single version to bump rather than independent releases per app. Before you start, check the packageManager field in package.json against your local pnpm, read the VPN section if you touch account or vpn-settings, and plan on serving both VPN entry points if you need the whole thing locally.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- ProtonMail/WebClients5,691 ★ · +85
| Repository | 2026-09-23 | 2026-11-14 |
|---|---|---|
| ProtonMail/WebClients | 5.6k | 5.7k |
85 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Nine applications share one pnpm workspace and one version
The tree is one repository with nine browser applications inside it: Proton Mail, Proton Calendar, Proton Drive, Proton Account, Proton VPN, Proton Pass, Proton Wallet, Proton Lumo and Proton Meet. Alongside the applications sit the shared modules they import and the tooling that surrounds development of the web clients. Everything is wired with pnpm and pnpm workspaces, and the versioning is unified: all packages inside carry one version, so a change in a shared component library cannot ship ahead of the applications that render it. The top level makes that split visible, with applications/ holding the apps and packages/ holding the shared code, next to pnpm-workspace.yaml and pnpm-lock.yaml. Internal dependencies are declared with the workspace protocol, so the root manifest lists packages such as @proton/components, @proton/calendar, @proton/activation and @proton/account as workspace:^ rather than as registry versions. Nine apps and dozens of packages then resolve to local directories during install instead of published tarballs.
Local setup is a clone, one install, and a filtered run
Three prerequisites are named: Node.js satisfying engines.node in the root package.json, pnpm satisfying engines.pnpm, and git. CI installs the exact pnpm version from the packageManager field, but locally any version that satisfies engines.pnpm is fine, and the README suggests installing it with npm install -g pnpm. After that the whole monorepo installs in one step, and the filtered command decides which application you actually see:
# Clone the project
git clone https://github.com/ProtonMail/WebClients.git
git clone [email protected]:ProtonMail/WebClients.git
# Install all dependencies for the entire monorepo & symlink
# local dependents to one another
pnpm install
# Run web clients by running proton-<package-name>
# Example: proton mail web client
pnpm --filter proton-mail run startThe two clone lines are the HTTPS and SSH forms of the same repository, so pick the one that matches your keys. Note what pnpm install does for you: it installs for the entire monorepo and symlinks local dependents to one another, which is the step that makes the workspace protocol resolve. Applications start through the filter flag rather than a directory change, so proton-mail, proton-calendar and the rest are reached the same way.
VPN is served twice because the two domains share no session
One application in this monorepo is deliberately served two different ways. VPN appears on both proton.me and protonvpn.com, but the entry points differ: for protonvpn.com it comes from applications/vpn-settings, and for account.proton.me/u/{X}/vpn it comes from applications/account. Pieces of the app are shared and live in @proton/components or @proton/shared, so the code is not duplicated, only the entry point is. The reason is session handling. The two domains are separate, and no local SSO is shared between them, so a local checkout has to serve both applications separately:
# To serve VPN through vpn-settings
pnpm --filter proton-vpn-settings run start --port 8050
# To serve VPN through account
pnpm run start-all --applications "proton-account"The port flag is what keeps the two from colliding on one machine, and start-all routes through a local SSO helper under utilities/local-sso. This is the sharpest constraint in the repository for a newcomer: a VPN change that looks local still has two fronts to serve before you can see it.
Releases are a manual command against a clean main branch
Versioning an application is done by hand, and the precondition is spelled out: be on the main branch, in a state clean enough to release. From the root folder the version command names both the application and the number:
pnpm --filter @proton/version run version --applications proton-X --version x.x.x.xThe shape of that line is worth reading twice, because it is where the unified versioning shows its cost. One command takes a list of applications and a version, which suits a monorepo where every package moves together, and it routes through the @proton/version package rather than through the root scripts. Swap proton-X for the app you are releasing and x.x.x.x for the number, and the same command handles a single app or a batch.
Root scripts are the real interface for the tooling
Most of the guardrails are wired as root scripts in package.json, and several of them run straight from the utilities/ directory rather than through a package filter. Two are branch and workspace checks:
node utilities/repo-rules/src/branch-check/run.ts
node utilities/repo-rules/src/workspace-constraints/run.tsThe rest of the root surface covers versioning, dead dependency hunting and coverage: app-versions runs a shell script under utilities/app-versions, version delegates to @proton/version, knip runs knip against knip.json, knip:staged runs a staged variant, and nyc is wired to npx nyc. install hooks matter too. postinstall runs is-ci || (husky; pnpm run config-app), which means the git hooks are installed only when the install is not happening on CI, and config-app then executes packages/config/install with errors swallowed by 2>/dev/null and a trailing || true. That fallback is why a config step failing does not stop a fresh clone from finishing, and it is also the reason a broken config install is quiet. create-atom scaffolds through @proton/atoms.
Every lint, type and scan tool has its own file at the top level
The top level is unusually explicit about tooling, which is what a monorepo of this size needs. Browsers are pinned in .browserslistrc. TypeScript is split across tsconfig.base.json and tsconfig.webpack.json. Formatting runs through prettier.config.mjs with .prettierignore and .editorconfig alongside it. Styling has .stylelintrc with its own ignore file and stylelint-config-standard-scss plus stylelint-use-logical-spec among the root dependencies, and TypeScript itself is pinned at ^6.0.3. Static analysis has its own layers: knip.json for unused code and dependencies, .semgrepignore for semgrep, and .grype.yaml for vulnerability scanning. Change handling is spread across .husky/, lint-staged.config.mjs, renovate.json for dependency updates, and turbo.json for task running, while .margebot.yml and findApp.config.mjs sit beside a patches/ directory that holds upstream patch files and .npmrc for package manager settings. The cost of that breadth is a wide surface for a first contributor to read before touching any application code.
What the repository will not give you
Four things a newcomer tends to look for are not here. You work from a clone and produce versions yourself. The clone instructions give an HTTPS line and an SSH line side by side without saying which one to use, so a first setup involves picking the transport that matches your keys. Node.js and pnpm are specified only as ranges in engines.node and engines.pnpm, with the exact pnpm version CI uses in the packageManager field. And the localization story is a pointer outward: the repository invites help with translations and links a blog post for the details. The other limit is structural rather than missing. Because VPN enters through two applications on two domains with no shared local SSO, a developer working on that app cannot get the full picture from one dev server, and a release that ships VPN has to account for both entry points.
Editorial conclusion
Work in this repository if you are changing Proton's own web interfaces or the shared packages behind them, and expect a pnpm workspace with a single version to bump rather than independent releases per app. Before you start, check the packageManager field in package.json against your local pnpm, read the VPN section if you touch account or vpn-settings, and plan on serving both VPN entry points if you need the whole thing locally.
Frequently asked questions
What is a WebClient in the ProtonMail WebClients repository?
Here a WebClient is a browser front end. The monorepo hosts Proton Mail, Calendar, Drive, Account, VPN, Pass, Wallet, Lumo and Meet as applications, each with shared modules under packages/ and unified versioning across the workspace.
Can you give me an example of a web client run from WebClients?
Clone the repository, run pnpm install, then start one application by name. Proton Mail is started with pnpm --filter proton-mail run start, and the VPN settings entry point with pnpm --filter proton-vpn-settings run start --port 8050.
What is the difference between HTTP client and WebClient here?
In this repository WebClient means a browser application, not an HTTP client library. The root manifest declares frontend concerns such as TypeScript, prettier, stylelint and is-ci, and the applications are started with pnpm filters rather than configured as HTTP consumers.
What is the difference between a web client and a web server in WebClients?
The repository holds the client half only. The top level contains applications/ and packages/ plus build and lint configuration, and no server application appears among them, so the APIs the clients call are not hosted in this tree.
What are the web clients hosted in this monorepo?
Nine of them: Proton Mail, Proton Calendar, Proton Drive, Proton Account, Proton VPN, Proton Pass, Proton Wallet, Proton Lumo and Proton Meet. VPN is the exception, since it is served through applications/vpn-settings for protonvpn.com and through applications/account for account.proton.me.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/protonmail-webclients)Star history chart for your README
[](https://hysenlabs.com/projects/protonmail-webclients)Was this useful?