diff --git a/.drone.jsonnet b/.drone.jsonnet index 436a039ee..0c41502a6 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -1,8 +1,6 @@ -local default_deps_base='libsystemd-dev python3-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev libsqlite3-dev libcurl4-openssl-dev make'; -local default_deps_nocxx='libsodium-dev ' + default_deps_base; // libsodium-dev needs to be >= 1.0.18 -local default_deps='g++ ' + default_deps_nocxx; -local default_windows_deps='mingw-w64 zip nsis'; -local docker_base = 'registry.oxen.rocks/lokinet-ci-'; +local distro = "fedora-34"; +local distro_name = 'Fedora 34'; +local distro_docker = 'fedora:34'; local submodules = { name: 'submodules', @@ -10,273 +8,39 @@ local submodules = { commands: ['git fetch --tags', 'git submodule update --init --recursive --depth=1'] }; -local apt_get_quiet = 'apt-get -o=Dpkg::Use-Pty=0 -q'; +local dnf(arch) = 'dnf -y --setopt install_weak_deps=False --setopt cachedir=/cache/'+distro+'/'+arch+'/${DRONE_STAGE_MACHINE} '; -// Regular build on a debian-like system: -local debian_pipeline(name, image, - arch='amd64', - deps=default_deps, - build_type='Release', - lto=false, - werror=true, - cmake_extra='', - extra_cmds=[], - jobs=6, - loki_repo=false, - allow_fail=false) = { +local rpm_pipeline(image, buildarch='amd64', rpmarch='x86_64', jobs=6) = { kind: 'pipeline', type: 'docker', - name: name, - platform: { arch: arch }, - trigger: { branch: { exclude: ['debian/*', 'ubuntu/*'] } }, + name: distro_name + ' (' + rpmarch + ')', + platform: { arch: buildarch }, steps: [ submodules, { name: 'build', image: image, - [if allow_fail then "failure"]: "ignore", - environment: { SSH_KEY: { from_secret: "SSH_KEY" } }, + environment: { + SSH_KEY: { from_secret: "SSH_KEY" }, + RPM_BUILD_NCPUS: jobs + }, commands: [ 'echo "Building on ${DRONE_STAGE_MACHINE}"', - 'echo "man-db man-db/auto-update boolean false" | debconf-set-selections', - apt_get_quiet + ' update', - apt_get_quiet + ' install -y eatmydata', - ] + (if loki_repo then [ - 'eatmydata ' + apt_get_quiet + ' install -y lsb-release', - 'cp contrib/deb.loki.network.gpg /etc/apt/trusted.gpg.d', - 'echo deb http://deb.loki.network $$(lsb_release -sc) main >/etc/apt/sources.list.d/loki.network.list', - 'eatmydata ' + apt_get_quiet + ' update' - ] else [] - ) + [ - 'eatmydata ' + apt_get_quiet + ' dist-upgrade -y', - 'eatmydata ' + apt_get_quiet + ' install -y gdb cmake git pkg-config ccache ' + deps, - 'mkdir build', - 'cd build', - 'cmake .. -DWITH_SETCAP=OFF -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_BUILD_TYPE='+build_type+' ' + - (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + - '-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') + - cmake_extra, - 'VERBOSE=1 make -j' + jobs, - '../contrib/ci/drone-gdb.sh ./test/testAll --use-colour yes', - ] + extra_cmds, - } - ], -}; -local apk_builder(name, image, extra_cmds=[], allow_fail=false, jobs=6) = { - kind: 'pipeline', - type: 'docker', - name: name, - platform: {arch: "amd64"}, - trigger: { branch: { exclude: ['debian/*', 'ubuntu/*'] } }, - steps: [ - submodules, - { - name: 'build', - image: image, - [if allow_fail then "failure"]: "ignore", - environment: { SSH_KEY: { from_secret: "SSH_KEY" }, ANDROID: "android" }, - commands: [ - 'VERBOSE=1 JOBS='+jobs+' NDK=/usr/lib/android-ndk ./contrib/android.sh' - ] + extra_cmds - } - ] -}; -// windows cross compile on debian -local windows_cross_pipeline(name, image, - arch='amd64', - build_type='Release', - lto=false, - werror=false, - cmake_extra='', - toolchain='32', - extra_cmds=[], - jobs=6, - allow_fail=false) = { - kind: 'pipeline', - type: 'docker', - name: name, - platform: { arch: arch }, - trigger: { branch: { exclude: ['debian/*', 'ubuntu/*'] } }, - steps: [ - submodules, - { - name: 'build', - image: image, - [if allow_fail then "failure"]: "ignore", - environment: { SSH_KEY: { from_secret: "SSH_KEY" }, WINDOWS_BUILD_NAME: toolchain+"bit" }, - commands: [ - 'echo "Building on ${DRONE_STAGE_MACHINE}"', - 'echo "man-db man-db/auto-update boolean false" | debconf-set-selections', - apt_get_quiet + ' update', - apt_get_quiet + ' install -y eatmydata', - 'eatmydata ' + apt_get_quiet + ' install -y build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool', - 'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix', - 'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix', - 'VERBOSE=1 JOBS=' + jobs + ' ./contrib/windows.sh' - ] + extra_cmds, - } - ], -}; - -// Builds a snapshot .deb on a debian-like system by merging into the debian/* or ubuntu/* branch -local deb_builder(image, distro, distro_branch, arch='amd64', loki_repo=true) = { - kind: 'pipeline', - type: 'docker', - name: 'DEB (' + distro + (if arch == 'amd64' then '' else '/' + arch) + ')', - platform: { arch: arch }, - environment: { distro_branch: distro_branch, distro: distro }, - steps: [ - submodules, - { - name: 'build', - image: image, - failure: 'ignore', - environment: { SSH_KEY: { from_secret: "SSH_KEY" } }, - commands: [ - 'echo "Building on ${DRONE_STAGE_MACHINE}"', - 'echo "man-db man-db/auto-update boolean false" | debconf-set-selections' - ] + (if loki_repo then [ - 'cp contrib/deb.loki.network.gpg /etc/apt/trusted.gpg.d', - 'echo deb http://deb.loki.network $${distro} main >/etc/apt/sources.list.d/loki.network.list' - ] else []) + [ - apt_get_quiet + ' update', - apt_get_quiet + ' install -y eatmydata', - 'eatmydata ' + apt_get_quiet + ' install -y git devscripts equivs ccache git-buildpackage python3-dev', - ||| - # Look for the debian branch in this repo first, try upstream if that fails. - if ! git checkout $${distro_branch}; then - git remote add --fetch upstream https://github.com/oxen-io/loki-network.git && - git checkout $${distro_branch} - fi - |||, - # Tell the merge how to resolve conflicts in the source .drone.jsonnet (we don't - # care about it at all since *this* .drone.jsonnet is already loaded). - 'git config merge.ours.driver true', - 'echo .drone.jsonnet merge=ours >>.gitattributes', - - 'git merge ${DRONE_COMMIT}', - 'export DEBEMAIL="${DRONE_COMMIT_AUTHOR_EMAIL}" DEBFULLNAME="${DRONE_COMMIT_AUTHOR_NAME}"', - 'gbp dch -S -s "HEAD^" --spawn-editor=never -U low', - 'eatmydata mk-build-deps --install --remove --tool "' + apt_get_quiet + ' -o Debug::pkgProblemResolver=yes --no-install-recommends -y"', - 'export DEB_BUILD_OPTIONS="parallel=$$(nproc)"', - #'grep -q lib debian/lokinet-bin.install || echo "/usr/lib/lib*.so*" >>debian/lokinet-bin.install', - 'debuild -e CCACHE_DIR -b', - './contrib/ci/drone-debs-upload.sh ' + distro, - ] - } - ] -}; - - -// Macos build -local mac_builder(name, - build_type='Release', - werror=true, - cmake_extra='', - extra_cmds=[], - jobs=6, - allow_fail=false) = { - kind: 'pipeline', - type: 'exec', - name: name, - platform: { os: 'darwin', arch: 'amd64' }, - steps: [ - { name: 'submodules', commands: ['git fetch --tags', 'git submodule update --init --recursive'] }, - { - name: 'build', - environment: { SSH_KEY: { from_secret: "SSH_KEY" } }, - commands: [ - 'echo "Building on ${DRONE_STAGE_MACHINE}"', - // If you don't do this then the C compiler doesn't have an include path containing - // basic system headers. WTF apple: - 'export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"', - 'ulimit -n 1024', // because macos sets ulimit to 256 for some reason yeah idk - 'mkdir build', - 'cd build', - 'cmake .. -DCMAKE_CXX_FLAGS=-fcolor-diagnostics -DCMAKE_BUILD_TYPE='+build_type+' ' + - (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + cmake_extra, - 'VERBOSE=1 make -j' + jobs, - './test/testAll --use-colour yes', - ] + extra_cmds, + dnf(rpmarch) + 'distro-sync', + dnf(rpmarch) + 'install rpm-build git-archive-all dnf-plugins-core ccache', + dnf(rpmarch) + 'config-manager --add-repo https://rpm.oxen.io/fedora/oxen.repo', + 'pkg_src_base="$(rpm -q --queryformat=\'%{NAME}-%{VERSION}\n\' --specfile SPECS/lokinet.spec | head -n 1)"', + 'git-archive-all --prefix $pkg_src_base/ SOURCES/$pkg_src_base.src.tar.gz', + dnf(rpmarch) + 'builddep --spec SPECS/lokinet.spec', + 'if [ -n "$CCACHE_DIR" ]; then mkdir -pv ~/.cache; ln -sv "$CCACHE_DIR" ~/.cache/ccache; fi', + 'rpmbuild --define "_topdir $(pwd)" -bb SPECS/lokinet.spec', + './SPECS/ci-upload.sh ' + distro + ' ' + rpmarch, + ], } ] }; - [ - { - name: 'lint check', - kind: 'pipeline', - type: 'docker', - steps: [{ - name: 'build', image: 'registry.oxen.rocks/lokinet-ci-lint', - commands: [ - 'echo "Building on ${DRONE_STAGE_MACHINE}"', - apt_get_quiet + ' update', - apt_get_quiet + ' install -y eatmydata', - 'eatmydata ' + apt_get_quiet + ' install -y git clang-format-11', - './contrib/ci/drone-format-verify.sh'] - }] - }, - - // Various debian builds - debian_pipeline("Debian sid (amd64)", "debian:sid"), - debian_pipeline("Debian sid/Debug (amd64)", "debian:sid", build_type='Debug'), - debian_pipeline("Debian sid/clang-11 (amd64)", docker_base+'debian-sid', deps='clang-11 '+default_deps_nocxx, - cmake_extra='-DCMAKE_C_COMPILER=clang-11 -DCMAKE_CXX_COMPILER=clang++-11 '), - debian_pipeline("Debian buster (i386)", "i386/debian:buster", cmake_extra='-DDOWNLOAD_SODIUM=ON'), - debian_pipeline("Ubuntu focal (amd64)", docker_base+'ubuntu-focal'), - debian_pipeline("Ubuntu bionic (amd64)", "ubuntu:bionic", deps='g++-8 ' + default_deps_nocxx, - cmake_extra='-DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8', loki_repo=true), - - // ARM builds (ARM64 and armhf) - debian_pipeline("Debian sid (ARM64)", "debian:sid", arch="arm64", jobs=4), - debian_pipeline("Debian buster (armhf)", "arm32v7/debian:buster", arch="arm64", cmake_extra='-DDOWNLOAD_SODIUM=ON', jobs=4), - // Static armhf build (gets uploaded) - debian_pipeline("Static (buster armhf)", "arm32v7/debian:buster", arch="arm64", deps='g++ python3-dev automake libtool', - cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' + - '-DCMAKE_CXX_FLAGS="-march=armv7-a+fp" -DCMAKE_C_FLAGS="-march=armv7-a+fp" -DNATIVE_BUILD=OFF ' + - '-DWITH_SYSTEMD=OFF', - extra_cmds=[ - '../contrib/ci/drone-check-static-libs.sh', - 'UPLOAD_OS=linux-armhf ../contrib/ci/drone-static-upload.sh' - ], - jobs=4), - // android apk builder - apk_builder("android apk", "registry.oxen.rocks/lokinet-ci-android", extra_cmds=['UPLOAD_OS=android ./contrib/ci/drone-static-upload.sh']), - - // Windows builds (x64) - windows_cross_pipeline("Windows (amd64)", docker_base+'debian-win32-cross', - toolchain='64', extra_cmds=[ - './contrib/ci/drone-static-upload.sh' - ]), - - // Static build (on bionic) which gets uploaded to builds.lokinet.dev: - debian_pipeline("Static (bionic amd64)", docker_base+'ubuntu-bionic', deps='g++-8 python3-dev automake libtool', lto=true, - cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ' + - '-DCMAKE_CXX_FLAGS="-march=x86-64 -mtune=haswell" -DCMAKE_C_FLAGS="-march=x86-64 -mtune=haswell" -DNATIVE_BUILD=OFF ' + - '-DWITH_SYSTEMD=OFF', - extra_cmds=[ - '../contrib/ci/drone-check-static-libs.sh', - '../contrib/ci/drone-static-upload.sh' - ]), - - // integration tests - debian_pipeline("Router Hive", "ubuntu:focal", deps='python3-dev python3-pytest python3-pybind11 ' + default_deps, - cmake_extra='-DWITH_HIVE=ON'), - - // Deb builds: - deb_builder("debian:sid", "sid", "debian/sid"), - deb_builder("debian:buster", "buster", "debian/buster"), - deb_builder("ubuntu:focal", "focal", "ubuntu/focal"), - deb_builder("debian:sid", "sid", "debian/sid", arch='arm64'), - - // Macos builds: - mac_builder('macOS (Release)'), - mac_builder('macOS (Debug)', build_type='Debug'), - mac_builder('macOS (Static)', cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DDOWNLOAD_SODIUM=FORCE -DDOWNLOAD_CURL=FORCE -DDOWNLOAD_UV=FORCE', - extra_cmds=[ - '../contrib/ci/drone-check-static-libs.sh', - '../contrib/ci/drone-static-upload.sh' - ]), + rpm_pipeline(distro_docker), + rpm_pipeline("arm64v8/" + distro_docker, buildarch='arm64', rpmarch="aarch64", jobs=4) ] diff --git a/SOURCES/default-dns.patch b/SOURCES/default-dns.patch new file mode 100644 index 000000000..66beac950 --- /dev/null +++ b/SOURCES/default-dns.patch @@ -0,0 +1,16 @@ +diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp +index 78d152602..8b07b0cec 100644 +--- a/llarp/config/config.cpp ++++ b/llarp/config/config.cpp +@@ -703,7 +703,10 @@ namespace llarp + // can bind to other 127.* IPs to avoid conflicting with something else that may be listening on + // 127.0.0.1:53. + #ifdef __linux__ +- constexpr Default DefaultDNSBind{"127.3.2.1:53"}; ++ // Fedora's systemd-resolved seems unable to connect to 127.3.2.1 for unknown reasons, ++ // however since systemd-resolved is perfectly happy with a different port so listen on ++ // localhost:1053 as a workaround. ++ constexpr Default DefaultDNSBind{"127.0.0.1:1053"}; + #else + constexpr Default DefaultDNSBind{"127.0.0.1:53"}; + #endif diff --git a/SOURCES/default-upstream-dns.patch b/SOURCES/default-upstream-dns.patch new file mode 100644 index 000000000..1b780a0c1 --- /dev/null +++ b/SOURCES/default-upstream-dns.patch @@ -0,0 +1,23 @@ +commit 73f0432b2873d3af91a0c8cf2dd107463318b9d9 +Author: Jason Rhinelander +Date: Wed Aug 11 18:24:11 2021 -0300 + + Fix default upstream DNS not working + + The default upstream DNS was being set to 1.1.1.1:0, which doesn't work. + This fixes it to also set the port so that default upstream resolution + (i.e. with an empty config) works again. + +diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp +index bef3e521f..721a479df 100644 +--- a/llarp/config/config.cpp ++++ b/llarp/config/config.cpp +@@ -711,6 +711,8 @@ namespace llarp + // Default, but if we get any upstream (including upstream=, i.e. empty string) we clear it + constexpr Default DefaultUpstreamDNS{"1.1.1.1"}; + m_upstreamDNS.emplace_back(DefaultUpstreamDNS.val); ++ if (!m_upstreamDNS.back().getPort()) ++ m_upstreamDNS.back().setPort(53); + + conf.defineOption( + "dns", diff --git a/SOURCES/dnssec-lokinet.negative b/SOURCES/dnssec-lokinet.negative new file mode 100644 index 000000000..0e30fbb63 --- /dev/null +++ b/SOURCES/dnssec-lokinet.negative @@ -0,0 +1,2 @@ +loki +snode diff --git a/SOURCES/lokinet.service b/SOURCES/lokinet.service new file mode 100644 index 000000000..3b0e990d0 --- /dev/null +++ b/SOURCES/lokinet.service @@ -0,0 +1,21 @@ +[Unit] +Description=LokiNET: Anonymous Network layer thingydoo, client +AssertFileNotEmpty=/var/lib/lokinet/bootstrap.signed +Wants=network-online.target +After=network-online.target + +[Service] +User=_lokinet +Type=notify +WatchdogSec=30s +SyslogIdentifier=lokinet +WorkingDirectory=/var/lib/lokinet +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE +ExecStart=/usr/bin/lokinet /var/lib/lokinet/lokinet.ini +Environment=LOKINET_NETID=lokinet +Restart=always +RestartSec=5s + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/version-as-rpm-version.patch b/SOURCES/version-as-rpm-version.patch new file mode 100644 index 000000000..868783b3b --- /dev/null +++ b/SOURCES/version-as-rpm-version.patch @@ -0,0 +1,28 @@ +From: Jason Rhinelander +Date: Fri, 13 Dec 2019 17:23:41 -0400 +Subject: Pass debian version as GIT_VERSION + +--- + cmake/Version.cmake | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/cmake/Version.cmake b/cmake/Version.cmake +index 45037a0..d9fdaef 100644 +--- a/cmake/Version.cmake ++++ b/cmake/Version.cmake +@@ -1,4 +1,8 @@ + ++if(GIT_VERSION) ++ set(VERSIONTAG "${GIT_VERSION}") ++ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp") ++else() + find_package(Git QUIET) + set(GIT_INDEX_FILE "${PROJECT_SOURCE_DIR}/.git/index") + if(EXISTS ${GIT_INDEX_FILE} AND ( GIT_FOUND OR Git_FOUND) ) +@@ -18,5 +22,6 @@ else() + set(VERSIONTAG "nogit") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp") + endif() ++endif() + + add_custom_target(genversion DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp") diff --git a/SPECS/ci-upload.sh b/SPECS/ci-upload.sh new file mode 100755 index 000000000..dec261cef --- /dev/null +++ b/SPECS/ci-upload.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# Script used with Drone CI to upload build artifacts (because specifying all this in +# .drone.jsonnet is too painful). + + + +set -o errexit + +if [ -z "$SSH_KEY" ]; then + echo -e "\n\n\n\e[31;1mUnable to upload artifact: SSH_KEY not set\e[0m" + # Just warn but don't fail, so that this doesn't trigger a build failure for untrusted builds + exit 0 +fi + +echo "$SSH_KEY" >ssh_key + +set -o xtrace # Don't start tracing until *after* we write the ssh key + +chmod 600 ssh_key + +distro="${1:-unknown}" +rpmarch="${2:-x86_64}" + +base="rpm-$distro-$(date --date=@$DRONE_BUILD_CREATED +%Y%m%dT%H%M%SZ)-${DRONE_COMMIT:0:9}" + +br="${DRONE_BRANCH// /_}" +br="${br//\//-}" +upload_to="builds.lokinet.dev/${DRONE_REPO// /_}/$br/$base" + +put= +for rpm in RPMS/${rpmarch}/*.rpm; do + put+=$'\n'"put $rpm $upload_to" + + echo -e "\n\n\e[35;1m$rpm contents:\e[0m" + rpm --query --list $rpm +done + +# sftp doesn't have any equivalent to mkdir -p, so we have to split the above up into a chain of +# -mkdir a/, -mkdir a/b/, -mkdir a/b/c/, ... commands. The leading `-` allows the command to fail +# without error. +upload_dirs=(${upload_to//\// }) +mkdirs= +dir_tmp="" +for p in "${upload_dirs[@]}"; do + dir_tmp="$dir_tmp$p/" + mkdirs="$mkdirs +-mkdir $dir_tmp" +done + +sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@builds.lokinet.dev </dev/null; then + groupadd --system _loki +fi +if ! getent passwd _lokinet >/dev/null; then + useradd --badnames --system --home-dir /var/lib/lokinet --group _loki --comment "Lokinet system user" _lokinet +fi + +# Make sure the _lokinet user is part of the _loki group (in case it already existed) +if ! id -Gn _lokinet | grep -qw _loki; then + usermod _lokinet -g _loki +fi + +%post + +datadir=/var/lib/lokinet +mkdir -p $datadir +chown _lokinet:_loki $datadir + +if ! [ -e /var/lib/lokinet/bootstrap.signed ]; then + /usr/bin/lokinet-bootstrap lokinet /var/lib/lokinet/bootstrap.signed + chown _lokinet:_loki /var/lib/lokinet/bootstrap.signed +fi + +if ! [ -e /etc/loki/lokinet.ini ]; then + mkdir -p /etc/loki + /usr/bin/lokinet -g /etc/loki/lokinet.ini + chmod 640 /etc/loki/lokinet.ini + chown _lokinet:_loki /etc/loki/lokinet.ini + ln -sf /etc/loki/lokinet.ini /var/lib/lokinet/lokinet.ini +fi + +%systemd_post lokinet.service + +%preun +%systemd_preun lokinet.service + +%postun +%systemd_postun lokinet.service + +%changelog +* Wed Aug 11 2021 Jason Rhinelander - 0.9.5-5 +- Apply default upstream dns patch from PR #1715 + +* Wed Aug 11 2021 Jason Rhinelander - 0.9.5-4 +- Change default DNS address to 127.0.0.1:1053 because systemd-resolved has trouble with 127.3.2.1 + for unknown reasons. +- Make it work + +* Tue Aug 10 2021 Jason Rhinelander - 0.9.5-3 +- Updated for rpm.oxen.io packaging +- Split into lokinet/lokinet-bin/lokinet-monitor packages + +* Thu Jul 22 2021 Technical Tumbleweed (necro_nemesis@hotmail.com) Lokinet 0.9.5 +- Build with systemd-resolved and binary lokinet-bootstrap + +* Sun Mar 07 2021 Technical Tumbleweed (necro_nemesis@hotmail.com) Lokinet 0.8.2 +- First Lokinet RPM