An SMTP client and server library for Zig implementing RFC 5321.
0

Configure Feed

Select the types of activity you want to include in your feed.

Use zon2nix to provide Zig dependencies to Nix builds

build.zig.zon.nix is generated from build.zig.zon by
github.com/jcollie/zon2nix (regenerate with
`nix run github:jcollie/zon2nix#zon2nix -- --nix=build.zig.zon.nix
--16 build.zig.zon` whenever dependencies change). It evaluates to the
package layout `zig build --system` expects, replacing the hand-written
nix/zig-deps.nix: package.nix now builds and tests with --system, the
flake exposes it as packages.zig-deps, and the docs workflow uses it
directly instead of materializing zig-pkg. The lazy exim and isemail
dependencies are included, so those test features work offline too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012HBHFhoTYa8TU9GLwobfbx

+148 -42
+6
REUSE.toml
··· 18 18 "© 2026 Jeffrey C. Ollie <jeff@ocjtech.us>", 19 19 ] 20 20 SPDX-License-Identifier = "GPL-2.0-or-later" 21 + 22 + # Generated by zon2nix from build.zig.zon. 23 + [[annotations]] 24 + path = "build.zig.zon.nix" 25 + SPDX-FileCopyrightText = "© 2026 Jeffrey C. Ollie <jeff@ocjtech.us>" 26 + SPDX-License-Identifier = "MIT"
+133
build.zig.zon.nix
··· 1 + # generated by zon2nix (https://github.com/jcollie/zon2nix) 2 + { 3 + lib, 4 + linkFarm, 5 + fetchzip, 6 + fetchurl, 7 + fetchgit, 8 + runCommandLocal, 9 + zig_0_16, 10 + zstd, 11 + name ? "zig-packages", 12 + }: 13 + let 14 + unpackZigArtifact = 15 + { 16 + name, 17 + artifact, 18 + }: 19 + runCommandLocal name 20 + { 21 + nativeBuildInputs = [ zig_0_16 ]; 22 + } 23 + '' 24 + # workaround https://codeberg.org/ziglang/zig/issues/31866 25 + # https://github.com/Cloudef/zig2nix/issues/54 26 + mkdir "$TMPDIR/src" "$TMPDIR/cache" 27 + touch "$TMPDIR/src/build.zig" 28 + hash="$(cd "$TMPDIR/src" && zig fetch --global-cache-dir "$TMPDIR/cache" ${artifact})" 29 + mkdir "$out" 30 + tar zxvf "$TMPDIR/cache/p/$hash.tar.gz" --directory "$out/" --strip-components=1 31 + ''; 32 + 33 + fetchZig = 34 + { 35 + name, 36 + url, 37 + hash, 38 + unpack, 39 + }: 40 + let 41 + artifact = 42 + if unpack then 43 + fetchzip { 44 + inherit url hash; 45 + nativeBuildInputs = [ zstd ]; 46 + } 47 + else 48 + fetchurl { inherit url hash; }; 49 + in 50 + unpackZigArtifact { inherit name artifact; }; 51 + 52 + fetchGitZig = 53 + { 54 + name, 55 + url, 56 + hash, 57 + }: 58 + let 59 + parts = lib.splitString "#" url; 60 + url_base = builtins.elemAt parts 0; 61 + url_without_query = builtins.elemAt (lib.splitString "?" url_base) 0; 62 + rev_base = builtins.elemAt parts 1; 63 + rev = 64 + if builtins.match "^[a-fA-F0-9]{40}$" rev_base != null then rev_base else "refs/heads/${rev_base}"; 65 + in 66 + fetchgit { 67 + inherit name rev hash; 68 + url = url_without_query; 69 + deepClone = false; 70 + fetchSubmodules = false; 71 + }; 72 + 73 + fetchZigArtifact = 74 + { 75 + name, 76 + url, 77 + hash, 78 + unpack, 79 + }: 80 + let 81 + parts = lib.splitString "://" url; 82 + proto = builtins.elemAt parts 0; 83 + path = builtins.elemAt parts 1; 84 + fetcher = { 85 + "git+http" = fetchGitZig { 86 + inherit name hash; 87 + url = "http://${path}"; 88 + }; 89 + "git+https" = fetchGitZig { 90 + inherit name hash; 91 + url = "https://${path}"; 92 + }; 93 + http = fetchZig { 94 + inherit name hash unpack; 95 + url = "http://${path}"; 96 + }; 97 + https = fetchZig { 98 + inherit name hash unpack; 99 + url = "https://${path}"; 100 + }; 101 + }; 102 + in 103 + fetcher.${proto}; 104 + in 105 + linkFarm name [ 106 + { 107 + name = "N-V-__8AAFtMiwHRrbE2kejFyc1FTFciBTZJlJOPGt-eIVGJ"; 108 + path = fetchZigArtifact { 109 + name = "exim"; 110 + url = "https://github.com/Exim/exim/archive/13835a3c1e057efad7da269c0f93bf2eac850205.tar.gz"; 111 + hash = "sha256-mqyGOAONL1uvPWZbraGmBjzrsTRW2424lGae0x9CR1U="; 112 + unpack = false; 113 + }; 114 + } 115 + { 116 + name = "N-V-__8AAI5RBAAIsFSqnDYObnOsBo3t9cQMXnvu0vQQRSAf"; 117 + path = fetchZigArtifact { 118 + name = "isemail"; 119 + url = "https://github.com/dominicsayers/isemail/archive/cfeefc3f2f88cb195053f6a309fa4f640cd369b5.tar.gz"; 120 + hash = "sha256-bgW2Lj00UluNZWrydCU7sx9332PmbttAAER4fq5ACmo="; 121 + unpack = false; 122 + }; 123 + } 124 + { 125 + name = "tls-0.1.0-ER2e0jGpBgCkVC-Yp12NgSdHNUtZr52MleJ8roHlUa54"; 126 + path = fetchZigArtifact { 127 + name = "tls"; 128 + url = "https://github.com/ianic/tls.zig/archive/e04ae448ce7ee70c136d4d48b059314543203809.tar.gz"; 129 + hash = "sha256-afPTfauIV49IATX0szuOdKLloyqI4XKsyNk8KkasAvg="; 130 + unpack = true; 131 + }; 132 + } 133 + ]
+1 -1
flake.nix
··· 36 36 rec { 37 37 zsmtp = pkgs.callPackage ./nix/package.nix { }; 38 38 default = zsmtp; 39 - zig-pkg = pkgs.callPackage ./nix/zig-deps.nix { }; 39 + zig-deps = pkgs.callPackage ./build.zig.zon.nix { }; 40 40 } 41 41 ); 42 42
+5 -10
nix/package.nix
··· 8 8 zig_0_16, 9 9 }: 10 10 let 11 - zigDeps = callPackage ./zig-deps.nix { }; 11 + # Generated by zon2nix from build.zig.zon; regenerate with: 12 + # nix run github:jcollie/zon2nix#zon2nix -- --nix=build.zig.zon.nix --16 build.zig.zon 13 + zigDeps = callPackage ../build.zig.zon.nix { }; 12 14 in 13 15 stdenv.mkDerivation { 14 16 pname = "zsmtp"; ··· 18 20 19 21 nativeBuildInputs = [ zig_0_16 ]; 20 22 21 - # Provide the dependency offline through the project-local package 22 - # directory, which the build consults before fetching. 23 - postPatch = '' 24 - mkdir -p zig-pkg 25 - cp -r ${zigDeps}/* zig-pkg/ 26 - ''; 27 - 28 23 dontConfigure = true; 29 24 30 25 buildPhase = '' 31 26 runHook preBuild 32 27 export ZIG_GLOBAL_CACHE_DIR=$TMPDIR/zig-global-cache 33 - zig build install -Doptimize=ReleaseSafe --prefix $out 28 + zig build install -Doptimize=ReleaseSafe --prefix $out --system ${zigDeps} 34 29 runHook postBuild 35 30 ''; 36 31 37 32 doCheck = true; 38 33 checkPhase = '' 39 34 runHook preCheck 40 - zig build test 35 + zig build test --system ${zigDeps} 41 36 runHook postCheck 42 37 ''; 43 38
-25
nix/zig-deps.nix
··· 1 - # SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us> 2 - # SPDX-License-Identifier: MIT 3 - 4 - # The project-local Zig package directory (zig-pkg/) as a derivation, so 5 - # builds can run without Zig fetching anything over the network. Only the 6 - # files listed in each dependency's `paths` may be present, since Zig 7 - # hashes the package contents against the directory name. 8 - 9 - { 10 - runCommand, 11 - fetchzip, 12 - }: 13 - let 14 - # Must match the url/hash pinned in build.zig.zon. 15 - tlsDep = fetchzip { 16 - name = "tls.zig-src"; 17 - url = "https://github.com/ianic/tls.zig/archive/e04ae448ce7ee70c136d4d48b059314543203809.tar.gz"; 18 - hash = "sha256-afPTfauIV49IATX0szuOdKLloyqI4XKsyNk8KkasAvg="; 19 - }; 20 - tlsDepId = "tls-0.1.0-ER2e0jGpBgCkVC-Yp12NgSdHNUtZr52MleJ8roHlUa54"; 21 - in 22 - runCommand "zsmtp-zig-pkg" { } '' 23 - mkdir -p $out/${tlsDepId} 24 - cp -r ${tlsDep}/{build.zig,build.zig.zon,readme.md,src} $out/${tlsDepId}/ 25 - ''
+3 -6
.forgejo/workflows/publish.yaml
··· 15 15 - name: Checkout 16 16 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 17 17 18 - # Materialize the Zig dependencies from nix so zig never has to 19 - # fetch over the network on the runner. 20 - - name: Materialize Zig dependencies 21 - run: cp -r --no-preserve=mode "$(nix build --print-out-paths .#zig-pkg)" zig-pkg 22 - 18 + # The Zig dependencies come from nix (via zon2nix's generated 19 + # build.zig.zon.nix), so zig never fetches over the network. 23 20 - name: Build docs 24 - run: nix develop -c zig build docs 21 + run: nix develop -c zig build docs --system "$(nix build --print-out-paths .#zig-deps)" 25 22 26 23 - name: Publish docs 27 24 run: nix develop -c git-pages-cli https://jeff.ocj.page/zsmtp/ --server ocj.page --token ${{ forge.token }} --upload-dir zig-out/docs