pyproject.toml — setuptools metadata with a legacy-email-proxy console script. Runtime dependencies are read dynamically from requirements.txt, so the Docker build and the package cannot drift apart.
proxy_server.run() — a synchronous entry point for that console script; main is a coroutine and cannot be referenced by one. The __main__ path behaves as before.
flake.nix / package.nix — the package, overlays.default, a dev shell, and checks. The pytest suite runs as part of the build.
module.nix — a NixOS module: services.legacy-email-proxy with freeform settings (environment variables) and a separate environmentFile for credentials, so secrets stay out of the Nix store. Runs under DynamicUser, takes CAP_NET_BIND_SERVICE only while a listener is on a privileged port, and opens no firewall ports.
README: pip install, Nix usage, and a NixOS service example.
Why
The proxy could only be consumed as a container. Anyone deploying it on NixOS had to vendor a package definition into their own configuration — which is exactly what happened downstream, and is now deleted there in favour of this.
Not in scope
The Dockerfile and its CI workflow are untouched.
No Nix job in CI; the runner has no Nix. The build is reproducible locally with nix flake check.
version is static and tracks the latest tag (0.3.0); bump it with the tag.
Consumed from a downstream NixOS host with --override-input: the unit's ExecStart resolves to the module's own build, and that flake's checks pass too.
## What
- `pyproject.toml` — setuptools metadata with a `legacy-email-proxy` console script. Runtime dependencies are read dynamically from `requirements.txt`, so the Docker build and the package cannot drift apart.
- `proxy_server.run()` — a synchronous entry point for that console script; `main` is a coroutine and cannot be referenced by one. The `__main__` path behaves as before.
- `flake.nix` / `package.nix` — the package, `overlays.default`, a dev shell, and `checks`. The pytest suite runs as part of the build.
- `module.nix` — a NixOS module: `services.legacy-email-proxy` with freeform `settings` (environment variables) and a separate `environmentFile` for credentials, so secrets stay out of the Nix store. Runs under `DynamicUser`, takes `CAP_NET_BIND_SERVICE` only while a listener is on a privileged port, and opens no firewall ports.
- README: pip install, Nix usage, and a NixOS service example.
## Why
The proxy could only be consumed as a container. Anyone deploying it on NixOS had to vendor a package definition into their own configuration — which is exactly what happened downstream, and is now deleted there in favour of this.
## Not in scope
- The `Dockerfile` and its CI workflow are untouched.
- No Nix job in CI; the runner has no Nix. The build is reproducible locally with `nix flake check`.
- `version` is static and tracks the latest tag (`0.3.0`); bump it with the tag.
## Verification
- `nix flake check` — package builds, 14 tests pass inside the build.
- Consumed from a downstream NixOS host with `--override-input`: the unit's `ExecStart` resolves to the module's own build, and that flake's checks pass too.
Package the proxy properly so it can be consumed outside a container, and
without downstream users vendoring a package definition into their own
configuration.
- pyproject.toml: setuptools metadata with a `legacy-email-proxy` console
script. Runtime dependencies are read dynamically from requirements.txt, so
the Docker build and the package cannot drift apart.
- proxy_server.run(): a synchronous entry point for the console script, since
`main` is a coroutine and cannot be referenced by one directly. The
`__main__` path is unchanged in behaviour.
- package.nix / flake.nix: the package, an overlay, and a dev shell. The test
suite runs as part of the build, so `nix flake check` covers it.
- module.nix: a NixOS module exposing `services.legacy-email-proxy` with
freeform `settings` (environment variables) and a separate `environmentFile`
for credentials, so secrets stay out of the Nix store. Runs under
DynamicUser with CAP_NET_BIND_SERVICE only while a listener needs a
privileged port, and opens no firewall ports.
The Dockerfile and its CI workflow are deliberately untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
What
pyproject.toml— setuptools metadata with alegacy-email-proxyconsole script. Runtime dependencies are read dynamically fromrequirements.txt, so the Docker build and the package cannot drift apart.proxy_server.run()— a synchronous entry point for that console script;mainis a coroutine and cannot be referenced by one. The__main__path behaves as before.flake.nix/package.nix— the package,overlays.default, a dev shell, andchecks. The pytest suite runs as part of the build.module.nix— a NixOS module:services.legacy-email-proxywith freeformsettings(environment variables) and a separateenvironmentFilefor credentials, so secrets stay out of the Nix store. Runs underDynamicUser, takesCAP_NET_BIND_SERVICEonly while a listener is on a privileged port, and opens no firewall ports.Why
The proxy could only be consumed as a container. Anyone deploying it on NixOS had to vendor a package definition into their own configuration — which is exactly what happened downstream, and is now deleted there in favour of this.
Not in scope
Dockerfileand its CI workflow are untouched.nix flake check.versionis static and tracks the latest tag (0.3.0); bump it with the tag.Verification
nix flake check— package builds, 14 tests pass inside the build.--override-input: the unit'sExecStartresolves to the module's own build, and that flake's checks pass too.