5 Commits
Author SHA1 Message Date
Bendik Aagaard LynghaugandClaude Opus 5.5 c9dbc4a576 release v0.1.3
ci / quality (push) Successful in 2m40s
release / build (aarch64-unknown-linux-gnu) (push) Successful in 2m19s
release / build (x86_64-unknown-linux-gnu) (push) Successful in 1m58s
release / publish (push) Successful in 48s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:45:45 +02:00
Bendik Aagaard LynghaugandClaude Opus 5.5 3382a42b99 varde in the uhhm Arch registry
dist/arch/PKGBUILD packages the release tarballs: system and per-user
units, the per-user socket on for every session (as PipeWire's is; the
daemon starts only when an app connects), config and man pages. The
release workflow publishes it for x86_64 and aarch64 from klokka.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 12:43:46 +02:00
Bendik Aagaard LynghaugandClaude Opus 5.5 b7752da694 The ctl roundtrip test sets relay_url
ci / quality (push) Successful in 2m36s
Config grew relay_url in 98db877; the test's literal never did, so CI
had not compiled since.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MznPTC31w5mSUK4XMehcpi
2026-10-02 09:27:44 +02:00
Bendik Aagaard LynghaugandClaude Opus 4.8 4ff007e403 ci: v4 builtin:checkout in ci + release (no Node, no token-in-URL)
ci / quality (push) Failing after 53s
builtin:checkout is native Go, so the rust:1 container needs no Node and the
job token no longer rides in the fetch URL (was visible in process args).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-28 13:39:06 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 98db87703e Config: relay_url for a self-hosted iroh relay (RelayMode::Custom)
ci / quality (push) Failing after 56s
release / build (aarch64-unknown-linux-gnu) (push) Successful in 2m17s
release / build (x86_64-unknown-linux-gnu) (push) Successful in 1m58s
The sovereign hole-punch hook redoal's ADR-0021 milestone 3 needs: when
relay_url is set (VARDE_RELAY_URL or the config file), the endpoint
coordinates NAT traversal through the operator's own iroh relay instead
of n0's or none — takes precedence over wan_upload's n0 defaults. None
keeps the existing LAN-only / n0 behaviour, so nothing changes by default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 23:15:28 +02:00
10 changed files with 129 additions and 28 deletions
+3 -10
View File
@@ -12,17 +12,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: rust:1 container: rust:1
steps: steps:
# Not the checkout action: it runs on node, which the rust:1 job # v4 built-in checkout: native Go, needs no Node (rust:1 has none) and
# container doesn't have and this runner doesn't inject (the v5 # keeps the token out of a fetch URL. Uses the job token for the private repo.
# run died with 'exec: "node": not found'). Plain git needs
# neither, and the token URL works against the private repo.
- name: checkout - name: checkout
run: | uses: builtin:checkout
git init -q .
git fetch --depth 1 \
"https://gitea:${{ secrets.GITHUB_TOKEN }}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" \
"$GITHUB_SHA"
git checkout -q FETCH_HEAD
- name: rustfmt - name: rustfmt
run: | run: |
rustup component add rustfmt rustup component add rustfmt
+45 -10
View File
@@ -19,17 +19,10 @@ jobs:
matrix: matrix:
target: [x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu] target: [x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu]
steps: steps:
# Not the checkout action: it runs on node, which the rust:1 job # v4 built-in checkout: native Go, needs no Node (rust:1 has none) and
# container doesn't have and this runner doesn't inject (the v5 # keeps the token out of a fetch URL. Uses the job token for the private repo.
# run died with 'exec: "node": not found'). Plain git needs
# neither, and the token URL works against the private repo.
- name: checkout - name: checkout
run: | uses: builtin:checkout
git init -q .
git fetch --depth 1 \
"https://gitea:${{ secrets.GITHUB_TOKEN }}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" \
"$GITHUB_SHA"
git checkout -q FETCH_HEAD
- name: install tooling - name: install tooling
run: | run: |
@@ -96,3 +89,45 @@ jobs:
-H "Authorization: token $TOKEN" \ -H "Authorization: token $TOKEN" \
-F "attachment=@$f" -F "attachment=@$f"
done done
# The uhhm Arch registry (pacman.conf: .../api/packages/bl/arch/uhhm),
# from the tarballs just released. On klokka: an Arch host with
# makepkg. REGISTRY_TOKEN is the uhhm org's write:package token.
publish:
needs: build
runs-on: aarch64
steps:
- name: checkout
uses: builtin:checkout
- name: fetch tarballs and pin the PKGBUILD
run: |
set -eu
TAG="${GITHUB_REF_NAME}"
VERSION="${TAG#v}"
BASE="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/download/${TAG}"
cd dist/arch
for t in x86_64 aarch64; do
f="varde-${VERSION}-${t}-unknown-linux-gnu.tar.gz"
# The repo is private: anonymous release downloads 404.
curl -fsSL -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -o "$f" "$BASE/$f"
done
SUM_X86=$(sha256sum "varde-${VERSION}-x86_64-unknown-linux-gnu.tar.gz" | cut -d' ' -f1)
SUM_AARCH=$(sha256sum "varde-${VERSION}-aarch64-unknown-linux-gnu.tar.gz" | cut -d' ' -f1)
sed -i "s/^pkgver=.*/pkgver=${VERSION}/" PKGBUILD
sed -i "s/sha256sums_x86_64=('.*')/sha256sums_x86_64=('${SUM_X86}')/" PKGBUILD
sed -i "s/sha256sums_aarch64=('.*')/sha256sums_aarch64=('${SUM_AARCH}')/" PKGBUILD
- name: publish to the uhhm Arch registry
run: |
set -eu
if [ -z "${{ secrets.REGISTRY_TOKEN }}" ]; then
echo "::error::REGISTRY_TOKEN not set - add it to publish"
exit 1
fi
cd dist/arch
for carch in aarch64 x86_64; do
pkgfile=$(CARCH="$carch" makepkg --packagelist | tail -1)
CARCH="$carch" makepkg -f --nodeps --noconfirm
curl --fail-with-body --user "bl:${{ secrets.REGISTRY_TOKEN }}" \
--upload-file "$pkgfile" \
"${GITHUB_SERVER_URL}/api/packages/bl/arch/uhhm"
done
+1
View File
@@ -1,2 +1,3 @@
target/ target/
.claude/ .claude/
.DS_Store
Generated
+3 -3
View File
@@ -4491,7 +4491,7 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "varde-ctl" name = "varde-ctl"
version = "0.1.1" version = "0.1.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -4504,7 +4504,7 @@ dependencies = [
[[package]] [[package]]
name = "varde-daemon" name = "varde-daemon"
version = "0.1.1" version = "0.1.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"iroh", "iroh",
@@ -4529,7 +4529,7 @@ dependencies = [
[[package]] [[package]]
name = "varde-proto" name = "varde-proto"
version = "0.1.1" version = "0.1.3"
dependencies = [ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
+1 -1
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["varde-proto", "varde-daemon", "varde-ctl"] members = ["varde-proto", "varde-daemon", "varde-ctl"]
[workspace.package] [workspace.package]
version = "0.1.1" version = "0.1.3"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://project.uhhm.no/uhhm/varde" repository = "https://project.uhhm.no/uhhm/varde"
+41
View File
@@ -0,0 +1,41 @@
# The uhhm Arch registry package, built by .gitea/workflows/release.yml
# from the release tarballs (the CI pins pkgver and the sums). For a
# from-source build (AUR style) see ../PKGBUILD.
pkgname=varde
pkgver=0.1.3
pkgrel=1
pkgdesc="Content-addressed blob mirror daemon on iroh"
arch=('x86_64' 'aarch64')
url="https://project.uhhm.no/uhhm/varde"
license=('MIT OR Apache-2.0')
depends=('gcc-libs' 'glibc')
optdepends=('networkmanager: metered-connection detection')
backup=('etc/varde/config.toml')
install=varde.install
options=('!strip' '!debug')
source_x86_64=("varde-${pkgver}-x86_64-unknown-linux-gnu.tar.gz")
source_aarch64=("varde-${pkgver}-aarch64-unknown-linux-gnu.tar.gz")
sha256sums_x86_64=('SKIP')
sha256sums_aarch64=('SKIP')
package() {
cd "varde-${pkgver}-${CARCH}-unknown-linux-gnu"
install -Dm755 bin/varde-daemon "$pkgdir/usr/bin/varde-daemon"
install -Dm755 bin/varde-ctl "$pkgdir/usr/bin/varde-ctl"
install -Dm644 systemd/varde.service "$pkgdir/usr/lib/systemd/system/varde.service"
install -Dm644 systemd/varde.socket "$pkgdir/usr/lib/systemd/system/varde.socket"
install -Dm644 systemd/user/varde.service "$pkgdir/usr/lib/systemd/user/varde.service"
install -Dm644 systemd/user/varde.socket "$pkgdir/usr/lib/systemd/user/varde.socket"
# The per-user socket is on for every session, the way PipeWire's
# is: it costs nothing until an app connects, and then the daemon
# starts by socket activation. The system daemon stays opt-in.
install -dm755 "$pkgdir/usr/lib/systemd/user/sockets.target.wants"
ln -s ../varde.socket "$pkgdir/usr/lib/systemd/user/sockets.target.wants/varde.socket"
install -Dm644 config.toml.example "$pkgdir/etc/varde/config.toml"
install -Dm644 man/varde.8 "$pkgdir/usr/share/man/man8/varde.8"
install -Dm644 man/varde-ctl.1 "$pkgdir/usr/share/man/man1/varde-ctl.1"
install -Dm644 LICENSE-MIT "$pkgdir/usr/share/licenses/$pkgname/LICENSE-MIT"
install -Dm644 LICENSE-APACHE "$pkgdir/usr/share/licenses/$pkgname/LICENSE-APACHE"
}
+10
View File
@@ -0,0 +1,10 @@
post_install() {
echo ":: The per-user varde socket starts with your next login."
echo " To use it now: systemctl --user daemon-reload && systemctl --user start varde.socket"
echo " The system daemon is opt-in: systemctl enable --now varde.socket"
}
post_upgrade() {
echo ":: Restart a running daemon to pick up the new version:"
echo " systemctl --user try-restart varde.service (and/or: systemctl try-restart varde.service)"
}
+1
View File
@@ -18,6 +18,7 @@ fn ctl_status_round_trips() {
max_download_bytes_per_sec: 0, max_download_bytes_per_sec: 0,
discovery: false, discovery: false,
wan_upload: false, wan_upload: false,
relay_url: None,
gc_interval_secs: 300, gc_interval_secs: 300,
}; };
+11
View File
@@ -24,6 +24,12 @@ pub struct Config {
/// Whether any WAN (non-link-local) upload is permitted. Default off: /// Whether any WAN (non-link-local) upload is permitted. Default off:
/// LAN-only posture with zero WAN upload. /// LAN-only posture with zero WAN upload.
pub wan_upload: bool, pub wan_upload: bool,
/// A self-hosted iroh relay URL for NAT traversal. When set, the
/// endpoint uses this relay (`RelayMode::Custom`) instead of n0's
/// or none — the sovereign hole-punch coordinator an embedder runs
/// on its own infrastructure. Takes precedence over `wan_upload`'s
/// n0 defaults. `None` keeps the LAN-only / n0 behaviour.
pub relay_url: Option<String>,
/// Interval of the store's built-in garbage collector in seconds. /// Interval of the store's built-in garbage collector in seconds.
/// Since iroh-blobs 0.103 there is no on-demand gc; unpinned blobs /// Since iroh-blobs 0.103 there is no on-demand gc; unpinned blobs
/// are swept by this loop. /// are swept by this loop.
@@ -41,6 +47,7 @@ struct FileConfig {
max_download_bytes_per_sec: Option<u64>, max_download_bytes_per_sec: Option<u64>,
discovery: Option<bool>, discovery: Option<bool>,
wan_upload: Option<bool>, wan_upload: Option<bool>,
relay_url: Option<String>,
gc_interval_secs: Option<u64>, gc_interval_secs: Option<u64>,
} }
@@ -176,6 +183,10 @@ impl Config {
wan_upload: env_bool("VARDE_WAN_UPLOAD")? wan_upload: env_bool("VARDE_WAN_UPLOAD")?
.or(file.wan_upload) .or(file.wan_upload)
.unwrap_or(false), .unwrap_or(false),
relay_url: std::env::var("VARDE_RELAY_URL")
.ok()
.filter(|s| !s.is_empty())
.or(file.relay_url),
gc_interval_secs: env_u64("VARDE_GC_INTERVAL")? gc_interval_secs: env_u64("VARDE_GC_INTERVAL")?
.or(file.gc_interval_secs) .or(file.gc_interval_secs)
.unwrap_or(300), .unwrap_or(300),
+13 -4
View File
@@ -62,10 +62,19 @@ impl Transfer {
metered: MeteredState, metered: MeteredState,
) -> Result<Transfer> { ) -> Result<Transfer> {
let secret = load_or_create_secret(&config.store_dir.join("secret.key"))?; let secret = load_or_create_secret(&config.store_dir.join("secret.key"))?;
// With wan_upload the n0 defaults apply (their relays and DNS // A self-hosted relay (relay_url) is the sovereign hole-punch
// lookup, matching the pre-1.0 default relay mode); otherwise the // path and takes precedence: the endpoint coordinates through
// endpoint gets no external services at all. // the operator's own iroh relay, never n0's. Failing that,
let builder = if config.wan_upload { // wan_upload opts into n0's relays + DNS; otherwise the endpoint
// gets no external services at all (LAN-only).
let builder = if let Some(url) = &config.relay_url {
let relay_url: iroh::RelayUrl = url
.parse()
.with_context(|| format!("parsing relay_url {url:?}"))?;
let relay_map = iroh::RelayMap::from(relay_url);
Endpoint::builder(iroh::endpoint::presets::Minimal)
.relay_mode(RelayMode::Custom(relay_map))
} else if config.wan_upload {
Endpoint::builder(iroh::endpoint::presets::N0) Endpoint::builder(iroh::endpoint::presets::N0)
} else { } else {
Endpoint::builder(iroh::endpoint::presets::Minimal).relay_mode(RelayMode::Disabled) Endpoint::builder(iroh::endpoint::presets::Minimal).relay_mode(RelayMode::Disabled)