14 Commits
Author SHA1 Message Date
Bendik Aagaard LynghaugandClaude Fable 5.1 a1776dbbed release v0.1.8
ci / quality (push) Failing after 9s
release / build (aarch64-unknown-linux-gnu) (push) Successful in 2m31s
release / build (x86_64-unknown-linux-gnu) (push) Successful in 2m2s
release / publish (push) Successful in 47s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 13:55:18 +02:00
Bendik Aagaard LynghaugandClaude Fable 5.1 ddfb983108 iroh 1.3.0 in the lock, this time
0.1.7's bump never reached Cargo.lock: the update ran through a pipe
that closed early and cargo died before writing it. Verified in the
lock now; the suite passes on 1.3.0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 13:53:41 +02:00
Bendik Aagaard LynghaugandClaude Fable 5.1 0f2b0504d5 release v0.1.7
ci / quality (push) Failing after 5s
release / build (aarch64-unknown-linux-gnu) (push) Successful in 2m30s
release / build (x86_64-unknown-linux-gnu) (push) Successful in 2m16s
release / publish (push) Successful in 46s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 13:45:20 +02:00
Bendik Aagaard LynghaugandClaude Fable 5.1 08fcba749a iroh 1.3, and a socket that survives a service restart
iroh 1.0.2 -> 1.3.0 (relay reconnects that keep answering, rate-limit
notices from relays, the n0 DNS resolver with configurable fallbacks,
relay connection metrics), iroh-mdns-address-lookup 0.4 -> 0.6,
n0-future 0.1 -> 0.3. No code changes were needed; the suite passes.

varde.service now keeps /run/varde across restarts
(RuntimeDirectoryPreserve=yes): stopping the service removed the
directory and with it the socket unit's file, leaving varde.socket
"active" on an unlinked path after any service restart. The install
notes say to restart the socket, which cycles both.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 13:43:43 +02:00
Bendik Aagaard LynghaugandClaude Opus 5.5 6ca859c691 release v0.1.6
ci / quality (push) Failing after 5s
release / build (aarch64-unknown-linux-gnu) (push) Successful in 2m25s
release / build (x86_64-unknown-linux-gnu) (push) Successful in 2m0s
release / publish (push) Successful in 48s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:30:44 +02:00
Bendik Aagaard LynghaugandClaude Opus 5.5 c2b9167e1e Pushes name the peer by ticket, and an allowance can confirm its push
PeerAllowPush and Push take a blob ticket the peer issued as well as a
NodeId; the ticket's addresses are remembered, so the push dials
directly. A peer allowed to push may observe too: the sender confirms a
push by observing it, and without that every allowance-only push
reported failure though it had landed. The allowance test now fails
instead of skipping on anything but an unreachable peer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:29:03 +02:00
Bendik Aagaard LynghaugandClaude Opus 5.5 4d9985c9cb release v0.1.5
ci / quality (push) Failing after 5s
release / build (aarch64-unknown-linux-gnu) (push) Successful in 2m26s
release / build (x86_64-unknown-linux-gnu) (push) Successful in 2m18s
release / publish (push) Successful in 47s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:24:51 +02:00
Bendik Aagaard LynghaugandClaude Opus 5.5 2063d11890 PeerAllowPush: consent to exchange pushes, for a while, and nothing else
PeerTrust syncs a peer's pins and serves it everything - too much to
grant a stranger. An app that wants two peers sharing something (two
phones at the same place) to hand each other new content allows pushes
for ttl_secs (at most a day): both sides may push, the receiver pins,
and the allowance lapses on its own. Asking again extends it;
PeerUntrust ends it. varde-ctl peer allow-push.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:21:50 +02:00
Bendik Aagaard LynghaugandClaude Opus 5.5 0006a188e3 release v0.1.4
ci / quality (push) Failing after 8s
release / build (aarch64-unknown-linux-gnu) (push) Successful in 2m26s
release / build (x86_64-unknown-linux-gnu) (push) Successful in 2m6s
release / publish (push) Successful in 48s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:08:52 +02:00
Bendik Aagaard LynghaugandClaude Opus 5.5 687af8bd2a Put and Read: content on the socket, for clients that share no files
The system daemon runs as its own user behind ProtectHome, so it can
neither read a file a desktop app wrote for Add nor write one back for
Materialize - redoal on the system varde failed every get with EACCES.
Put imports base64 bytes carried in the request and Read returns a raw
blob's bytes in the reply, both up to MAX_INLINE_BYTES (16 MiB).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:08:50 +02:00
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
18 changed files with 600 additions and 411 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
Generated
+149 -371
View File
@@ -431,9 +431,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]] [[package]]
name = "cfg_aliases" name = "cfg_aliases"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
[[package]] [[package]]
name = "chacha20" name = "chacha20"
@@ -581,16 +581,6 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.10.1" version = "0.10.1"
@@ -631,24 +621,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "crossbeam-channel"
version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.22" version = "0.8.22"
@@ -722,41 +694,6 @@ dependencies = [
"syn 2.0.118", "syn 2.0.118",
] ]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.118",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn 2.0.118",
]
[[package]] [[package]]
name = "data-encoding" name = "data-encoding"
version = "2.11.0" version = "2.11.0"
@@ -814,65 +751,13 @@ version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.118",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.118",
]
[[package]]
name = "derive_more"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
dependencies = [
"derive_more-impl 1.0.0",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "2.1.1" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
dependencies = [ dependencies = [
"derive_more-impl 2.1.1", "derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.118",
"unicode-xid",
] ]
[[package]] [[package]]
@@ -966,12 +851,6 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "either"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
[[package]] [[package]]
name = "embedded-io" name = "embedded-io"
version = "0.4.0" version = "0.4.0"
@@ -1406,35 +1285,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hickory-net"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2295ed2f9c31e471e1428a8f88a3f0e1f4b27c15049592138d1eebe9c35b183"
dependencies = [
"async-trait",
"bytes",
"cfg-if",
"data-encoding",
"futures-channel",
"futures-io",
"futures-util",
"h2",
"hickory-proto",
"http",
"idna",
"ipnet",
"jni 0.22.4",
"rand 0.10.2",
"rustls",
"thiserror 2.0.18",
"tinyvec",
"tokio",
"tokio-rustls",
"tracing",
"url",
]
[[package]] [[package]]
name = "hickory-proto" name = "hickory-proto"
version = "0.26.1" version = "0.26.1"
@@ -1446,43 +1296,13 @@ dependencies = [
"ipnet", "ipnet",
"jni 0.22.4", "jni 0.22.4",
"once_cell", "once_cell",
"prefix-trie",
"rand 0.10.2", "rand 0.10.2",
"ring",
"thiserror 2.0.18", "thiserror 2.0.18",
"tinyvec", "tinyvec",
"tracing", "tracing",
"url", "url",
] ]
[[package]]
name = "hickory-resolver"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d58d28879ceecde6607729660c2667a081ccdc082e082675042793960f178c"
dependencies = [
"cfg-if",
"futures-util",
"hickory-net",
"hickory-proto",
"ipconfig",
"ipnet",
"jni 0.22.4",
"moka",
"ndk-context",
"once_cell",
"parking_lot",
"rand 0.10.2",
"resolv-conf",
"rustls",
"smallvec",
"system-configuration",
"thiserror 2.0.18",
"tokio",
"tokio-rustls",
"tracing",
]
[[package]] [[package]]
name = "http" name = "http"
version = "1.4.2" version = "1.4.2"
@@ -1703,12 +1523,6 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "identity-hash" name = "identity-hash"
version = "0.1.0" version = "0.1.0"
@@ -1802,15 +1616,12 @@ name = "ipnet"
version = "2.12.0" version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "iroh" name = "iroh"
version = "1.0.2" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fca9b4b462c343ff88fc0af4096c186f939b602a0bc08723536ef2c31c93971" checksum = "885787b892b5e2507c701f132ecbd45d2bad4bbb75157a19427087c16dadb833"
dependencies = [ dependencies = [
"backon", "backon",
"blake3", "blake3",
@@ -1818,11 +1629,10 @@ dependencies = [
"cfg_aliases", "cfg_aliases",
"ctutils", "ctutils",
"data-encoding", "data-encoding",
"derive_more 2.1.1", "derive_more",
"ed25519-dalek", "ed25519-dalek",
"futures-util", "futures-util",
"getrandom 0.4.3", "getrandom 0.4.3",
"hickory-resolver",
"http", "http",
"ipnet", "ipnet",
"iroh-base", "iroh-base",
@@ -1830,7 +1640,7 @@ dependencies = [
"iroh-metrics", "iroh-metrics",
"iroh-relay", "iroh-relay",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
"n0-watcher", "n0-watcher",
"netwatch", "netwatch",
"noq", "noq",
@@ -1859,14 +1669,14 @@ dependencies = [
[[package]] [[package]]
name = "iroh-base" name = "iroh-base"
version = "1.0.3" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6be73e16ee21c923aca9b3121aaa0db936f7c7ecc156ff47b8dac944c68d59a8" checksum = "6ae9092be76c9f5429776ab3394f5a5a78dc19dc2524267e884a5b9781546c3f"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"data-encoding", "data-encoding",
"data-encoding-macro", "data-encoding-macro",
"derive_more 2.1.1", "derive_more",
"ed25519-dalek", "ed25519-dalek",
"getrandom 0.4.3", "getrandom 0.4.3",
"n0-error", "n0-error",
@@ -1889,7 +1699,7 @@ dependencies = [
"chrono", "chrono",
"constant_time_eq", "constant_time_eq",
"data-encoding", "data-encoding",
"derive_more 2.1.1", "derive_more",
"genawaiter", "genawaiter",
"getrandom 0.4.3", "getrandom 0.4.3",
"hex", "hex",
@@ -1901,7 +1711,7 @@ dependencies = [
"iroh-util", "iroh-util",
"irpc", "irpc",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
"nested_enum_utils", "nested_enum_utils",
"noq", "noq",
"postcard", "postcard",
@@ -1919,18 +1729,17 @@ dependencies = [
[[package]] [[package]]
name = "iroh-dns" name = "iroh-dns"
version = "1.0.2" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516e4eedc38e33ab69a6bd325520332dc3d67b25454e2d590ebb84a25240dd9a" checksum = "80276761b56e904e26ea71d0863beef0bb8d5ab6b639cbd1338e4b615209e3a1"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"cfg_aliases", "cfg_aliases",
"derive_more 2.1.1", "derive_more",
"hickory-resolver",
"iroh-base", "iroh-base",
"n0-dns-resolver",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
"ndk-context",
"portable-atomic", "portable-atomic",
"rand 0.10.2", "rand 0.10.2",
"rustls", "rustls",
@@ -1956,17 +1765,17 @@ dependencies = [
[[package]] [[package]]
name = "iroh-mdns-address-lookup" name = "iroh-mdns-address-lookup"
version = "0.4.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c1f941695731bc3999d8c13d7e94d50e229427da81ac957eec3818264f17559" checksum = "7afb03c296022fcb724a7bb76dbb8dcca6aac4f4eb65df161e084b7276c93d8c"
dependencies = [ dependencies = [
"data-encoding", "data-encoding",
"derive_more 2.1.1", "derive_more",
"futures-util", "futures-util",
"iroh", "iroh",
"iroh-base", "iroh-base",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
"n0-watcher", "n0-watcher",
"swarm-discovery", "swarm-discovery",
"tokio", "tokio",
@@ -1976,9 +1785,9 @@ dependencies = [
[[package]] [[package]]
name = "iroh-metrics" name = "iroh-metrics"
version = "1.0.1" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291065721ad7c477b972e581bbc528df031dc8eb5e39fe1ff3300ae5dfb157ef" checksum = "8ede55536349842337f7cdc63012ec33dc637945f3259c98a0763aac364cdf09"
dependencies = [ dependencies = [
"iroh-metrics-derive", "iroh-metrics-derive",
"itoa", "itoa",
@@ -2003,17 +1812,16 @@ dependencies = [
[[package]] [[package]]
name = "iroh-relay" name = "iroh-relay"
version = "1.0.2" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8149bb6a57126225a07d6928846d82dcedfd24ea0f863ef7b2eb475e1d726354" checksum = "ee116a8233f84980574bb8d11e4517503080cd2c3605bafca04ee9c35d708bd9"
dependencies = [ dependencies = [
"blake3", "blake3",
"bytes", "bytes",
"cfg_aliases", "cfg_aliases",
"data-encoding", "data-encoding",
"derive_more 2.1.1", "derive_more",
"getrandom 0.4.3", "getrandom 0.4.3",
"hickory-resolver",
"http", "http",
"http-body-util", "http-body-util",
"hyper", "hyper",
@@ -2023,7 +1831,7 @@ dependencies = [
"iroh-metrics", "iroh-metrics",
"lru", "lru",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
"noq", "noq",
"noq-proto", "noq-proto",
"num_enum", "num_enum",
@@ -2042,7 +1850,6 @@ dependencies = [
"tokio-websockets", "tokio-websockets",
"tracing", "tracing",
"url", "url",
"vergen-gitcl",
"webpki-roots", "webpki-roots",
"ws_stream_wasm", "ws_stream_wasm",
] ]
@@ -2054,7 +1861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da53233419ca36bf521ed45683b7748366f9b233032891eefc2d70567a84ac54" checksum = "da53233419ca36bf521ed45683b7748366f9b233032891eefc2d70567a84ac54"
dependencies = [ dependencies = [
"data-encoding", "data-encoding",
"derive_more 2.1.1", "derive_more",
"iroh-base", "iroh-base",
"n0-error", "n0-error",
"postcard", "postcard",
@@ -2067,10 +1874,10 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20e41eb982f15230c55f0a70a74a514360e1f565b07861924fd0e8db172b3d00" checksum = "20e41eb982f15230c55f0a70a74a514360e1f565b07861924fd0e8db172b3d00"
dependencies = [ dependencies = [
"derive_more 2.1.1", "derive_more",
"iroh", "iroh",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
"tokio", "tokio",
"tracing", "tracing",
] ]
@@ -2085,7 +1892,7 @@ dependencies = [
"futures-util", "futures-util",
"irpc-derive", "irpc-derive",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
"noq", "noq",
"postcard", "postcard",
"rcgen", "rcgen",
@@ -2326,20 +2133,28 @@ dependencies = [
] ]
[[package]] [[package]]
name = "moka" name = "n0-dns-resolver"
version = "0.12.15" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" checksum = "535c99c9a786bee714155d2ee4fc2477405c2c43536e1bc6a51de09a0d1448f7"
dependencies = [ dependencies = [
"crossbeam-channel", "derive_more",
"crossbeam-epoch", "ipconfig",
"crossbeam-utils", "jni 0.22.4",
"equivalent", "lru",
"parking_lot", "n0-error",
"portable-atomic", "n0-future",
"smallvec", "ndk-context",
"tagptr", "rand 0.10.2",
"uuid", "reqwest",
"rustc-hash",
"rustls",
"simple-dns",
"system-configuration",
"tokio",
"tokio-rustls",
"tracing",
"webpki-roots",
] ]
[[package]] [[package]]
@@ -2363,27 +2178,6 @@ dependencies = [
"syn 2.0.118", "syn 2.0.118",
] ]
[[package]]
name = "n0-future"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bb0e5d99e681ab3c938842b96fcb41bf8a7bb4bfdb11ccbd653a7e83e06c794"
dependencies = [
"cfg_aliases",
"derive_more 1.0.0",
"futures-buffered",
"futures-lite",
"futures-util",
"js-sys",
"pin-project",
"send_wrapper",
"tokio",
"tokio-util",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-time",
]
[[package]] [[package]]
name = "n0-future" name = "n0-future"
version = "0.3.2" version = "0.3.2"
@@ -2391,7 +2185,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2ab99dfb861450e68853d34ae665243a88b8c493d01ba957321a1e9b2312bbe" checksum = "e2ab99dfb861450e68853d34ae665243a88b8c493d01ba957321a1e9b2312bbe"
dependencies = [ dependencies = [
"cfg_aliases", "cfg_aliases",
"derive_more 2.1.1", "derive_more",
"futures-buffered", "futures-buffered",
"futures-lite", "futures-lite",
"futures-util", "futures-util",
@@ -2411,9 +2205,9 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbc618745ad0b7414b149d0517ad8b5573b2fb4d4e2717add3d2446ce1fdd826" checksum = "bbc618745ad0b7414b149d0517ad8b5573b2fb4d4e2717add3d2446ce1fdd826"
dependencies = [ dependencies = [
"derive_more 2.1.1", "derive_more",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
] ]
[[package]] [[package]]
@@ -2448,9 +2242,9 @@ dependencies = [
"libc", "libc",
"mac-addr", "mac-addr",
"ndk-context", "ndk-context",
"netlink-packet-core", "netlink-packet-core 0.8.2",
"netlink-packet-route", "netlink-packet-route 0.31.0",
"netlink-sys", "netlink-sys 0.8.8",
"objc2", "objc2",
"objc2-core-foundation", "objc2-core-foundation",
"objc2-core-wlan", "objc2-core-wlan",
@@ -2461,6 +2255,30 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "netdev"
version = "0.46.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c52c2584961c68f5a6e3356797344061f818c93b8acc9cd6d7e284795e563e"
dependencies = [
"block2",
"dispatch2",
"dlopen2",
"ipnet",
"jni 0.21.1",
"libc",
"mac-addr",
"ndk-context",
"netlink-packet-core 0.9.0",
"netlink-packet-route 0.33.0",
"netlink-sys 0.9.0",
"objc2-core-foundation",
"objc2-system-configuration",
"once_cell",
"plist",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "netlink-packet-core" name = "netlink-packet-core"
version = "0.8.2" version = "0.8.2"
@@ -2470,6 +2288,12 @@ dependencies = [
"paste", "paste",
] ]
[[package]]
name = "netlink-packet-core"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d6e2daf9a8c2e21302714706860a0e6be02e03d17294ecc66b354ea7d4059dd"
[[package]] [[package]]
name = "netlink-packet-route" name = "netlink-packet-route"
version = "0.31.0" version = "0.31.0"
@@ -2479,7 +2303,20 @@ dependencies = [
"bitflags", "bitflags",
"libc", "libc",
"log", "log",
"netlink-packet-core", "netlink-packet-core 0.8.2",
]
[[package]]
name = "netlink-packet-route"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59d48ffc8b73a506e1ff0878528c72668f2bfbc3d875b6be890a96be5d0d5576"
dependencies = [
"bitflags",
"libc",
"log",
"netlink-packet-core 0.9.0",
"zerocopy",
] ]
[[package]] [[package]]
@@ -2492,8 +2329,8 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"log", "log",
"netlink-packet-core", "netlink-packet-core 0.8.2",
"netlink-sys", "netlink-sys 0.8.8",
"thiserror 2.0.18", "thiserror 2.0.18",
] ]
@@ -2511,26 +2348,38 @@ dependencies = [
] ]
[[package]] [[package]]
name = "netwatch" name = "netlink-sys"
version = "0.19.1" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d9cbe01741347ef750d743d6690603f5eed8341e679fb51c8e629337aa11976" checksum = "c99d38e00b420df49e940fe0826e667c3dad82ac68eb4c2bbf754c1c14a83a10"
dependencies = [
"bytes",
"libc",
"log",
]
[[package]]
name = "netwatch"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39da9cad5f23aa43401f09497d3b280e51b5feba115d9ffbf38ca35d6ff95e96"
dependencies = [ dependencies = [
"atomic-waker", "atomic-waker",
"bytes", "bytes",
"cfg_aliases", "cfg_aliases",
"derive_more 2.1.1", "derive_more",
"ipnet", "ipnet",
"js-sys", "js-sys",
"libc", "libc",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
"n0-watcher", "n0-watcher",
"netdev", "netdev 0.45.0",
"netlink-packet-core", "netdev 0.46.3",
"netlink-packet-route", "netlink-packet-core 0.8.2",
"netlink-packet-route 0.31.0",
"netlink-proto", "netlink-proto",
"netlink-sys", "netlink-sys 0.8.8",
"noq-udp", "noq-udp",
"objc2-core-foundation", "objc2-core-foundation",
"objc2-system-configuration", "objc2-system-configuration",
@@ -2559,13 +2408,13 @@ dependencies = [
[[package]] [[package]]
name = "noq" name = "noq"
version = "1.0.1" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bf95190af1bd4a00a10e8255ca0c8ddd9e9a9f5e79151d7a7eb6d56aff5dc89" checksum = "b78be567e796cfa74bb9bdc4117790af2d505eb887019cf8803244353eb09d89"
dependencies = [ dependencies = [
"bytes", "bytes",
"cfg_aliases", "cfg_aliases",
"derive_more 2.1.1", "derive_more",
"noq-proto", "noq-proto",
"noq-udp", "noq-udp",
"pin-project-lite", "pin-project-lite",
@@ -2581,13 +2430,13 @@ dependencies = [
[[package]] [[package]]
name = "noq-proto" name = "noq-proto"
version = "1.1.1" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baa7b5ccd819a9c68a0d955e67a881032d09b1a17219b1f90b0997a0888e1a15" checksum = "7c1e5b6fe668491eca022f745a0a9402585626c73a7b839b3424ace15d6a9c8f"
dependencies = [ dependencies = [
"aes-gcm", "aes-gcm",
"bytes", "bytes",
"derive_more 2.1.1", "derive_more",
"enum-assoc", "enum-assoc",
"fastbloom", "fastbloom",
"getrandom 0.4.3", "getrandom 0.4.3",
@@ -2610,9 +2459,9 @@ dependencies = [
[[package]] [[package]]
name = "noq-udp" name = "noq-udp"
version = "1.0.1" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3137a52df66c20090a889828d1c655f21f52294cba64e5c4fbb04fc83eee7c8e" checksum = "4dc50afea61aff926e150a36c31f06094608848e114a3fe667d76ec233163b1c"
dependencies = [ dependencies = [
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@@ -2686,15 +2535,6 @@ dependencies = [
"syn 2.0.118", "syn 2.0.118",
] ]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "objc2" name = "objc2"
version = "0.6.4" version = "0.6.4"
@@ -2984,19 +2824,19 @@ dependencies = [
[[package]] [[package]]
name = "portmapper" name = "portmapper"
version = "0.19.1" version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3713e4977408279158444a18c1a01ac9bf2e7eaf1fbfd1a19ac9cd18d90721" checksum = "ca97242f016e090a25330613bc2382aab65eb22f9149dbe84d8f8e711d49a530"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
"derive_more 2.1.1", "derive_more",
"hyper-util", "hyper-util",
"igd-next", "igd-next",
"iroh-metrics", "iroh-metrics",
"libc", "libc",
"n0-error", "n0-error",
"n0-future 0.3.2", "n0-future",
"netwatch", "netwatch",
"num_enum", "num_enum",
"rand 0.10.2", "rand 0.10.2",
@@ -3070,17 +2910,6 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "prefix-trie"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf6e3177f0684016a5c209b00882e15f8bdd3f3bb48f0491df10cd102d0c6e7"
dependencies = [
"either",
"ipnet",
"num-traits",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "3.5.0" version = "3.5.0"
@@ -3381,12 +3210,6 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "resolv-conf"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7"
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.14" version = "0.17.14"
@@ -3481,7 +3304,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
dependencies = [ dependencies = [
"core-foundation 0.10.1", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"jni 0.22.4", "jni 0.22.4",
"log", "log",
@@ -3574,7 +3397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"core-foundation 0.10.1", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
"security-framework-sys", "security-framework-sys",
@@ -3767,9 +3590,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]] [[package]]
name = "simple-dns" name = "simple-dns"
version = "0.11.3" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a75cbde1bf934313596a004973e462f9a82caa814dcf1a5f507bdf51597eeb4" checksum = "2b6f884fa9a8d48101774bfbd3aeb81e968dd22cffd19a372da69f183db22c1a"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
@@ -3973,12 +3796,12 @@ dependencies = [
[[package]] [[package]]
name = "system-configuration" name = "system-configuration"
version = "0.7.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" checksum = "501336eb7ba9e417300a6a0fa985721065467aa83a6dcf0422a8e43e4c0328fa"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"core-foundation 0.9.4", "core-foundation",
"system-configuration-sys", "system-configuration-sys",
] ]
@@ -3992,12 +3815,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "tagptr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.27.0" version = "3.27.0"
@@ -4005,7 +3822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [ dependencies = [
"fastrand", "fastrand",
"getrandom 0.4.3", "getrandom 0.3.4",
"once_cell", "once_cell",
"rustix", "rustix",
"windows-sys 0.61.2", "windows-sys 0.61.2",
@@ -4068,9 +3885,7 @@ checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50"
dependencies = [ dependencies = [
"deranged", "deranged",
"js-sys", "js-sys",
"libc",
"num-conv", "num-conv",
"num_threads",
"powerfmt", "powerfmt",
"serde_core", "serde_core",
"time-core", "time-core",
@@ -4477,7 +4292,6 @@ version = "1.23.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea5fab0d6c3c01ae70085a09cb03d4c7a1d6314e2b3e075392783396d724ca0a" checksum = "ea5fab0d6c3c01ae70085a09cb03d4c7a1d6314e2b3e075392783396d724ca0a"
dependencies = [ dependencies = [
"getrandom 0.4.3",
"js-sys", "js-sys",
"serde_core", "serde_core",
"wasm-bindgen", "wasm-bindgen",
@@ -4491,7 +4305,7 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "varde-ctl" name = "varde-ctl"
version = "0.1.1" version = "0.1.8"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -4504,14 +4318,15 @@ dependencies = [
[[package]] [[package]]
name = "varde-daemon" name = "varde-daemon"
version = "0.1.1" version = "0.1.8"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64",
"iroh", "iroh",
"iroh-blobs", "iroh-blobs",
"iroh-mdns-address-lookup", "iroh-mdns-address-lookup",
"irpc", "irpc",
"n0-future 0.1.3", "n0-future",
"postcard", "postcard",
"proptest", "proptest",
"reflink-copy", "reflink-copy",
@@ -4529,49 +4344,12 @@ dependencies = [
[[package]] [[package]]
name = "varde-proto" name = "varde-proto"
version = "0.1.1" version = "0.1.8"
dependencies = [ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
] ]
[[package]]
name = "vergen"
version = "9.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75"
dependencies = [
"anyhow",
"derive_builder",
"rustversion",
"vergen-lib",
]
[[package]]
name = "vergen-gitcl"
version = "9.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ff3b5300a085d6bcd8fc96a507f706a28ae3814693236c9b409db71a1d15b9"
dependencies = [
"anyhow",
"derive_builder",
"rustversion",
"time",
"vergen",
"vergen-lib",
]
[[package]]
name = "vergen-lib"
version = "9.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569"
dependencies = [
"anyhow",
"derive_builder",
"rustversion",
]
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
+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.8"
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 - the socket, not the service:"
echo " systemctl try-restart varde.socket; systemctl --user try-restart varde.socket"
}
+4
View File
@@ -16,6 +16,10 @@ Restart=on-failure
DynamicUser=yes DynamicUser=yes
StateDirectory=varde StateDirectory=varde
RuntimeDirectory=varde RuntimeDirectory=varde
# The socket unit's file lives in this directory: removing it on every
# service stop (the default) left varde.socket listening on an unlinked
# path after any restart - "active" with nothing to connect to.
RuntimeDirectoryPreserve=yes
NoNewPrivileges=yes NoNewPrivileges=yes
ProtectSystem=strict ProtectSystem=strict
ProtectHome=yes ProtectHome=yes
+14 -1
View File
@@ -73,7 +73,8 @@ enum Command {
/// Peer trust operations /// Peer trust operations
#[command(subcommand)] #[command(subcommand)]
Peer(PeerCommand), Peer(PeerCommand),
/// Push fully-present content to a trusted peer /// Push fully-present content to a trusted peer, or one allowed to
/// exchange pushes
Push { Push {
/// BLAKE3 hash (hex) /// BLAKE3 hash (hex)
hash: String, hash: String,
@@ -116,6 +117,14 @@ enum PeerCommand {
/// iroh NodeId (z-base-32) /// iroh NodeId (z-base-32)
node_id: String, node_id: String,
}, },
/// Allow pushes with a peer for a while, without trusting it
AllowPush {
/// iroh NodeId (z-base-32)
node_id: String,
/// How long, in seconds (at most a day)
#[arg(long, default_value_t = 1800)]
ttl: u64,
},
/// List trusted peers /// List trusted peers
List, List,
} }
@@ -187,6 +196,9 @@ fn to_request(command: Command) -> Result<Request> {
}, },
Command::Peer(PeerCommand::Trust { node_id }) => Request::PeerTrust { node_id }, Command::Peer(PeerCommand::Trust { node_id }) => Request::PeerTrust { node_id },
Command::Peer(PeerCommand::Untrust { node_id }) => Request::PeerUntrust { node_id }, Command::Peer(PeerCommand::Untrust { node_id }) => Request::PeerUntrust { node_id },
Command::Peer(PeerCommand::AllowPush { node_id, ttl }) => {
Request::PeerAllowPush { node_id, ttl_secs: ttl }
}
Command::Peer(PeerCommand::List) => Request::PeerList {}, Command::Peer(PeerCommand::List) => Request::PeerList {},
Command::Push { hash, node_id } => Request::Push { hash, node_id }, Command::Push { hash, node_id } => Request::Push { hash, node_id },
Command::Gc => Request::Gc {}, Command::Gc => Request::Gc {},
@@ -246,6 +258,7 @@ fn print_human(data: Option<&ResponseData>) {
match data { match data {
ResponseData::Added { hash, bytes } => println!("added {hash} ({bytes} bytes)"), ResponseData::Added { hash, bytes } => println!("added {hash} ({bytes} bytes)"),
ResponseData::Pinned { hash } => println!("pinned {hash}"), ResponseData::Pinned { hash } => println!("pinned {hash}"),
ResponseData::Bytes { data } => println!("{data}"),
ResponseData::Done {} => println!("ok"), ResponseData::Done {} => println!("ok"),
ResponseData::Materialized { bytes, reflinked } => { ResponseData::Materialized { bytes, reflinked } => {
let how = if *reflinked { "reflinked" } else { "copied" }; let how = if *reflinked { "reflinked" } else { "copied" };
+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,
}; };
+4 -2
View File
@@ -16,11 +16,13 @@ required-features = ["socket-api"]
varde-proto = { workspace = true } varde-proto = { workspace = true }
iroh = { workspace = true } iroh = { workspace = true }
iroh-blobs = { workspace = true } iroh-blobs = { workspace = true }
# Request::Put / Read carry content inline, base64.
base64 = "0.22"
# mDNS/local-network address lookup left iroh core in 1.0. # mDNS/local-network address lookup left iroh core in 1.0.
iroh-mdns-address-lookup = "0.4" iroh-mdns-address-lookup = "0.6"
# Interop with iroh-blobs streams and channels; both are already in the # Interop with iroh-blobs streams and channels; both are already in the
# dependency tree via iroh-blobs. # dependency tree via iroh-blobs.
n0-future = "0.1" n0-future = "0.3"
irpc = "0.17" irpc = "0.17"
# Feature-gated D-Bus client for NetworkManager metered status. # Feature-gated D-Bus client for NetworkManager metered status.
zbus = { version = "5", optional = true, default-features = false, features = ["tokio"] } zbus = { version = "5", optional = true, default-features = false, features = ["tokio"] }
+70 -8
View File
@@ -335,6 +335,26 @@ impl Daemon {
} }
} }
/// The peer a request names: a NodeId, or a blob ticket from it (whose
/// addresses are remembered, so dialing it needs no discovery). An app
/// that knows peers by the tickets they hand out - redoal does - can
/// allow and push without unpacking them.
fn peer_of(&self, peer: &str) -> Result<iroh::EndpointId, OpError> {
if let Ok(id) = peer.parse::<iroh::EndpointId>() {
return Ok(id);
}
match crate::transfer::parse_ticket(peer) {
Ok((_, _, addr)) => {
let id = addr.id;
self.transfer.remember(addr);
Ok(id)
}
Err(_) => Err(OpError::InvalidArgument(format!(
"{peer:?} is neither a node id nor a ticket"
))),
}
}
/// Trusted peers seen on the LAN recently. /// Trusted peers seen on the LAN recently.
fn present_trusted_peers(&self) -> Vec<PeerInfo> { fn present_trusted_peers(&self) -> Vec<PeerInfo> {
let peers = self.lan_peers.lock().expect("lan peers lock"); let peers = self.lan_peers.lock().expect("lan peers lock");
@@ -371,6 +391,39 @@ impl Daemon {
bytes, bytes,
}) })
} }
Request::Put { data } => {
use base64::Engine as _;
let bytes = base64::engine::general_purpose::STANDARD
.decode(data.as_bytes())
.map_err(|e| OpError::InvalidArgument(format!("put: data is not base64: {e}")))?;
if bytes.len() as u64 > varde_proto::MAX_INLINE_BYTES {
return Err(OpError::InvalidArgument(format!(
"put: {} bytes, more than {} inline; add it by path",
bytes.len(),
varde_proto::MAX_INLINE_BYTES
)));
}
let (hash, bytes) = self.store.add_bytes(bytes).await?;
self.meta.record_format(&hash.to_hex(), StoredFormat::Raw)?;
Ok(ResponseData::Added {
hash: hash.to_hex().to_string(),
bytes,
})
}
Request::Read { hash } => {
use base64::Engine as _;
let parsed = parse_hash(&hash)?;
if self.meta.format_of(&parsed.to_hex()) == StoredFormat::HashSeq {
return Err(OpError::InvalidArgument(format!(
"read: {} is a collection; materialize it",
parsed.to_hex()
)));
}
let bytes = self.store.read_bytes(parsed, varde_proto::MAX_INLINE_BYTES).await?;
Ok(ResponseData::Bytes {
data: base64::engine::general_purpose::STANDARD.encode(bytes),
})
}
Request::Pin { hash, policy } => { Request::Pin { hash, policy } => {
let parsed = parse_hash(&hash)?; let parsed = parse_hash(&hash)?;
self.meta.pin(&parsed.to_hex(), policy)?; self.meta.pin(&parsed.to_hex(), policy)?;
@@ -533,24 +586,33 @@ impl Daemon {
let parsed: iroh::EndpointId = node_id let parsed: iroh::EndpointId = node_id
.parse() .parse()
.map_err(|e| OpError::InvalidArgument(format!("invalid node id: {e}")))?; .map_err(|e| OpError::InvalidArgument(format!("invalid node id: {e}")))?;
if !self.meta.untrust_peer(&parsed.to_string())? { let was_trusted = self.meta.untrust_peer(&parsed.to_string())?;
let was_allowed = self.meta.disallow_push(&parsed.to_string())?;
if !was_trusted && !was_allowed {
return Err(OpError::NotFound(format!("{parsed} was not trusted"))); return Err(OpError::NotFound(format!("{parsed} was not trusted")));
} }
Ok(ResponseData::Done {}) Ok(ResponseData::Done {})
} }
Request::PeerAllowPush { node_id, ttl_secs } => {
let parsed = self.peer_of(&node_id)?;
let ttl = ttl_secs.min(varde_proto::MAX_PUSH_TTL_SECS);
self.meta
.allow_push(&parsed.to_string(), crate::meta::unix_now() + ttl)?;
debug!(peer = %parsed, ttl, "pushes allowed");
Ok(ResponseData::Done {})
}
Request::PeerList {} => Ok(ResponseData::Peers { Request::PeerList {} => Ok(ResponseData::Peers {
peers: self.present_trusted_peers(), peers: self.present_trusted_peers(),
}), }),
Request::Push { hash, node_id } => { Request::Push { hash, node_id } => {
let parsed = parse_hash(&hash)?; let parsed = parse_hash(&hash)?;
let peer: iroh::EndpointId = node_id let peer = self.peer_of(&node_id)?;
.parse() // Consent, sending side: we only push to peers we trust or
.map_err(|e| OpError::InvalidArgument(format!("invalid node id: {e}")))?; // hold a push allowance for. (The receiver independently
// Consent, sending side: we only push to peers we trust. // requires the same of us.)
// (The receiver independently requires that it trusts us.) if !self.meta.may_push(&peer.to_string()) {
if !self.meta.is_trusted(&peer.to_string()) {
return Err(OpError::InvalidArgument(format!( return Err(OpError::InvalidArgument(format!(
"{peer} is not a trusted peer; trust it before pushing" "{peer} is neither trusted nor allowed to exchange pushes; trust it or allow pushes first"
))); )));
} }
let (_, _, complete) = self.store.presence(parsed).await?; let (_, _, complete) = self.store.presence(parsed).await?;
+60
View File
@@ -40,6 +40,10 @@ struct MetaState {
/// Trusted peer NodeIds (z-base-32). /// Trusted peer NodeIds (z-base-32).
#[serde(default)] #[serde(default)]
trusted_peers: BTreeSet<String>, trusted_peers: BTreeSet<String>,
/// Peers allowed to push to us and be pushed to, until a unix time
/// (`PeerAllowPush`): consent for that one thing, lapsing on its own.
#[serde(default)]
push_peers: BTreeMap<String, u64>,
/// Hashes (hex) the user shared via ticket export: standing consent /// Hashes (hex) the user shared via ticket export: standing consent
/// to serve them to anyone, like an `open_lan` pin. /// to serve them to anyone, like an `open_lan` pin.
#[serde(default)] #[serde(default)]
@@ -164,6 +168,31 @@ impl Meta {
.clone() .clone()
} }
/// Allow pushes with `node_id` until `until` (unix seconds). Extends,
/// never shortens, an allowance already held; expired ones are
/// dropped on the way.
pub fn allow_push(&self, node_id: &str, until: u64) -> Result<()> {
let now = unix_now();
self.mutate(|s| {
s.push_peers.retain(|_, u| *u > now);
let entry = s.push_peers.entry(node_id.to_string()).or_insert(0);
*entry = (*entry).max(until);
})
}
/// Whether pushes with `node_id` are allowed now: a trusted peer, or
/// one holding an unexpired push allowance.
pub fn may_push(&self, node_id: &str) -> bool {
let state = self.state.lock().expect("meta lock poisoned");
state.trusted_peers.contains(node_id)
|| state.push_peers.get(node_id).is_some_and(|u| *u > unix_now())
}
/// End a push allowance. Returns whether there was one.
pub fn disallow_push(&self, node_id: &str) -> Result<bool> {
self.mutate(|s| s.push_peers.remove(node_id).is_some())
}
pub fn is_trusted(&self, node_id: &str) -> bool { pub fn is_trusted(&self, node_id: &str) -> bool {
self.state self.state
.lock() .lock()
@@ -173,6 +202,14 @@ impl Meta {
} }
} }
/// Seconds since the unix epoch.
pub fn unix_now() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -201,4 +238,27 @@ mod tests {
assert!(meta.unpin("h").unwrap()); assert!(meta.unpin("h").unwrap());
assert!(!meta.is_pinned("h")); assert!(!meta.is_pinned("h"));
} }
#[test]
fn a_push_allowance_lapses_and_never_shortens() {
let dir = tempfile::tempdir().unwrap();
let meta = Meta::load(dir.path()).unwrap();
let now = unix_now();
assert!(!meta.may_push("p"));
meta.allow_push("p", now + 600).unwrap();
assert!(meta.may_push("p"));
assert!(!meta.is_trusted("p"), "a push allowance is not trust");
meta.allow_push("p", now + 10).unwrap();
assert!(meta.may_push("p"), "a shorter ask does not cut it short");
meta.allow_push("q", now.saturating_sub(1)).unwrap();
assert!(!meta.may_push("q"), "expired");
// Survives a reload.
let again = Meta::load(dir.path()).unwrap();
assert!(again.may_push("p"));
assert!(again.disallow_push("p").unwrap());
assert!(!again.may_push("p"));
// Full trust allows pushes too.
again.trust_peer("t").unwrap();
assert!(again.may_push("t"));
}
} }
+18 -8
View File
@@ -111,6 +111,8 @@ pub struct Gate {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct ConnInfo { struct ConnInfo {
trusted: bool, trusted: bool,
/// Trusted, or holding a push allowance (`PeerAllowPush`).
may_push: bool,
endpoint_id: Option<String>, endpoint_id: Option<String>,
} }
@@ -142,6 +144,11 @@ pub fn provider_events(gate: Gate) -> EventSender {
.endpoint_id .endpoint_id
.map(|id| gate.meta.is_trusted(&id.to_string())) .map(|id| gate.meta.is_trusted(&id.to_string()))
.unwrap_or(false); .unwrap_or(false);
let may_push = m
.inner
.endpoint_id
.map(|id| gate.meta.may_push(&id.to_string()))
.unwrap_or(false);
let ClientConnected { let ClientConnected {
connection_id, connection_id,
endpoint_id, endpoint_id,
@@ -151,6 +158,7 @@ pub fn provider_events(gate: Gate) -> EventSender {
connection_id, connection_id,
ConnInfo { ConnInfo {
trusted, trusted,
may_push,
endpoint_id: endpoint_id.map(|id| id.to_string()), endpoint_id: endpoint_id.map(|id| id.to_string()),
}, },
); );
@@ -192,11 +200,12 @@ pub fn provider_events(gate: Gate) -> EventSender {
ProviderMessage::ObserveRequestReceived(m) => { ProviderMessage::ObserveRequestReceived(m) => {
// Presence is information too: gate it like data. // Presence is information too: gate it like data.
// Bitfields are metadata, not payload, so trusted // Bitfields are metadata, not payload, so trusted
// peers may observe even while serving is off or the // peers - and peers allowed to push, whose push is
// connection is metered (push confirmation needs it). // confirmed by observing it - may observe even while
// serving is off or the connection is metered.
let trusted = conns let trusted = conns
.get(&m.inner.connection_id) .get(&m.inner.connection_id)
.map(|i| i.trusted) .map(|i| i.trusted || i.may_push)
.unwrap_or(false); .unwrap_or(false);
let allowed = trusted let allowed = trusted
|| allow(&gate, &conns, m.inner.connection_id, [m.inner.request.hash]); || allow(&gate, &conns, m.inner.connection_id, [m.inner.request.hash]);
@@ -208,12 +217,13 @@ pub fn provider_events(gate: Gate) -> EventSender {
m.tx.send(verdict).await.ok(); m.tx.send(verdict).await.ok();
} }
ProviderMessage::PushRequestReceived(m) => { ProviderMessage::PushRequestReceived(m) => {
// Inbound writes: trusted peers only, deferred while // Inbound writes: trusted peers and push allowances
// metered (RateLimited invites a retry later). // only, deferred while metered (RateLimited invites a
// retry later).
let info = conns.get(&m.inner.connection_id); let info = conns.get(&m.inner.connection_id);
let trusted = info.map(|i| i.trusted).unwrap_or(false); let may_push = info.map(|i| i.may_push).unwrap_or(false);
debug!(hash = %m.inner.request.hash.to_hex(), trusted, "push request received"); debug!(hash = %m.inner.request.hash.to_hex(), may_push, "push request received");
let verdict = if !trusted { let verdict = if !may_push {
Err(AbortReason::Permission) Err(AbortReason::Permission)
} else if gate.metered.is_metered() { } else if gate.metered.is_metered() {
Err(AbortReason::RateLimited) Err(AbortReason::RateLimited)
+24
View File
@@ -228,6 +228,30 @@ impl BlobStore {
/// Export `hash` to `dest`. Returns bytes written and whether at least /// Export `hash` to `dest`. Returns bytes written and whether at least
/// one file was reflinked. /// one file was reflinked.
/// Import `data` as one raw blob (`Request::Put`).
pub async fn add_bytes(&self, data: Vec<u8>) -> Result<(Hash, u64), OpError> {
let size = data.len() as u64;
let tag = self.store.add_bytes(data).temp_tag().await.map_err(internal)?;
info!(hash = %tag.hash().to_hex(), size, "imported bytes");
Ok((tag.hash(), size))
}
/// The whole content of a complete raw blob (`Request::Read`), when it
/// is no larger than `limit`.
pub async fn read_bytes(&self, hash: Hash, limit: u64) -> Result<Vec<u8>, OpError> {
match self.store.blobs().status(hash).await.map_err(internal)? {
BlobStatus::Complete { size } if size > limit => Err(OpError::InvalidArgument(format!(
"{} is {size} bytes, more than {limit} can be read inline; materialize it",
hash.to_hex()
))),
BlobStatus::Complete { .. } => {
let bytes = self.store.blobs().get_bytes(hash).await.map_err(internal)?;
Ok(bytes.to_vec())
}
_ => Err(OpError::NotFound(format!("{} is not complete in the store", hash.to_hex()))),
}
}
pub async fn materialize( pub async fn materialize(
&self, &self,
hash: Hash, hash: Hash,
+9
View File
@@ -298,6 +298,15 @@ impl Transfer {
Ok(ticket.to_string()) Ok(ticket.to_string())
} }
/// Teach the endpoint where a peer can be reached (the addresses a
/// ticket carries), so a push dials it directly instead of waiting on
/// discovery.
pub fn remember(&self, addr: EndpointAddr) {
if !addr.addrs.is_empty() {
self.known_addrs.add_endpoint_info(addr);
}
}
/// Queue a background fetch of `content` from `providers`. Emits /// Queue a background fetch of `content` from `providers`. Emits
/// progress events and [`Event::PinComplete`] when fully verified. /// progress events and [`Event::PinComplete`] when fully verified.
pub fn spawn_fetch(&self, content: HashAndFormat, providers: Vec<EndpointAddr>) { pub fn spawn_fetch(&self, content: HashAndFormat, providers: Vec<EndpointAddr>) {
+58
View File
@@ -422,3 +422,61 @@ fn push_rejected_without_receiver_trust() {
// And the receiver holds none of it. // And the receiver holds none of it.
assert!(!is_complete(&b.socket, &hash)); assert!(!is_complete(&b.socket, &hash));
} }
/// A push allowance is enough on both sides: two daemons that only
/// allowed pushes with each other (strangers sharing a place, in redoal)
/// exchange content without trusting each other - and the allowance
/// grants nothing else, so neither lists the other as a trusted peer.
#[test]
fn push_allowance_is_enough_and_is_not_trust() {
let _serial = MDNS_SERIAL.lock().unwrap_or_else(|e| e.into_inner());
let dir_a = tempfile::tempdir().unwrap();
let dir_b = tempfile::tempdir().unwrap();
let a = support::spawn_daemon_with_env(dir_a.path(), &[("VARDE_DISCOVERY", "true")]);
let b = support::spawn_daemon_with_env(dir_b.path(), &[("VARDE_DISCOVERY", "true")]);
let mut client_a = support::Client::connect(&a.socket);
let mut client_b = support::Client::connect(&b.socket);
let id_a = node_id(&mut client_a);
let dir = tempfile::tempdir().unwrap();
let src = dir.path().join("voice.bin");
let payload = vec![7u8; 120_000];
std::fs::write(&src, &payload).unwrap();
let hash = add_file(&mut client_a, &src);
pin(&mut client_a, &hash, false);
// A knows B by a ticket B handed out (as redoal peers do), not by
// its bare node id.
let b_src = dir.path().join("b-own.bin");
std::fs::write(&b_src, vec![1u8; 1_000]).unwrap();
let b_hash = add_file(&mut client_b, &b_src);
let b_ticket = match client_b.request(&Request::TicketExport { hash: b_hash }).data {
Some(ResponseData::Ticket { ticket }) => ticket,
other => panic!("unexpected ticket reply: {other:?}"),
};
assert!(client_a.request(&Request::PeerAllowPush { node_id: b_ticket.clone(), ttl_secs: 600 }).ok);
assert!(client_b.request(&Request::PeerAllowPush { node_id: id_a, ttl_secs: 600 }).ok);
match client_a.request(&Request::PeerList {}).data {
Some(ResponseData::Peers { peers }) => assert!(peers.is_empty(), "an allowance is not trust: {peers:?}"),
other => panic!("unexpected peer list: {other:?}"),
}
// The peer's address arrives by mdns; retry until it does.
let deadline = Instant::now() + Duration::from_secs(30);
let reply = loop {
let reply = client_a.request(&Request::Push { hash: hash.clone(), node_id: b_ticket.clone() });
if reply.ok || Instant::now() > deadline {
break reply;
}
std::thread::sleep(Duration::from_millis(500));
};
if !reply.ok {
let message = reply.error.map(|e| e.message).unwrap_or_default();
// Only an unreachable peer is the environment's fault; any other
// refusal is a real failure.
assert!(message.contains("connect"), "push failed: {message}");
eprintln!("SKIP: push never reached the peer (multicast unavailable?): {message}");
return;
}
assert!(wait_complete(&b.socket, &hash, Duration::from_secs(60)), "pushed content not complete on receiver");
}
+42
View File
@@ -276,3 +276,45 @@ fn reflink_when_supported() {
drop(daemon); drop(daemon);
let _ = std::fs::remove_dir_all(&base); let _ = std::fs::remove_dir_all(&base);
} }
/// Put and Read carry the bytes on the socket: a client that shares no
/// filesystem with the daemon (a desktop app and the system daemon, which
/// runs as its own user behind ProtectHome) stores and reads back without
/// a path either side must reach. A Put blob reads back by Materialize too.
#[test]
fn bytes_round_trip_inline() {
use base64::Engine as _;
let b64 = base64::engine::general_purpose::STANDARD;
let daemon = support::spawn_daemon();
let mut client = support::Client::connect(&daemon.socket);
let payload: Vec<u8> = (0..50_000u32).flat_map(|i| i.to_le_bytes()).collect();
let reply = client.request(&Request::Put { data: b64.encode(&payload) });
assert!(reply.ok, "put failed: {:?}", reply.error);
let hash = match reply.data {
Some(ResponseData::Added { hash, bytes }) => {
assert_eq!(bytes, payload.len() as u64);
hash
}
other => panic!("unexpected put reply: {other:?}"),
};
let reply = client.request(&Request::Read { hash: hash.clone() });
assert!(reply.ok, "read failed: {:?}", reply.error);
match reply.data {
Some(ResponseData::Bytes { data }) => assert_eq!(b64.decode(data).unwrap(), payload),
other => panic!("unexpected read reply: {other:?}"),
}
let dir = tempfile::tempdir().unwrap();
let dest = dir.path().join("out.bin");
materialize(&mut client, &hash, &dest, MaterializeMode::Copy).unwrap();
assert_eq!(std::fs::read(&dest).unwrap(), payload);
// Not base64: refused, not stored.
let reply = client.request(&Request::Put { data: "not base64!".into() });
assert_eq!(reply.error.expect("error body").code, ErrorCode::InvalidArgument);
// Unknown hash: not found.
let reply = client.request(&Request::Read { hash: "0".repeat(64) });
assert!(!reply.ok);
}
+47
View File
@@ -20,6 +20,13 @@ use serde::{Deserialize, Serialize};
/// Bumped on incompatible changes; reported in [`GlobalStatus`]. /// Bumped on incompatible changes; reported in [`GlobalStatus`].
pub const PROTOCOL_VERSION: u32 = 2; pub const PROTOCOL_VERSION: u32 = 2;
/// The longest a [`Request::PeerAllowPush`] allowance may last: a day.
pub const MAX_PUSH_TTL_SECS: u64 = 24 * 60 * 60;
/// The most bytes a [`Request::Put`] or a [`Request::Read`] reply carries
/// inline. Larger content goes through `Add`/`Materialize` and a path.
pub const MAX_INLINE_BYTES: u64 = 16 * 1024 * 1024;
/// A request from a client to the daemon. One JSON object per line. /// A request from a client to the daemon. One JSON object per line.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")] #[serde(tag = "type", rename_all = "snake_case")]
@@ -61,6 +68,24 @@ pub enum Request {
#[serde(default)] #[serde(default)]
mode: MaterializeMode, mode: MaterializeMode,
}, },
/// Import bytes carried in the request, base64 (standard alphabet),
/// at most [`MAX_INLINE_BYTES`]. Replies [`ResponseData::Added`].
///
/// For a client that shares no filesystem with the daemon: the system
/// daemon runs as its own user with `ProtectHome`, so it can neither
/// read a file a desktop app wrote for `Add` nor write one back for
/// `Materialize`. Added in varde 0.1.4.
Put {
/// The content, base64.
data: String,
},
/// Read a complete raw blob back in the reply, base64, at most
/// [`MAX_INLINE_BYTES`]. Replies [`ResponseData::Bytes`]. The
/// counterpart of [`Request::Put`]; added in varde 0.1.4.
Read {
/// Hash (hex) of the blob.
hash: String,
},
/// Query daemon status, globally or for one hash. /// Query daemon status, globally or for one hash.
Status { Status {
/// If set, report on this hash; otherwise report global status. /// If set, report on this hash; otherwise report global status.
@@ -94,6 +119,20 @@ pub enum Request {
/// The peer's iroh NodeId (z-base-32). /// The peer's iroh NodeId (z-base-32).
node_id: String, node_id: String,
}, },
/// Allow pushes between this daemon and a peer for `ttl_secs` (at most
/// [`MAX_PUSH_TTL_SECS`]): we may push to it, and accept what it pushes
/// to us. Nothing else - unlike [`Request::PeerTrust`] it neither
/// syncs the peer's pins nor serves it our content - so an app can
/// grant it to strangers it shares something with for a while (two
/// phones at the same place) and let it lapse. Asking again extends
/// it; [`Request::PeerUntrust`] ends it early. Added in varde 0.1.5.
PeerAllowPush {
/// The peer's iroh NodeId (z-base-32), or a blob ticket it issued
/// (varde 0.1.6+), whose addresses are then used to reach it.
node_id: String,
/// How long the allowance lasts, in seconds.
ttl_secs: u64,
},
/// List trusted peers and their current connectivity. /// List trusted peers and their current connectivity.
PeerList {}, PeerList {},
/// Push fully-present content to a trusted peer, unprompted. /// Push fully-present content to a trusted peer, unprompted.
@@ -104,6 +143,9 @@ pub enum Request {
/// policy), so the content survives gc and shows up in `List`. /// policy), so the content survives gc and shows up in `List`.
Push { Push {
/// Hash (hex) of the fully-present blob or HashSeq root to push. /// Hash (hex) of the fully-present blob or HashSeq root to push.
///
/// The receiver below may be a NodeId or, from varde 0.1.6, a blob
/// ticket it issued.
hash: String, hash: String,
/// The receiving peer's iroh NodeId (z-base-32). /// The receiving peer's iroh NodeId (z-base-32).
node_id: String, node_id: String,
@@ -228,6 +270,11 @@ pub enum ResponseData {
/// Whether at least one file was reflinked rather than copied. /// Whether at least one file was reflinked rather than copied.
reflinked: bool, reflinked: bool,
}, },
/// A blob's content, base64. Reply to [`Request::Read`].
Bytes {
/// The content, base64 (standard alphabet).
data: String,
},
/// Global daemon status. Reply to [`Request::Status`] without a hash. /// Global daemon status. Reply to [`Request::Status`] without a hash.
Status(GlobalStatus), Status(GlobalStatus),
/// Per-hash status. Reply to [`Request::Status`] with a hash. /// Per-hash status. Reply to [`Request::Status`] with a hash.