diff --git a/.editorconfig b/.editorconfig
index b01e3da7a..56ccf3432 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,3 +4,11 @@ root = true
charset = utf-8
insert_final_newline = true
end_of_line = lf
+
+[tests/shadow/shadow.data.template/hosts/**/*{key,cert}*]
+charset = unset
+end_of_line = unset
+insert_final_newline = unset
+trim_trailing_whitespace = unset
+indent_style = unset
+indent_size = unset
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a15d2a93b..57b3930bc 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -160,7 +160,7 @@ build-repro:
- tpa
- amd64
-integration:
+integration-chutney:
stage: test
image: debian:stable-slim
script:
@@ -177,6 +177,90 @@ integration:
tags:
- amd64
+integration-shadow:
+ variables:
+ JOB_SHADOW_REPO: "https://github.com/shadow/shadow.git"
+ JOB_SHADOW_BRANCH: "main"
+ JOB_SHADOW_COMMIT: "v2.2.0"
+
+ JOB_TGEN_REPO: "https://github.com/shadow/tgen.git"
+ JOB_TGEN_BRANCH: "main"
+ JOB_TGEN_COMMIT: "v1.1.1"
+ stage: test
+ cache:
+ - key: $CI_JOB_NAME-shadow-$JOB_SHADOW_COMMIT
+ paths:
+ - opt/shadow
+ - key: $CI_JOB_NAME-tgen-$JOB_TGEN_COMMIT
+ paths:
+ - opt/tgen
+ image: debian:11-slim
+ script:
+ # We're going to install binaries to $HOME/.local/bin
+ - 'export PATH=$HOME/.local/bin:$PATH'
+
+ - apt-get update
+ - apt-get install -y git tor stow
+ - mkdir -p ~/src
+ - mkdir -p ~/.local
+
+ # Build shadow
+ - |
+ if [ -f opt/shadow/bin/shadow ]
+ then
+ echo "Using shadow binary from cache"
+ else
+ echo "Building shadow"
+ git clone --shallow-since=2021-08-01 -b $JOB_SHADOW_BRANCH $JOB_SHADOW_REPO ~/src/shadow
+ pushd ~/src/shadow
+ git checkout $JOB_SHADOW_COMMIT
+ export CC=gcc CXX=g++ CONTAINER=debian:11-slim BUILDTYPE=release RUSTPROFILE=minimal
+ ci/container_scripts/install_deps.sh
+ ci/container_scripts/install_extra_deps.sh
+ export PATH="$HOME/.cargo/bin:${PATH}"
+ ./setup build --jobs $(nproc) --prefix $CI_PROJECT_DIR/opt/shadow
+ ./setup install
+ popd
+ fi
+ - apt-get install -y libglib2.0-0
+ - stow -d opt -t $HOME/.local shadow
+
+ # Build tgen
+ - |
+ if [ -f opt/tgen/bin/tgen ]
+ then
+ echo "Using tgen binary from cache"
+ else
+ echo "Building tgen"
+ git clone --shallow-since=2022-01-01 -b $JOB_TGEN_BRANCH $JOB_TGEN_REPO ~/src/tgen
+ pushd ~/src/tgen
+ git checkout $JOB_TGEN_COMMIT
+ apt-get install -y cmake gcc libglib2.0-0 libglib2.0-dev libigraph-dev make
+ mkdir build
+ cd build
+ cmake .. -DCMAKE_INSTALL_PREFIX=$CI_PROJECT_DIR/opt/tgen
+ make --jobs $(nproc)
+ make install
+ popd
+ fi
+ - apt-get install -y libigraph1 libglib2.0-0
+ - stow -d opt -t $HOME/.local tgen
+
+ # Ensure newly installed executables can be found
+ - hash -r
+
+ # Run tests
+ - pushd tests/shadow
+ - ./run.sh
+ artifacts:
+ paths:
+ - tests/shadow
+ when: always
+ expire_in: 1 week
+ tags:
+ - amd64
+ - shadow-small
+
coverage-aggregated:
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
diff --git a/tests/shadow/README.md b/tests/shadow/README.md
new file mode 100644
index 000000000..96b1c0d13
--- /dev/null
+++ b/tests/shadow/README.md
@@ -0,0 +1,26 @@
+# Shadow-based integration test
+
+This is an integration test for arti that uses the
+[shadow](https://shadow.github.io/) simulator. It creates a simulation of a
+small Tor network, uses the `arti` client to perform some transfers across this
+simulated network, and validates that the transfers succeeded.
+
+## Running locally
+
+To run locally, you'll need to install shadow itself somewhere on our `PATH`,
+following [shadow's installation
+instructions](https://shadow.github.io/docs/guide/supported_platforms.html).
+
+Next you'll need to install executables that will run inside the simulation, in the
+locations where [`shadow.yaml`](./shadow.yaml) expects to find them.
+
+* Install [`tgen`](https://github.com/shadow/tgen/) in `~/.local`.
+
+* Install [`tor`](https://gitlab.torproject.org/tpo/core/tor) in `/usr/sbin`.
+ Typically you can install it using your host system's package manager.
+
+* Build the `arti` client for target `x86_64-unknown-linux-gnu`, so that the
+ binary is at: `../../target/x86_64-unknown-linux-gnu/debug/arti`.
+
+Once those are installed, you can invoke the [`run.sh`](./run.sh) script from
+this directory.
diff --git a/tests/shadow/conf/authgen.pw b/tests/shadow/conf/authgen.pw
new file mode 100644
index 000000000..65ee896a0
--- /dev/null
+++ b/tests/shadow/conf/authgen.pw
@@ -0,0 +1,2 @@
+shadowprivatenetwork
+
diff --git a/tests/shadow/conf/authgen.torrc b/tests/shadow/conf/authgen.torrc
new file mode 100644
index 000000000..a23c8bf72
--- /dev/null
+++ b/tests/shadow/conf/authgen.torrc
@@ -0,0 +1,3 @@
+DirServer test 127.0.0.1:5000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
+ORPort 5000
+
diff --git a/tests/shadow/conf/shadowresolv.conf b/tests/shadow/conf/shadowresolv.conf
new file mode 100644
index 000000000..bbc8559cd
--- /dev/null
+++ b/tests/shadow/conf/shadowresolv.conf
@@ -0,0 +1 @@
+nameserver 127.0.0.1
diff --git a/tests/shadow/conf/tgen.client.graphml.xml b/tests/shadow/conf/tgen.client.graphml.xml
new file mode 100644
index 000000000..61dbc2cb3
--- /dev/null
+++ b/tests/shadow/conf/tgen.client.graphml.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+ fileserver:80
+
+
+ 1 MiB
+ 1 KiB
+
+
+ 1,2,3,4,5,6,7,8,9,10
+
+
+ 10
+ 3600
+
+
+
+
+
+
+
diff --git a/tests/shadow/conf/tgen.server.graphml.xml b/tests/shadow/conf/tgen.server.graphml.xml
new file mode 100644
index 000000000..49758d6d2
--- /dev/null
+++ b/tests/shadow/conf/tgen.server.graphml.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ 80
+
+
+
diff --git a/tests/shadow/conf/tgen.torclient.graphml.xml b/tests/shadow/conf/tgen.torclient.graphml.xml
new file mode 100644
index 000000000..2cd646d18
--- /dev/null
+++ b/tests/shadow/conf/tgen.torclient.graphml.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+ fileserver:80
+ localhost:9000
+
+
+ 1 MiB
+ 1 KiB
+
+
+ 1,2,3,4,5,6,7,8,9,10
+
+
+ 10
+ 3600
+
+
+
+
+
+
+
diff --git a/tests/shadow/conf/tor.authority.torrc b/tests/shadow/conf/tor.authority.torrc
new file mode 100644
index 000000000..48095a1d6
--- /dev/null
+++ b/tests/shadow/conf/tor.authority.torrc
@@ -0,0 +1,8 @@
+AuthoritativeDirectory 1
+V3AuthoritativeDirectory 1
+V3BandwidthsFile ../torflowauthority/v3bw
+ExitPolicy "reject *:*"
+TestingDirAuthVoteGuard 3FB0BD7827C760FE7F9DD810FCB10322D63AB4CF,FF197204099FA0E507FA46D41FED97D3337B4BAA
+TestingDirAuthVoteExit 4EBB385C80A2CA5D671E16F1C722FBFB5F176891,0A9B1B207FD13A6F117F95CAFA358EEE2234F19A
+TestingDirAuthVoteGuardIsStrict 1
+TestingDirAuthVoteExitIsStrict 1
diff --git a/tests/shadow/conf/tor.common.torrc b/tests/shadow/conf/tor.common.torrc
new file mode 100644
index 000000000..ac6881b8c
--- /dev/null
+++ b/tests/shadow/conf/tor.common.torrc
@@ -0,0 +1,26 @@
+DataDirectory .
+BandwidthRate 1024000
+BandwidthBurst 1024000
+DirServer 4uthority bridge v3ident=604ED7BE52100945A47EAD301D45FADF493C6371 orport=9111 100.0.0.1:9112 A52C A5B5 6C64 D864 F6AE 43E5 6F29 ACBD 5706 DDA1
+TestingTorNetwork 1
+ServerDNSResolvConfFile ../../../conf/shadowresolv.conf
+ServerDNSTestAddresses 4uthority
+ServerDNSAllowBrokenConfig 1
+ServerDNSDetectHijacking 0
+AssumeReachable 1
+AuthDirTestReachability 0
+NumCPUs 1
+Log info stdout
+LogTimeGranularity 1
+HeartbeatPeriod 1
+SafeLogging 0
+ContactInfo https://github.com/shadow/shadow-plugin-tor/issues
+DisableDebuggerAttachment 0
+PathBiasUseThreshold 10000
+PathBiasCircThreshold 10000
+DoSCircuitCreationEnabled 0
+DoSConnectionEnabled 0
+DoSRefuseSingleHopClientRendezvous 0
+CircuitPriorityHalflife 30
+ControlPort 9051
+LearnCircuitBuildTimeout 0
diff --git a/tests/shadow/conf/tor.exit.torrc b/tests/shadow/conf/tor.exit.torrc
new file mode 100644
index 000000000..bd3d33eab
--- /dev/null
+++ b/tests/shadow/conf/tor.exit.torrc
@@ -0,0 +1 @@
+ExitPolicy "accept *:*"
diff --git a/tests/shadow/conf/tor.non-exit.torrc b/tests/shadow/conf/tor.non-exit.torrc
new file mode 100644
index 000000000..89de84871
--- /dev/null
+++ b/tests/shadow/conf/tor.non-exit.torrc
@@ -0,0 +1 @@
+ExitPolicy "reject *:*"
diff --git a/tests/shadow/conf/tor.relay.torrc b/tests/shadow/conf/tor.relay.torrc
new file mode 100644
index 000000000..006e477f1
--- /dev/null
+++ b/tests/shadow/conf/tor.relay.torrc
@@ -0,0 +1,3 @@
+ORPort 9111 IPv4Only
+DirPort 9112
+SocksPort 0
diff --git a/tests/shadow/run.sh b/tests/shadow/run.sh
new file mode 100755
index 000000000..25fa65e1e
--- /dev/null
+++ b/tests/shadow/run.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+set -euo pipefail
+
+# Remove output of previous run
+rm -rf shadow.data
+
+# Run the simulation
+shadow \
+ --model-unblocked-syscall-latency=true \
+ --log-level=debug \
+ --strace-logging-mode=standard \
+ --parallelism="$(nproc)" \
+ --template-directory=./shadow.data.template \
+ --progress=true \
+ shadow.yaml \
+ > shadow.log
+
+# Check whether file transfers via arti inside the simulation succeeded
+successes="$(grep -c stream-success shadow.data/hosts/articlient/articlient.tgen.1001.stdout || true)"
+if [ "$successes" = 10 ]
+then
+ echo "Passed"
+ exit 0
+else
+ echo "Failed. Only got $successes successful streams."
+fi
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/fingerprint b/tests/shadow/shadow.data.template/hosts/4uthority/fingerprint
new file mode 100644
index 000000000..ae347c47b
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/4uthority/fingerprint
@@ -0,0 +1 @@
+Unnamed A52CA5B56C64D864F6AE43E56F29ACBD5706DDA1
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/fingerprint-ed25519 b/tests/shadow/shadow.data.template/hosts/4uthority/fingerprint-ed25519
new file mode 100644
index 000000000..e185e29e0
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/4uthority/fingerprint-ed25519
@@ -0,0 +1 @@
+Unnamed 4ngwAxQ6Zw2VJB241CenzfrVtT6omCDOf9yGB1XAqTs
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/keys/authority_certificate b/tests/shadow/shadow.data.template/hosts/4uthority/keys/authority_certificate
new file mode 100644
index 000000000..a70eaae88
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/4uthority/keys/authority_certificate
@@ -0,0 +1,45 @@
+dir-key-certificate-version 3
+fingerprint 604ED7BE52100945A47EAD301D45FADF493C6371
+dir-key-published 1999-12-01 06:00:01
+dir-key-expires 2000-12-01 06:00:01
+dir-identity-key
+-----BEGIN RSA PUBLIC KEY-----
+MIIBigKCAYEA1hnDm6FHOh7Us4jtlQTZ3hWhGqrG9MO51me1NSyia4MtagzuWYHX
+2uwjxAnc4mDa8uzUWumhXfMuqq1iOQNDwLC2pavhvJ0bSsKM12t1cXz5bH2ugnnR
+QlUA2jxISl8h9PkKj06t3qfG+5NY9y1Tl7cF+EcDKKhnUDn9BzV8XeqXPSZQDvrp
+1sHafEp0DxlqKAiQgWmc8OX8NdvefjBTZXLdN/N9F7r7YIXi7KZUxocbHK5nxF5b
+JuARLHxSNY4Kqec9Gsoaw1/CK1nF9yVYZQoTNysIv90H3Bi+KcN+U9Pvg2L0rVRp
+7jkc69rgVXLSxtDeNKWr4hOfUltm7QZ9Drer8CQfEsCcqBxSHGA9LcEClT25u0Lu
+1JJFHdl33t1VAhMCgOvDrrYXFXvDeuvoRRsvAkvNiqpKvOMkXRyKGbSbs2P1R5fy
+EpxTMI09SRflYF191+WZW+hx9SBMjpTg7pbe8tmAGYKv6FOBjYqGhP6eBDVFO8uL
+mh7X26tG92ljAgMBAAE=
+-----END RSA PUBLIC KEY-----
+dir-signing-key
+-----BEGIN RSA PUBLIC KEY-----
+MIIBCgKCAQEA8IsYZv91wS+wOd/J3U+BvPRg+hagYo+fEhKCY1ysnd9a8ImnxkUu
+ERJp5iFNDNHklbm7PE/0n/qYbYp052nMCannDSOiYRZwuLd44iXN5YmwyZ1ScTB2
+r63pvLNtqH3o9Kch9DsKDykJp4AVBM1ZHm+Pzpm6ybMcV8dSKGerHlzle99VbPjw
+kS/mtEgn1kMKQm7CesvrPfXPYyCGia+Lp0jiK5hXN2Fg9eNv/GAls2DvcFJgI0Kh
+vIQW2GSu/+vVLJ87ijC923Xp5UOEAdgLnYOnoZhCanc5N9ozUO0svDq4Q5gh4P3D
+uuXPLmi5exVYJFVFmMeR0Xn0ILKQ0fcIWwIDAQAB
+-----END RSA PUBLIC KEY-----
+dir-key-crosscert
+-----BEGIN ID SIGNATURE-----
+vphTtO7NyzkkL2BnDde25TNdLk9a56zZkp2I01nhSps6K0Gr1iHJCng/q99fE4Dw
+VlYTePO7MVvXivlQR6HjYtRQJwi6f4rl4E8vlA9c5J31M2tZs9Faih2e1GzgmlZd
+pInz8PgpXcyo20qjUTTjxsdtSO7niwdvfjsc14hQTjzVSqCgzM0D0xmmMLjoZa3Z
+2SudSDdJTQ5Y70PH9ItdrgUQcczb/6kQ2Xkhk7Mbm0OtTK++cbFbKB3lMvK7H8TE
+Uxkr2FwDPfD8+2xzDbd0tTBqRWKNEtWD9dtnj/TDW/rVP0qtITAvVTp5x/QQeZYn
+2A888gOchsXEnDRKScssOg==
+-----END ID SIGNATURE-----
+dir-key-certification
+-----BEGIN SIGNATURE-----
+B0/mXB9KI2FSka2EHCk+dB7L60b3XAAejq08LL71qlvENrreGr6OAAU38GzAoNg0
+OBEs8oCgIXQ1rf+ZOVhNxEBJNXw9GBQsCJ+udoqkEw4ZtvFv4L9uo5BZR07KqENM
+xICcpghTm2Xw/Ttc89OXz44cSHzufmgu8hId1nyVtEuBu097vlPDhSZeboMQhP86
+J6gDf/exf2/Rml8ahRJt1ywq/KlvHlI69TTpcQf/mf6yyNevrtrwxMLylEn4keZF
+m8ikTTHUhkvxHEWE6PuQpjmbOXeu6rpLBr0lD/jrwMxusY1nMptg+V6qKbmpHfti
+TZpdEe4BB+myMQywblPY1JJS0REybBm9VllsEOUToLw21QWyDcqwTYTbC27xyhb6
+fj2vq4CC3+sbrA2ns4fsD6uUK779YQcZBtEE0GUIOqB/L22trrVb499aFlmwk317
+LGyp0gb9Evc2hHz9vcJzF0kIcNpptbcboYLD6QAjNA2xbI0RtTrRqyCvJbo15ncc
+-----END SIGNATURE-----
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/keys/authority_identity_key b/tests/shadow/shadow.data.template/hosts/4uthority/keys/authority_identity_key
new file mode 100644
index 000000000..ec08b804d
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/4uthority/keys/authority_identity_key
@@ -0,0 +1,41 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+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
+hs9OaMibpgVvS/a0rXY=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/keys/authority_signing_key b/tests/shadow/shadow.data.template/hosts/4uthority/keys/authority_signing_key
new file mode 100644
index 000000000..aa9fd9ac1
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/4uthority/keys/authority_signing_key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA8IsYZv91wS+wOd/J3U+BvPRg+hagYo+fEhKCY1ysnd9a8Imn
+xkUuERJp5iFNDNHklbm7PE/0n/qYbYp052nMCannDSOiYRZwuLd44iXN5YmwyZ1S
+cTB2r63pvLNtqH3o9Kch9DsKDykJp4AVBM1ZHm+Pzpm6ybMcV8dSKGerHlzle99V
+bPjwkS/mtEgn1kMKQm7CesvrPfXPYyCGia+Lp0jiK5hXN2Fg9eNv/GAls2DvcFJg
+I0KhvIQW2GSu/+vVLJ87ijC923Xp5UOEAdgLnYOnoZhCanc5N9ozUO0svDq4Q5gh
+4P3DuuXPLmi5exVYJFVFmMeR0Xn0ILKQ0fcIWwIDAQABAoIBAEFH3pWFWvXU9WSf
+gW9YdoNLZ043+csF40vKTiydKBOoXEvPcAL1H21uq+LSwtBp3jlsMI5LvL32Cowf
++V4cbk1pGNWOCDGFH45h7KKjU9+poJr24Trqxisjbb3SjD4f/ar3+NUJeM9oecBF
+mG63NCp4aHTPb8vxmg2QgJGKoPwZ/0RU/lY+ic1VukzY+SNODFvs8MvaOiaxE5KJ
+Zc6V057IoELV9j0KJvPNp23MyczI4LYrJrAbD6u34+FX0smCs2pgn8YVdHm88JR+
+yXe9rZmUCTE7uCEBjZrYzGbdY9gRD54ZmyckZ4Amyhn0DjTVA2dQWzTCSzr4q2a9
+goydQiECgYEA/9SrHWfV85D6/hspTCCs4bvPc36o33ed1EaXyWANoxZ/UsDc606u
+ayN6N6OmrjdQLiJTdq6cF/c4zA7Qea44yqgp4fMLAEa2s//oBB9VFiSmj59WDmPr
+BzPaQ/aq0QwjF6KhGmGuKAySILH2FOE9wGexR5KnApD6uZXeVipVR6kCgYEA8LPW
+bBPOJzuSU55mImBrIIYHIbiH8Xq0ENJ7Fs37HY2WpnuZEEQ8L73KgFmVv4s57WQM
+XyzU1JN4fjYjRrdEJDVaHZa6CqA2YgFY4WgQtskRj5II9KfYEXsc/dZN1Xae/KwA
+Xe7WQ1OdqOQMgWks06MjmJ8FPb0WUxsYQyUlAmMCgYEA0/dE/8w0vOX2z+t5QBYi
+p7WQoMTk7qKRJnyG7tai9emB/PLHGD/MzABNFySEyfwSducB+Geb9U9kw83Sdb8g
+VukWKpvRFZ2Ba4owlpnPSdV8G6uNBcdLyF/yDDWoOiSwPrkEenNMdOpVeLl6hrpJ
+C83eWMrtjXbKBlJiMUpJ9mECgYAYuaBxAafPh+MEC+VZ6qWWda4N3LK6L2yHvdc/
+BHGjG4Q6HcAfV6MHFGvUu18Sie6tX/WVYTmLjIp4DH+bKAAr5/8VwwHhTYvIjXLf
+1JzC6eyP3uRknUkooojb2kOshb7XHaP8AiioSdKMIsDWGkOsibmCV/Nl4CKG7uWT
+ICGpGQKBgQCLNaMtwQJmCW2QJSmzVeau9j+fZ/rV5SyImhL6GbWVD2uYXhBFQVWR
+pS6w2d5HbzngLzLKqmZyrM9zYYwKLjYvVuf4atIwHNnj0sEAVeX7raE5Svdfpmvh
+QVfxLkPh9hfxcNVp0wm0loTR4nH/7DUQYtO6yutMqAXPBfTXBDj65Q==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_master_id_public_key b/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_master_id_public_key
new file mode 100644
index 000000000..faf1c8fbc
Binary files /dev/null and b/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_master_id_public_key differ
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_master_id_secret_key b/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_master_id_secret_key
new file mode 100644
index 000000000..4417e6490
Binary files /dev/null and b/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_master_id_secret_key differ
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_signing_cert b/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_signing_cert
new file mode 100644
index 000000000..57be6c839
Binary files /dev/null and b/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_signing_cert differ
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_signing_secret_key b/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_signing_secret_key
new file mode 100644
index 000000000..814dce620
Binary files /dev/null and b/tests/shadow/shadow.data.template/hosts/4uthority/keys/ed25519_signing_secret_key differ
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/keys/secret_id_key b/tests/shadow/shadow.data.template/hosts/4uthority/keys/secret_id_key
new file mode 100644
index 000000000..052635b2e
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/4uthority/keys/secret_id_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDDI/5gDYsibzoj1VLpcdkTYsimCSLJQ00sPPZSJ0ynGdY339u0
+xnpMivoV86tMWe1wOQQI/ZANL7ZI6+DSpYIu8w0UwjsEQSMp6I3TomlVooaPsuJc
+XNmnW++qS0MaTNfj4zY4KKihGA1oN6nX+xdHt8qCMK/btgxFQtBZMkLVCwIDAQAB
+AoGAOw0RVQ/EIs910JIlXO9OuUcTYOsy06TCHbj08TdjQs3WvWHIopPNPoxv0WTD
+WX99EpCuX+obRWJCzx5SEmxzUCp6NR9ZDmheoyaUO8W+sslA3s92nyVoGaVpL4Hk
+uaC1mQYY2wYK6LQY1Zaw2FkhrnCInBOKTKwt5xVNDygVZYECQQDloomCDiuUpetL
+BJRoPD4CriEa5iiCfT0Z/TKKtnCX5Vsj+AkTJuVNojzFjRphWxpomy43QVsKeoa2
+EdYM+TLpAkEA2YuYPgey9neE+ffrWTpNvLaPOM5EOHKGyqL921goWIh0xMuK5yqX
+joDv+Ebi2sRxpM5M5gnQaYxVEoZ72b2H0wJAIVGNDB7MOfw9AJyAgANFqBPl38ZH
+WG+dfvja7X99JLz1lWl2v52kJbiFgtBvZHLYiRcDBKdp80JF64L5ANbjiQJACU3q
+JgUIR3hXLKXI4BkZSA/60jAoRM3JLBOKjf4YQ6kabWxTNY3hy4uCN/k4LKkoPyc/
+rLsQueLxpIgx9d8gVwJBAKAspF2VD4BVwpv7YU0OLH/7qfeOlhYOh8RgqijHUGy4
+y+9RcdReFVaKCM6JoOjlB8mnzryT/fTcN3unZVX1YDs=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/keys/secret_onion_key b/tests/shadow/shadow.data.template/hosts/4uthority/keys/secret_onion_key
new file mode 100644
index 000000000..e0b1558d0
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/4uthority/keys/secret_onion_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCyl4GOPEFulXGewYnulRwv5tn5kEE9SOZWEs8XYVMsiIiTfVSJ
+9GQQ7U9eXqLIAyY9V5Dg6y8d7crP3Yx+veKvKDxeEgbrYcpI3ZqizM9NADeby/4d
+/23+YXjRnQfKpzf63W/EixEYCz1+3qGa7bzRJp3Ud6A0ly6k8thZESDGhwIDAQAB
+AoGBALE0a7S6TSJw3qBr4XT+N03AfiCG5nTs1MArQZktVN4nS30tKqkH+hJhphM5
+S8OyfEr5jCPguIu12zuN1U4dE8KXC6CyI6aWexeAuoa2M6bcM0uesZFG1NGmIYq6
+0S9cFymCyK/WRACH6XJbF87vs2E/6kyKlOOJCikSvNPja72hAkEA6Lw+Uria8beJ
+qQPwGDer0eNzjGL7oxN89vYqfFiDs8jTct2SaQgyyEiRBm9yiaLwZ1nQduKI+CvU
+diQFRHDShQJBAMRxt2qBsarhxAZU4KcKX9DEVwp4ga0YojioiXFrkmvruQGHjmYs
+RKhYJHQFNSnwJPtfvpIUphohbyKfrC/VEJsCQAxUbLjVLuA/aPubScz+pguokSaO
+1+X70lMDF50e01ti+FDNjEQkOwyBS62bmeXQzAutpm2l8ruzFr5rihvmHkECQQCR
+60psIeSAY21BqsCpM8qSf5WMIJFqq5ascXyOtHIyGgn1ueSrvoxNZBPH3qpr+Ybu
+9mLPjekIFffAh0zWxvSrAkAuXLfTjTRzWfC3ia8o2BZ0VUfh1YETpWej2K4w01y2
+JvmzhoNX5lFuSL+I0L78zhXGH3ZyZKPlPXgvsQAyi1du
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/torrc b/tests/shadow/shadow.data.template/hosts/4uthority/torrc
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/shadow/shadow.data.template/hosts/4uthority/torrc-defaults b/tests/shadow/shadow.data.template/hosts/4uthority/torrc-defaults
new file mode 100644
index 000000000..6418794bf
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/4uthority/torrc-defaults
@@ -0,0 +1,3 @@
+%include ../../../conf/tor.common.torrc
+%include ../../../conf/tor.relay.torrc
+%include ../../../conf/tor.authority.torrc
diff --git a/tests/shadow/shadow.data.template/hosts/articlient/arti.toml b/tests/shadow/shadow.data.template/hosts/articlient/arti.toml
new file mode 100644
index 000000000..05168e9ef
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/articlient/arti.toml
@@ -0,0 +1,17 @@
+[tor_network]
+fallback_caches = [
+ { rsa_identity = "A52CA5B56C64D864F6AE43E56F29ACBD5706DDA1", ed_identity = "4ngwAxQ6Zw2VJB241CenzfrVtT6omCDOf9yGB1XAqTs", orports = [ "100.0.0.1:9111" ] },
+]
+authorities = [
+ { name = "4uthority", v3ident = "604ED7BE52100945A47EAD301D45FADF493C6371" },
+]
+
+[path_rules]
+ipv4_subnet_family_prefix = 33
+ipv6_subnet_family_prefix = 129
+
+[application]
+# Disable process hardening, which interferes with shadow's process control
+# https://github.com/shadow/shadow/issues/2368
+permit_debugging = true
+allow_running_as_root = true
diff --git a/tests/shadow/shadow.data.template/hosts/exit1/fingerprint b/tests/shadow/shadow.data.template/hosts/exit1/fingerprint
new file mode 100644
index 000000000..3f6a51985
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/exit1/fingerprint
@@ -0,0 +1 @@
+exit1 0A9B1B207FD13A6F117F95CAFA358EEE2234F19A
diff --git a/tests/shadow/shadow.data.template/hosts/exit1/keys/secret_id_key b/tests/shadow/shadow.data.template/hosts/exit1/keys/secret_id_key
new file mode 100644
index 000000000..a4b537921
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/exit1/keys/secret_id_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDWHmC3V/AwxzeeaYLCOTZGdyLdef316yMyO0fP2s49wsoq82FN
+xRR54/VPcH6zQlRgG9q/cBzFEHvt7tnnN5ZyIpmvY33L1tVyh60f8ZZU9JJ/Dfm5
+FPSAbBg5F8/tlm8422G+THQ8DGRiwNn/aO8ITR98lLJOI/e4LckRMqhDMQIDAQAB
+AoGBAJxsmO+YmOqpv6JFJrfJoDMeeiQfKT9tRss9r5rUGPPZyGRUZXOxVOIvfVlJ
+r+Y5wPZtowNUXlBBWKhcZGicnZTySDc0ZxA5Pac9yZWMbhmGoytezHWEvPt9kW2W
+/G/bnOzSzzPh06WWc9dhhAj7ZKVf6vGs0642EbFXHRyq3Gy5AkEA6rwbGmtK72JS
+0sYWmxI2DcxeviRSW4JFGydQDIpezD8FSO9+BZloM330HWsqV0QivS2VWgn2GVe+
+8WoUMl8v+wJBAOmEJjtUSeoqpinTGJOcpxKt3KFzL/Gxc9dFshYNDAZxpT0GEYSM
+1Gnbt8Qw+fdBeQGLQVN1xyFp+8bSZb9bdcMCQCZ3tSlbM+iPAGuyMrou/dS9DTmz
+0htU+eY9Rw0HEAoAuej99nUMctsdZQf9K+8pJ1hBRByBTWdRzeTaMX0fxEkCQAq5
+mQ/shSCOKOeO3HgbH8lJ1vJLWg5duhZPCyjcHItHOPGej7toxHDAOKZ4IWLrzOT3
+Ql7U7/pm5btx+l5ZkwUCQATHvGizIpxDUtSUukiGmqfQPQGnGllQvhykUs4mK+ZH
+kM1BlX5gnq4uSDW786SVQJd1anfioicVgNxtpJYrMfo=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/exit1/keys/secret_onion_key b/tests/shadow/shadow.data.template/hosts/exit1/keys/secret_onion_key
new file mode 100644
index 000000000..923392ed9
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/exit1/keys/secret_onion_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCjARKjsThqAqCkpalru8i43zLkx8vBAD2QGruQRTlVOCE3F2hL
+Qqt/tWlsHT4Pjq2qqudgwDt2DoF1geYpPSiMiJkPjeWW/le1lSCSKWnE+WiC1MNl
+P5f38uLrbklZheiFPvdim+8jcgAj5Q6rEUglGRIZDB+VhyizOp+/LofCNQIDAQAB
+AoGAJ/O4XNSwDhOsyg4sk1JIZT+Xmsl5oEltzWcv97Fv1vpV8cnT7zZdEIUnXK9i
+Ka//0ufmpCkZqdruUh3PiuYJjNw+7BZblRhC6966TpMXb33nfFcPUAKCWT7wBsPl
+hJAMSmLZhJ40m/oQLxDTO7f/I5DOzNBhuBWfK1TkoLor78ECQQDMo514GLd1b361
+u3ZvIkNFwjRNDZOkwA4zwEA1bSn0Q4CMX3wzk94wKuQMeilfAjecYtZJTzehcm/F
+DWahTbtlAkEAy+pZF1HQgipV/NwvxTZNElE2wk0T/IBLQxdsABI4wXVsPVxJiq6G
+GFiFvyO1xpBEF0UkA+CRShzSCGsI5odGkQJAEEcPdocYyLmWraP8trhV+42IFXz3
++ongV1N6iAVn6esYpZHO0ld3vAMdYrSw58LIY6MlAm3PsgLlSxpWyIvjVQJAPhuV
+oRWpNUwhvo9t+VotpUuZKxsA4GJPChNDMM5b/Qh/vDy0pYm8jAPCUNJWljDwnEnR
+y2CFwBYBHtFC97vyEQJAFEalj9r6b658uiUPFIxd7Mwt/E5X2Ky9mEotUdRu1ngK
+2CmDcqghqYwh5ez71kcybCZik6LEnf+N9m9ckBvy8g==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/exit1/keys/secret_onion_key_ntor b/tests/shadow/shadow.data.template/hosts/exit1/keys/secret_onion_key_ntor
new file mode 100644
index 000000000..690165764
Binary files /dev/null and b/tests/shadow/shadow.data.template/hosts/exit1/keys/secret_onion_key_ntor differ
diff --git a/tests/shadow/shadow.data.template/hosts/exit1/torrc b/tests/shadow/shadow.data.template/hosts/exit1/torrc
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/shadow/shadow.data.template/hosts/exit1/torrc-defaults b/tests/shadow/shadow.data.template/hosts/exit1/torrc-defaults
new file mode 100644
index 000000000..446d5ee96
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/exit1/torrc-defaults
@@ -0,0 +1,3 @@
+%include ../../../conf/tor.common.torrc
+%include ../../../conf/tor.relay.torrc
+%include ../../../conf/tor.exit.torrc
diff --git a/tests/shadow/shadow.data.template/hosts/exit2/fingerprint b/tests/shadow/shadow.data.template/hosts/exit2/fingerprint
new file mode 100644
index 000000000..bb59838d8
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/exit2/fingerprint
@@ -0,0 +1 @@
+exit2 4EBB385C80A2CA5D671E16F1C722FBFB5F176891
diff --git a/tests/shadow/shadow.data.template/hosts/exit2/keys/secret_id_key b/tests/shadow/shadow.data.template/hosts/exit2/keys/secret_id_key
new file mode 100644
index 000000000..2ba72f4d4
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/exit2/keys/secret_id_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDQ6kPlqMrz6GB+dWdLVLdC0++jZgAuG5leLEcJ6a3Nfsd3XauZ
+N2ZVeJ5yqwRFK99C8dekA6pvTh3c4iznxI0s6fusNF7b8tCEoXRLZlqI2/0N3L9p
+ZrmTq9agDD4hXE+MXDn5uxSib8/UZqUsO6Zts2lsvl8AWQqehD4HnUsJowIDAQAB
+AoGAb44YUWLvdIyKMhyY2qXS8dFfrf3CbNx56cp06HsqPofLFRckkTz+3Pb6jLp7
+HkCOCB5BiqgWczbgRVlUpzHSO8ix4uFy0j6HzMwulJBkUKzqw9mHJs9gTaTT0N9p
+VHjuEMABU8h9ZeK5kqGTGPeqSInTGfBlSQKyQFtxmL54cokCQQD6pN8hKlrjZrLY
+50jhRKAma4XBfSFkAUQ8i+oJMV7ifPd49GiP8rcIP4bhSdLwswYW9sV0kf9UKAcg
+qnXsktUlAkEA1WEeaWdgkByU8SItuXMZOQsv9GC/RqJ5UGVVayre1ARFCHoZmcpJ
+VR5OL3+v+U9T42FEQ6s9olECMe15H/v9JwJBAMoyGQKDTaDnJ15NboCzwNJdqSCH
+Flm7n9WMNXfHCTmDAIzl9jgx4C/aN7AkwvOEKGR8HJxGbwjv5ZgXi1wWs7UCQG3H
+99OXAlFiwyaExS2ikBmPLe73T7HRqcS5PgdTGS3PvIyt9Tnwn2YRn3xEn48PR0O3
+KV3/XkiCkTSKz0SrawUCQQCJUu0ou6I+sRLMp/h0gvWP7tMSOpN2elsrkTYmkkxJ
+hPs9wavqV2x1uMlpKAT35w4CvblYKQUGgRNOPxYH+9gK
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/exit2/keys/secret_onion_key b/tests/shadow/shadow.data.template/hosts/exit2/keys/secret_onion_key
new file mode 100644
index 000000000..a9dba21bc
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/exit2/keys/secret_onion_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDAeOk5SjkHNONU4YPjgqOPR2wMmCP8Llfsc2zyA2lnRByiSXfz
+ANJajuRFkVU+ngAcU+RK1gtRma6WQcyGBOFa4xw4PdpePT1fJb7GbMQYFg6GQJ4l
+fZs3CkjJxAxJhUhzm06Cp6r9O4BUTC7uoyNukTblVFwvJVB7utckxu+RkwIDAQAB
+AoGBAK0uHeuGdTmexM2i/GDBMxnAUeAjJH3L1SDT2rsHhtd72afMIzlrC9qfp5VQ
+5zqoP8E9WrRbU2Pgu85G9hfAXGMzcLb8s0VozeVv85VaT+h/4hPZ4a5k9H1Iiivx
+OEeeTtOgKVjyAvVhybh756Z9N8i8q+9DVB3XeWoxJU2rWnkBAkEA3pZybYsdUXrC
+vxAdBv+BabK9NWysDEdjZpH7YncN33kSBYdmAhqBNqyC+2EYaBYb7RYtGtPUMUdm
+X11Qz4DXgwJBAN1dMlu+WRNohvJmQojJ0hkxgHleqGeOO+n6Bn3EFMFknFGvG7HF
+zHgjIfh7uRrvAwTeBQR784tcQZfXDOMmMLECQHmPMjjO45kO8PJFS4T53uQpoBlg
+GumtxwRKLz6MBECKLyRj2HrHnM+OEQ5V7W1SvbeSB9oB2316H9du7+YS2B8CQEMO
+sWujXP6JIqAZpDwlzzR8j2u05hUAvFujtwWwOY1gOg5wmlYfElKuaeMJL8jd8K8v
+xptWJS/pUaF6rte8wPECQQCRZcZFoqoqaRu8BG1AQ/Z/T9qBZdc5KDzQnckNPDzK
+UlkBbWX37BwRt75bSwtx6zRVIHfdc8VD4szAsEYA23po
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/exit2/keys/secret_onion_key_ntor b/tests/shadow/shadow.data.template/hosts/exit2/keys/secret_onion_key_ntor
new file mode 100644
index 000000000..3760e7691
Binary files /dev/null and b/tests/shadow/shadow.data.template/hosts/exit2/keys/secret_onion_key_ntor differ
diff --git a/tests/shadow/shadow.data.template/hosts/exit2/torrc b/tests/shadow/shadow.data.template/hosts/exit2/torrc
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/shadow/shadow.data.template/hosts/exit2/torrc-defaults b/tests/shadow/shadow.data.template/hosts/exit2/torrc-defaults
new file mode 100644
index 000000000..446d5ee96
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/exit2/torrc-defaults
@@ -0,0 +1,3 @@
+%include ../../../conf/tor.common.torrc
+%include ../../../conf/tor.relay.torrc
+%include ../../../conf/tor.exit.torrc
diff --git a/tests/shadow/shadow.data.template/hosts/relay1/fingerprint b/tests/shadow/shadow.data.template/hosts/relay1/fingerprint
new file mode 100644
index 000000000..927f49add
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/relay1/fingerprint
@@ -0,0 +1 @@
+relay1 3FB0BD7827C760FE7F9DD810FCB10322D63AB4CF
diff --git a/tests/shadow/shadow.data.template/hosts/relay1/keys/secret_id_key b/tests/shadow/shadow.data.template/hosts/relay1/keys/secret_id_key
new file mode 100644
index 000000000..7058d22f6
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/relay1/keys/secret_id_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDLQVnBp+spwAZE/YvTDSF567CIsdSZC1VhYQNV5qIs8j42qDYZ
+TdtgGFcKqBNesB/CyDX3vepszZgGKPw0SnxzxrZcJyi093QOpc/aWgacptEpXawK
+wXZp0hw+RObh/7QSJ5X72p6c+7lMFb8HGdbxmZHXtced4V1+RNQsjWzuJwIDAQAB
+AoGBAJg9mr0yyTG8YvXJ87FhZHGt18yLZnI7yvflclolJdQEnhTSabMKXgbcNFYH
+Dnsd8Kw0yjeGSNC2LehvSnjgUdYOhKPrz3QcejnUVWg1bXMx2EsoSqjRFWymEMis
+fPATXM/FvbJcHSDP62ht6UL/Xpxi8SePZtJ/m9L2j0bwKTIBAkEA/kpBHgAygVAv
+wjc2NMP1OpSkjiS5LjofFFFTbeV+pkpOEzJ9htB7Aw/W0BfIMSAe8+xAQd/Q1xIQ
+RK5McVBAfQJBAMyfPimZfY9Dy8nREOW7RCFpl9UWv+hbAqvIVlPOLDFLNbbdBrQu
+8QGcJbQJyTX5GRBhyC4SriIVmKCDAn2nrnMCQQCprjzDWxhYZ3xm2VanYPb7DwK7
+AtA6g3gHo7jLBS88oSp17uQKGIVs5p3Nn4DrHubolwXNrx541MncOn4xwn41AkA/
+MnuPe5F0+vpo1YJDgcf0DdnP/Vu+FsXHanOHJKkvfEwVs0QLyGe1GJXoehFD8zBT
+h++8kxZm/oaKJIxTZMOTAkBnP7ZSRNtgtBLCLwh5JpGh+oiP++Hx3biJ2HRVo5+E
+6oW5BcytArbD9myZxqjwi6OjIMi5YC1QjYRPNQMd/oPu
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/relay1/keys/secret_onion_key b/tests/shadow/shadow.data.template/hosts/relay1/keys/secret_onion_key
new file mode 100644
index 000000000..1c755bb83
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/relay1/keys/secret_onion_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC3tDxczkSWWMxfJiJ6HchqZ+ZX/aSJX2jT/sXsLCa2TeTtnSOw
+Ss7XSzfo4fEuW0u7Mw2YGwyoze7x5G9XRuY9a8FbC70vQryhO5U6huI0W/MYI7og
+mp8jeISFVH8xP5Y12rM74OZfyGHKSxvNTEai/CzxY01PYvRuujpXfMRA/QIDAQAB
+AoGAfL3gt0xBeq41qO6mPrH2o3qGCX3sunOVJU89as/YQbbHGyq3T0CdD8i94F98
+fHP2CtnyAzVq9T1RYeQyTtuP/0GccvNJzx/3hr6pXfsemVpLniP3xWU+qw43FHTr
+4qJ9e506xwrdD4FKNJxzaopXErD3fEA5WRWQcoY3FPtRIIECQQDr2t4mzIPhLZzp
+TRKnR7FObxAwNT+EaWo4z1Ekn7MrJ2GA8QiyK8NOKKrCLNQ1agsMZ/Ma5Vyjbvkx
+0iPb1bOtAkEAx2UNYHo+vV2ZL1E04gceC1vUaSjC9ec2CiOpFC2jQCnDhqwY2XPa
+TcfVQRKNdeJxlA+zTkalG0ESgS8hJi7skQJALOF2Op3aTqQ2YrLW5NwgolNCGlpX
+nuPMYckZoMV++6w/wqCbTgRD4wCcFd0f+JYYSiERwsF4kmUQqGlbh36v9QJBAK+L
+deV0env5RX30/pvoLDlP1YfnjQ01S8xNbPQI4esBWdTc+tunNZC42DT8AnLAs8Ho
+uLG3XHDML2Bwf7zAHyECQFsW7NKELTuN1jYOgbBGrScsylb6F7R9PD7Jx5emxJYP
+aH+fQOhqeiUsFpEiJGA+nNaYA8M3OIPUjPOLo07+rEk=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/relay1/keys/secret_onion_key_ntor b/tests/shadow/shadow.data.template/hosts/relay1/keys/secret_onion_key_ntor
new file mode 100644
index 000000000..b837a62f0
Binary files /dev/null and b/tests/shadow/shadow.data.template/hosts/relay1/keys/secret_onion_key_ntor differ
diff --git a/tests/shadow/shadow.data.template/hosts/relay1/torrc b/tests/shadow/shadow.data.template/hosts/relay1/torrc
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/shadow/shadow.data.template/hosts/relay1/torrc-defaults b/tests/shadow/shadow.data.template/hosts/relay1/torrc-defaults
new file mode 100644
index 000000000..b398ffc70
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/relay1/torrc-defaults
@@ -0,0 +1,3 @@
+%include ../../../conf/tor.common.torrc
+%include ../../../conf/tor.relay.torrc
+%include ../../../conf/tor.non-exit.torrc
diff --git a/tests/shadow/shadow.data.template/hosts/relay2/fingerprint b/tests/shadow/shadow.data.template/hosts/relay2/fingerprint
new file mode 100644
index 000000000..18baa8923
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/relay2/fingerprint
@@ -0,0 +1 @@
+relay2 FF197204099FA0E507FA46D41FED97D3337B4BAA
diff --git a/tests/shadow/shadow.data.template/hosts/relay2/keys/secret_id_key b/tests/shadow/shadow.data.template/hosts/relay2/keys/secret_id_key
new file mode 100644
index 000000000..4c0c06330
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/relay2/keys/secret_id_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCflLrEIuXI/eMaot4Eut37bMRmQEx9pG8xtgqWDk9ig+Cbf2J+
+s5zmizef4cOJBvJsz+aFzQlGSm4J8REZyHRih7lbwfsx+tyiVpugmmJEsCHUMhSU
+6ce/2k1AAcaLj3g8ldq9SWKCsReSbwYSS7YfwTu6/Q9d+WSSt9pdJyxgnQIDAQAB
+AoGAYemsqnlLCgaVGJvDo51QZFr5Kk/bXt/MhfDPEADZvnVyE5aWRW1JIL9cstGf
+2uyTRCZPp9sFDMbQO3NY7bvrhCz9y9ivbiewMqLKtXEB6j6Y/cGJQAc50uowrlCq
+Bt+7mBHmYqTvF03fhY48aROh4fJlPoPgo4ddP6fbjD2EbVUCQQDMEOYEDV9wLL8l
+0mxsk8B86PMgW6p70SJ9QQQUnDYFeRfIIkL6Fa4BjjCXA+4yRVEVJSMCHMXoVnyp
+5TZt1HRnAkEAyDGWVBrKGWAVFZE8M9IZXku9mTtCF5Jujrb8CyloVzIvQQ0+Pzhh
+XvokNkhQ5+xEfmUwCpbEwkQCc58j7cEAWwJAa1Jb1je+QL2Xzelr0nKmYksVSqhK
+8hFcJSmjXr+E/b3gzwWN2f664+CHF3UeuduQuCEu1zaQsw+BQszkokCA8wJAawIO
+f+Bd/NheDMw4KN76Dmxf6UNCncT48bIf4Xt9uXYqyhPhHPvvGEyPBLD9BitFpWZD
+GrXicJDX2qPLGbAblwJAOU2yVZ9ARLei5B3Pa9NyFl1LClM0wwEYQoyvqYC7me1+
+a74vBSgWk+phMnvWs37rb029vwknAV1IwSfxYjGXXA==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/relay2/keys/secret_onion_key b/tests/shadow/shadow.data.template/hosts/relay2/keys/secret_onion_key
new file mode 100644
index 000000000..e6cb348c5
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/relay2/keys/secret_onion_key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDYSBZyLdOwSOTf2B6Ad91cptmzmMUIhIjhQJwtDgFOlongssNk
+zIGB1rUw8gLFkX6mxgvFABLW+CQ1UmTjQucwVsVyRDS4QtKDEF0hvY5obCFHugn/
+wlen6/8gWyvhauwUEDRyS1pZEaA6P8KR0/MLN3mitKuP9ggbPaHWZk8MtwIDAQAB
+AoGAPoO/sSsb2AWJ2kW+s0VyP3ieznGm6gpdAjbJraAxsoSCv+cL4VPXTy8/WjFp
+qa+fWxc9iai2yVoaJSt2q9XHusIiHRlzuZUclbbj0HY4MrF88iYmc6nJMSFqn4p6
+LFmsyM7xx7XMmjgQPC0FTFbeMCYW5P6IgdNmQM7wihDEB+kCQQDsxa8GX9dSZw+E
+4xWdPTtLUUgyRDZl8xZonm14mjVPHCi77SlIQMRZwujyHvzbtD7/M2cnWtXufZ4i
+D3JTzaOzAkEA6dhrax6/OS2V57RGgdqXEghl309E8f9OtEMTfzPptWLTbRwSRYuP
+Rsjr4CPr8e1R+toCctkDRx/U5L1rjheA7QJAES63aOsDoUNBaQ9hvit3Zhvb9Wnl
+2LgvZCA7KYlnXPpaKmknmcq5dRoj9Y4yI2+NMs/0KHL7G3eaRjaAwXwm6QJADLhu
+F3SjuEuHhQQkdbnbT+rPB9e7C2L0+A9SBzBevTQ0BhJp2AigcZxKYHXzxsQ/GmMz
+kOXHLN5JaWv4ZtFWfQJAacs81Jw/yniawF7T8DTiNnmlJyc+Xi7Tf2VteKHIKZ7J
+o7vQtj+L56GTpy0UUCQQ+ABFvOyKIjTH8ARyVxsb/w==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/shadow/shadow.data.template/hosts/relay2/keys/secret_onion_key_ntor b/tests/shadow/shadow.data.template/hosts/relay2/keys/secret_onion_key_ntor
new file mode 100644
index 000000000..50ddd8dec
Binary files /dev/null and b/tests/shadow/shadow.data.template/hosts/relay2/keys/secret_onion_key_ntor differ
diff --git a/tests/shadow/shadow.data.template/hosts/relay2/torrc b/tests/shadow/shadow.data.template/hosts/relay2/torrc
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/shadow/shadow.data.template/hosts/relay2/torrc-defaults b/tests/shadow/shadow.data.template/hosts/relay2/torrc-defaults
new file mode 100644
index 000000000..b398ffc70
--- /dev/null
+++ b/tests/shadow/shadow.data.template/hosts/relay2/torrc-defaults
@@ -0,0 +1,3 @@
+%include ../../../conf/tor.common.torrc
+%include ../../../conf/tor.relay.torrc
+%include ../../../conf/tor.non-exit.torrc
diff --git a/tests/shadow/shadow.yaml b/tests/shadow/shadow.yaml
new file mode 100644
index 000000000..71d753f82
--- /dev/null
+++ b/tests/shadow/shadow.yaml
@@ -0,0 +1,81 @@
+general:
+ # FIXME: this is currently set carefully to just after transfers are expected
+ # to finish, but before arti spends a lot of time spinning CPU waiting for
+ # circuits to time out. Should diagnose and fix the spin.
+ stop_time: 27 min
+network:
+ graph:
+ type: gml
+ inline: |
+ graph [
+ directed 0
+ node [
+ id 0
+ host_bandwidth_down "1 Gbit"
+ host_bandwidth_up "1 Gbit"
+ ]
+ edge [
+ source 0
+ target 0
+ latency "50 ms"
+ jitter "0 ms"
+ packet_loss 0.0
+ ]
+ ]
+hosts:
+ fileserver:
+ network_node_id: 0
+ processes:
+ - path: ~/.local/bin/tgen
+ environment: OPENBLAS_NUM_THREADS=1
+ args: ../../../conf/tgen.server.graphml.xml
+ start_time: 1
+ 4uthority:
+ network_node_id: 0
+ ip_addr: 100.0.0.1
+ processes:
+ - path: /usr/sbin/tor
+ args: --Address 4uthority --Nickname 4uthority
+ --defaults-torrc torrc-defaults -f torrc
+ start_time: 1
+ exit1:
+ network_node_id: 0
+ processes:
+ - path: /usr/sbin/tor
+ args: --Address exit1 --Nickname exit1
+ --defaults-torrc torrc-defaults -f torrc
+ start_time: 60
+ exit2:
+ network_node_id: 0
+ processes:
+ - path: /usr/sbin/tor
+ args: --Address exit2 --Nickname exit2
+ --defaults-torrc torrc-defaults -f torrc
+ start_time: 60
+ relay1:
+ network_node_id: 0
+ processes:
+ - path: /usr/sbin/tor
+ args: --Address relay1 --Nickname relay1
+ --defaults-torrc torrc-defaults -f torrc
+ start_time: 60
+ relay2:
+ network_node_id: 0
+ processes:
+ - path: /usr/sbin/tor
+ args: --Address relay2 --Nickname relay2
+ --defaults-torrc torrc-defaults -f torrc
+ start_time: 60
+ articlient:
+ network_node_id: 0
+ options:
+ log_level: trace
+ processes:
+ - path: ../../target/x86_64-unknown-linux-gnu/debug/arti
+ args: proxy -c arti.toml -o proxy.socks_port=9000 --disable-fs-permission-checks -l debug,tor_proto=trace
+ environment: RUST_BACKTRACE=1;HOME=./home
+ start_time: 15m
+ - path: ~/.local/bin/tgen
+ environment: OPENBLAS_NUM_THREADS=1
+ args: ../../../conf/tgen.torclient.graphml.xml
+ start_time: 25m