nandi/nimstaticpublic Fork 0
5feb05094be9eb20a8a0588b44117d22c7dc3cac
Commits
Clone
git clone https://git.rickub.com/nandi/nimstatic.git
git clone ssh://git@rickub.com/nandi/nimstatic.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

muslkit: musl static libraries from Alpine, without Alpine 5feb050 · on 5feb05094be9eb20a8a0588b44117d22c7dc3cac · nandi · 14h ago
README.md · 120 lines · 4.5 KBmarkdown
Blame HistoryOpen raw

muslkit

musl-linked static libraries from Alpine, without Alpine.

Alpine builds everything against musl and ships *-static packages for most of
it. An .apk is just a tarball, and an APKINDEX is just a text file — so you
don't need apk, a chroot, a container or a distro to get libcrypto.a built
for musl. muslkit fetches the index, resolves a dependency closure, downloads
the packages and unpacks them into a sysroot directory you own.

Nothing is installed system-wide. Nothing needs root.

muslkit add openssl-libs-static
nim c -d:ssl $(muslkit nimflags) --cc:clang --clang.exe:./zigcc app.nim

Why

Getting a fully static Nim (or C, or Zig) binary with TLS on a glibc distro
means finding musl-built archives, and the usual answers are "install Alpine",
"run Docker" or "use Nix". Alpine's mirrors already serve exactly the files —
this just takes them.

Usage

muslkit add <pkg>...        Download packages and unpack into the sysroot
muslkit list                Show what the sysroot holds
muslkit libs                Show the static libraries in the sysroot
muslkit search <text>       Search the index (names and descriptions)
muslkit show <pkg>          Index record for one package
muslkit nimflags [-l lib]   Print nim flags for a static build
muslkit ccflags [-l lib]    Print cc/clang flags
muslkit nimcfg [-o file]    Write a nim.cfg fragment
muslkit zigcc [-o file]     Write a `zig cc -target …-musl` wrapper script
muslkit env                 Shell exports (PKG_CONFIG_*, MUSLKIT_ROOT)
muslkit path                Print the sysroot path
muslkit clean               Remove the sysroot (cache is kept)

Options: -r/--root, -b/--branch (default v3.21, edge for rolling),
-a/--arch, -m/--mirror, --repo main,community, -l/--lib, --cc,
--no-deps, --refresh, -q/--quiet.

The sysroot defaults to $XDG_DATA_HOME/muslkit/sysroot and honors
MUSLKIT_ROOT. Downloads are cached under $XDG_CACHE_HOME/muslkit, so a
second add is offline and the index is re-fetched only once a day.

A full static build, start to finish

muslkit add openssl-libs-static zlib-static
muslkit zigcc                     # writes ./zigcc (needs zig on PATH)
nim c -d:release -d:ssl \
  --cc:clang --clang.exe:./zigcc --clang.linkerexe:./zigcc \
  $(muslkit nimflags) --passL:-s \
  -o:app src/app.nim

muslkit nimflags emits more than include and library paths, because two
things bite every static Nim build with TLS:

  • -d:ssl makes Nim dlopen libssl at runtime. A static binary cannot,
    and dies at startup with could not load: libcrypto.so(...) — even on code
    paths that never touch the network. The fix is --dynlibOverride:ssl --dynlibOverride:crypto plus the archives on the link line.
  • OpenSSL 3 removed SSL_get_peer_certificate, which Nim's wrapper still
    references, so the link fails on one undefined symbol. The fix is
    -DSSL_get_peer_certificate=SSL_get1_peer_certificate.

Both are emitted automatically when libssl.a and libcrypto.a are present in
the sysroot. A static binary carries no CA trust store, so set SSL_CERT_FILE
to a bundle on the host that runs it.

Cross-compiling is the same command with --arch; muslkit add -a aarch64 openssl-libs-static and muslkit zigcc -a aarch64 line up.

Trust

Packages come over HTTPS from the mirror and are unpacked as-is. muslkit checks
the size recorded in the index but does not verify Alpine's RSA signatures —
apk's checksum field covers the package's control segment, not the file, so a
real check means implementing apk's signature format. Treat a sysroot as build
input, not as a trust root. If that matters for your use, pin a mirror you run.

Install

nimble install

or build in place with nim c -d:ssl -o:muslkit src/muslkit.nim. Requires
tar on PATH (an .apk is concatenated gzip streams, which GNU tar reads) and,
for the zigcc helper, zig.

Library

import muslkit

let remote = initRemote(branch = "edge")
let idx = remote.fetchIndex()
for pkg in idx.resolve(["openssl-libs-static"]):
  echo pkg.name, " ", pkg.version, " ", remote.fetchPackage(pkg)

Modules: muslkit/index (APKINDEX parsing, provides and dependency
resolution), muslkit/repo (mirror, cache, download, unpack),
muslkit/sysroot (manifest, static-lib discovery), muslkit/flags (nim/cc/
pkg-config flag emission).

Tests

nimble test

The suite is offline — index parsing, resolution through so:/pkg: provides,
manifest round-trips and flag emission all run against fixtures.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# muslkit

musl-linked static libraries from Alpine, without Alpine.

Alpine builds everything against musl and ships `*-static` packages for most of
it. An `.apk` is just a tarball, and an `APKINDEX` is just a text file — so you
don't need apk, a chroot, a container or a distro to get `libcrypto.a` built
for musl. muslkit fetches the index, resolves a dependency closure, downloads
the packages and unpacks them into a sysroot directory you own.

Nothing is installed system-wide. Nothing needs root.

```bash
muslkit add openssl-libs-static
nim c -d:ssl $(muslkit nimflags) --cc:clang --clang.exe:./zigcc app.nim
```

## Why

Getting a fully static Nim (or C, or Zig) binary with TLS on a glibc distro
means finding musl-built archives, and the usual answers are "install Alpine",
"run Docker" or "use Nix". Alpine's mirrors already serve exactly the files —
this just takes them.

## Usage

```
muslkit add <pkg>...        Download packages and unpack into the sysroot
muslkit list                Show what the sysroot holds
muslkit libs                Show the static libraries in the sysroot
muslkit search <text>       Search the index (names and descriptions)
muslkit show <pkg>          Index record for one package
muslkit nimflags [-l lib]   Print nim flags for a static build
muslkit ccflags [-l lib]    Print cc/clang flags
muslkit nimcfg [-o file]    Write a nim.cfg fragment
muslkit zigcc [-o file]     Write a `zig cc -target …-musl` wrapper script
muslkit env                 Shell exports (PKG_CONFIG_*, MUSLKIT_ROOT)
muslkit path                Print the sysroot path
muslkit clean               Remove the sysroot (cache is kept)
```

Options: `-r/--root`, `-b/--branch` (default `v3.21`, `edge` for rolling),
`-a/--arch`, `-m/--mirror`, `--repo main,community`, `-l/--lib`, `--cc`,
`--no-deps`, `--refresh`, `-q/--quiet`.

The sysroot defaults to `$XDG_DATA_HOME/muslkit/sysroot` and honors
`MUSLKIT_ROOT`. Downloads are cached under `$XDG_CACHE_HOME/muslkit`, so a
second `add` is offline and the index is re-fetched only once a day.

## A full static build, start to finish

```bash
muslkit add openssl-libs-static zlib-static
muslkit zigcc                     # writes ./zigcc (needs zig on PATH)
nim c -d:release -d:ssl \
  --cc:clang --clang.exe:./zigcc --clang.linkerexe:./zigcc \
  $(muslkit nimflags) --passL:-s \
  -o:app src/app.nim
```

`muslkit nimflags` emits more than include and library paths, because two
things bite every static Nim build with TLS:

- **`-d:ssl` makes Nim `dlopen` libssl at runtime.** A static binary cannot,
  and dies at startup with `could not load: libcrypto.so(...)` — even on code
  paths that never touch the network. The fix is `--dynlibOverride:ssl
  --dynlibOverride:crypto` plus the archives on the link line.
- **OpenSSL 3 removed `SSL_get_peer_certificate`,** which Nim's wrapper still
  references, so the link fails on one undefined symbol. The fix is
  `-DSSL_get_peer_certificate=SSL_get1_peer_certificate`.

Both are emitted automatically when `libssl.a` and `libcrypto.a` are present in
the sysroot. A static binary carries no CA trust store, so set `SSL_CERT_FILE`
to a bundle on the host that runs it.

Cross-compiling is the same command with `--arch`; `muslkit add -a aarch64
openssl-libs-static` and `muslkit zigcc -a aarch64` line up.

## Trust

Packages come over HTTPS from the mirror and are unpacked as-is. muslkit checks
the size recorded in the index but does **not** verify Alpine's RSA signatures —
apk's checksum field covers the package's control segment, not the file, so a
real check means implementing apk's signature format. Treat a sysroot as build
input, not as a trust root. If that matters for your use, pin a mirror you run.

## Install

```bash
nimble install
```

or build in place with `nim c -d:ssl -o:muslkit src/muslkit.nim`. Requires
`tar` on PATH (an `.apk` is concatenated gzip streams, which GNU tar reads) and,
for the `zigcc` helper, `zig`.

## Library

```nim
import muslkit

let remote = initRemote(branch = "edge")
let idx = remote.fetchIndex()
for pkg in idx.resolve(["openssl-libs-static"]):
  echo pkg.name, " ", pkg.version, " ", remote.fetchPackage(pkg)
```

Modules: `muslkit/index` (APKINDEX parsing, provides and dependency
resolution), `muslkit/repo` (mirror, cache, download, unpack),
`muslkit/sysroot` (manifest, static-lib discovery), `muslkit/flags` (nim/cc/
pkg-config flag emission).

## Tests

```bash
nimble test
```

The suite is offline — index parsing, resolution through `so:`/`pkg:` provides,
manifest round-trips and flag emission all run against fixtures.