Skip to content

feat: add iroh-relay-unyt deployment#18

Merged
ThetaSinner merged 3 commits intomainfrom
feat/iroh-relay-unyt
Apr 8, 2026
Merged

feat: add iroh-relay-unyt deployment#18
ThetaSinner merged 3 commits intomainfrom
feat/iroh-relay-unyt

Conversation

@ThetaSinner
Copy link
Copy Markdown
Member

@ThetaSinner ThetaSinner commented Apr 2, 2026

Summary

  • Deploy a standalone Iroh relay server (v0.95.1) for the Unyt project at iroh-relay-unyt.holochain.org
  • DigitalOcean droplet (s-2vcpu-2gb) in NYC1 with Cloudflare A/AAAA DNS records
  • Uses the upstream iroh-relay musl binary directly via a native systemd service (no containers)
  • Built-in Let's Encrypt TLS, QUIC address discovery on port 7842, metrics on port 9090
  • Open access (no auth restriction)
  • CI lint step added for the new cloud-init file

Summary by CodeRabbit

  • Chores
    • Added CI cloud-init schema validation that surfaces failures as annotated errors in pull requests.
  • New Features
    • End-to-end provisioning for iroh-relay instances: automated VM provisioning, cloud-init-based installation and service setup, TLS certificate issuance, metrics exposure, and automatic IPv4/IPv6 DNS records.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 48ed6b31-8f01-4e20-a75e-ab48205716be

📥 Commits

Reviewing files that changed from the base of the PR and between 05f27a5 and cd41b64.

📒 Files selected for processing (1)
  • main.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • main.go

Walkthrough

Adds two cloud-init files to provision iroh-relay hosts, a CI lint step validating one cloud-init, and Pulumi changes that read both cloud-init files and create DigitalOcean droplets plus Cloudflare A/AAAA records for each.

Changes

Cohort / File(s) Summary
CI / Workflow
\.github/workflows/preview.yaml
Adds a lint step invoking cloud-init schema against iroh-relay-unyt/cloud-init.yaml, captures stderr to stderr.log and $GITHUB_STEP_SUMMARY, emits ::error annotations from parsed diagnostics, and fails the step on validation errors.
Cloud-Init Configs
iroh-relay-unyt/cloud-init.yaml, iroh-relay-hc/cloud-init.yaml
Two new #cloud-config files that write /etc/iroh-relay/config.toml, /etc/systemd/system/iroh-relay.service, and /opt/iroh-relay/provision.sh; each downloads and verifies iroh-relay v0.95.1, installs the binary, creates directories, and runs the service via runcmd.
Pulumi Integration
main.go
Reads both cloud-init YAMLs before pulumi.Run, adds configureIrohRelayUnyt and configureIrohRelayHc which fetch DigitalOcean SSH keys, create droplets with UserData set to the cloud-init content (IPv6 enabled), and create Cloudflare A/AAAA records pointing to each droplet.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add iroh-relay-unyt deployment' accurately summarizes the main change: adding deployment infrastructure for the iroh-relay-unyt server, which is the primary objective across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/iroh-relay-unyt

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@holochain-release-automation2
Copy link
Copy Markdown
Collaborator

holochain-release-automation2 commented Apr 2, 2026

🍹 preview on network-services/holochain/network-services

Pulumi report

View in Pulumi Cloud

  Previewing update (holochain/network-services)

View Live: https://app.pulumi.com/holochain/network-services/network-services/previews/fbedd832-14b4-45d3-ac17-acdf4111eeda

pulumi:pulumi:Stack: (same)
  [urn=urn:pulumi:network-services::network-services::pulumi:pulumi:Stack::network-services-network-services]
Resources:
  12 unchanged
  

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
main.go (1)

189-242: Consider extracting shared droplet+DNS provisioning helpers.

configureIrohRelayUnyt and configureDevTestBootstrap2Iroh duplicate SSH key collection and A/AAAA record creation logic. A small helper would reduce drift risk for future infra changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@main.go` around lines 189 - 242, Both configureIrohRelayUnyt and
configureDevTestBootstrap2Iroh repeat SSH key collection and A/AAAA record
creation; factor that into a shared helper (e.g., provisionDropletWithDns) that
calls digitalocean.GetSshKeys, creates the Droplet via digitalocean.NewDroplet,
and creates cloudflare.NewRecord A and AAAA records. The helper should accept
parameters like name, image, region, size, tags, userData and zoneId (or
pulumi.Config), return the created Droplet resource (or its IPs) and surface
errors; then replace the duplicated logic in configureIrohRelayUnyt and
configureDevTestBootstrap2Iroh with calls to this new function to centralize
SshKeys handling and DNS record creation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@iroh-relay-unyt/cloud-init.yaml`:
- Around line 8-9: The metrics endpoint is currently bound to metrics_bind_addr
= "[::]:9090", which exposes it publicly; change the binding in the cloud-init
config by updating metrics_bind_addr to a loopback address (e.g.,
"127.0.0.1:9090" or "localhost:9090") or otherwise restrict access via host
firewall/allowlist so the Prometheus/monitoring scrape target is not publicly
reachable; update the metrics_bind_addr setting and ensure any
orchestration/consumers use the new local address.
- Around line 53-57: The install block downloads and installs the iroh-relay
tarball as root without integrity verification; update the cloud-init step that
uses curl /tmp/iroh-relay.tar.gz and install -m 0755 /tmp/iroh-relay
/usr/local/bin/iroh-relay to either (A) verify a published SHA256/GPG signature
before extracting (fail the build if verification fails) or (B) if the upstream
release lacks checksums/signatures, replace the binary install with a
reproducible installer (e.g., cargo install iroh-relay) or pin the exact release
URL and add an explicit comment/log entry documenting the lack of
checksum/signature and the accepted supply-chain risk so operators are aware.

---

Nitpick comments:
In `@main.go`:
- Around line 189-242: Both configureIrohRelayUnyt and
configureDevTestBootstrap2Iroh repeat SSH key collection and A/AAAA record
creation; factor that into a shared helper (e.g., provisionDropletWithDns) that
calls digitalocean.GetSshKeys, creates the Droplet via digitalocean.NewDroplet,
and creates cloudflare.NewRecord A and AAAA records. The helper should accept
parameters like name, image, region, size, tags, userData and zoneId (or
pulumi.Config), return the created Droplet resource (or its IPs) and surface
errors; then replace the duplicated logic in configureIrohRelayUnyt and
configureDevTestBootstrap2Iroh with calls to this new function to centralize
SshKeys handling and DNS record creation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 339b60c6-092d-45db-b4c3-bc637eb9ac45

📥 Commits

Reviewing files that changed from the base of the PR and between a4302fa and ae3b13f.

📒 Files selected for processing (3)
  • .github/workflows/preview.yaml
  • iroh-relay-unyt/cloud-init.yaml
  • main.go

Comment thread iroh-relay-unyt/cloud-init.yaml Outdated
Comment thread iroh-relay-unyt/cloud-init.yaml Outdated
Deploy a standalone Iroh relay server (v0.97.0) for the Unyt project
at iroh-relay-unyt.holochain.org on a DigitalOcean NYC1 droplet with
Cloudflare DNS. Uses the upstream binary directly with a native systemd
service and built-in Let's Encrypt TLS, QUIC address discovery enabled.
@ThetaSinner ThetaSinner force-pushed the feat/iroh-relay-unyt branch from add274d to 7add66a Compare April 2, 2026 15:26
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
main.go (1)

193-201: Consider extracting SSH key fetching to a helper function.

This SSH key fetching and fingerprint extraction pattern is duplicated across four configure* functions. Extracting it would reduce repetition and centralize any future changes.

♻️ Suggested helper function
func getSshFingerprints(ctx *pulumi.Context) ([]string, error) {
	getSshKeysResult, err := digitalocean.GetSshKeys(ctx, &digitalocean.GetSshKeysArgs{}, nil)
	if err != nil {
		return nil, err
	}

	var fingerprints []string
	for _, key := range getSshKeysResult.SshKeys {
		fingerprints = append(fingerprints, key.Fingerprint)
	}
	return fingerprints, nil
}

Then in each configure function:

-	getSshKeysResult, err := digitalocean.GetSshKeys(ctx, &digitalocean.GetSshKeysArgs{}, nil)
-	if err != nil {
-		return err
-	}
-
-	var sshFingerprints []string
-	for _, key := range getSshKeysResult.SshKeys {
-		sshFingerprints = append(sshFingerprints, key.Fingerprint)
-	}
+	sshFingerprints, err := getSshFingerprints(ctx)
+	if err != nil {
+		return err
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@main.go` around lines 193 - 201, The SSH key fetch + fingerprint extraction
(using digitalocean.GetSshKeys, getSshKeysResult and sshFingerprints) is
duplicated across multiple configure* functions; extract it into a single helper
(e.g., getSshFingerprints(ctx *pulumi.Context) ([]string, error)) that calls
digitalocean.GetSshKeys, handles the error, builds and returns the []string of
fingerprints, and then replace the inlined loops in each configure* function
with a call to this helper to remove repetition and centralize future changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@main.go`:
- Around line 193-201: The SSH key fetch + fingerprint extraction (using
digitalocean.GetSshKeys, getSshKeysResult and sshFingerprints) is duplicated
across multiple configure* functions; extract it into a single helper (e.g.,
getSshFingerprints(ctx *pulumi.Context) ([]string, error)) that calls
digitalocean.GetSshKeys, handles the error, builds and returns the []string of
fingerprints, and then replace the inlined loops in each configure* function
with a call to this helper to remove repetition and centralize future changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ad5ad6a7-216b-4443-9dde-a07995083a2e

📥 Commits

Reviewing files that changed from the base of the PR and between ae3b13f and 7add66a.

📒 Files selected for processing (3)
  • .github/workflows/preview.yaml
  • iroh-relay-unyt/cloud-init.yaml
  • main.go
✅ Files skipped from review due to trivial changes (1)
  • .github/workflows/preview.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • iroh-relay-unyt/cloud-init.yaml

@ThetaSinner ThetaSinner requested a review from a team April 3, 2026 00:37
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@iroh-relay-unyt/cloud-init.yaml`:
- Around line 52-58: The PR description incorrectly states v0.97.0 while the
cloud-init script clearly downloads and pins iroh-relay v0.95.1 (see the curl
URL ".../v0.95.1/iroh-relay-v0.95.1..." and the SHA "a7dbc3f7..."), so update
the PR description to state that v0.95.1 is being deployed (and note the
intentional downgrade referenced in the commit), and optionally mention the
pinned SHA256 verification used in the script for clarity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aa3fdefb-600d-4b63-93c7-1361228b57b9

📥 Commits

Reviewing files that changed from the base of the PR and between 7add66a and 05f27a5.

📒 Files selected for processing (3)
  • iroh-relay-hc/cloud-init.yaml
  • iroh-relay-unyt/cloud-init.yaml
  • main.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • main.go

Comment on lines +52 to +58
# Download and install iroh-relay v0.95.1
curl -fsSL https://github.com/n0-computer/iroh/releases/download/v0.95.1/iroh-relay-v0.95.1-x86_64-unknown-linux-musl.tar.gz \
-o /tmp/iroh-relay.tar.gz
echo "a7dbc3f72446c042159da7b10d3947141c88569d6226d9d0a71fb912cd0fe5ba /tmp/iroh-relay.tar.gz" | sha256sum -c -
tar -xzf /tmp/iroh-relay.tar.gz -C /tmp
install -m 0755 /tmp/iroh-relay /usr/local/bin/iroh-relay
rm -f /tmp/iroh-relay.tar.gz /tmp/iroh-relay
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the SHA256 hash matches the actual v0.95.1 release artifact
EXPECTED_HASH="a7dbc3f72446c042159da7b10d3947141c88569d6226d9d0a71fb912cd0fe5ba"
curl -fsSL https://github.com/n0-computer/iroh/releases/download/v0.95.1/iroh-relay-v0.95.1-x86_64-unknown-linux-musl.tar.gz \
  -o /tmp/iroh-relay-verify.tar.gz
ACTUAL_HASH=$(sha256sum /tmp/iroh-relay-verify.tar.gz | cut -d' ' -f1)
echo "Expected: ${EXPECTED_HASH}"
echo "Actual:   ${ACTUAL_HASH}"
if [ "${EXPECTED_HASH}" = "${ACTUAL_HASH}" ]; then
  echo "✓ Hash matches"
else
  echo "✗ Hash mismatch!"
  exit 1
fi
rm -f /tmp/iroh-relay-verify.tar.gz

Repository: holochain/network-services

Length of output: 237


🏁 Script executed:

#!/bin/bash
python3 <<'EOF'
import hashlib
import urllib.request
import sys

EXPECTED_HASH = "a7dbc3f72446c042159da7b10d3947141c88569d6226d9d0a71fb912cd0fe5ba"
URL = "https://github.com/n0-computer/iroh/releases/download/v0.95.1/iroh-relay-v0.95.1-x86_64-unknown-linux-musl.tar.gz"

try:
    print("Downloading iroh v0.95.1 release artifact...")
    with urllib.request.urlopen(URL) as response:
        sha256_hash = hashlib.sha256()
        while True:
            data = response.read(65536)
            if not data:
                break
            sha256_hash.update(data)
    
    actual_hash = sha256_hash.hexdigest()
    print(f"Expected: {EXPECTED_HASH}")
    print(f"Actual:   {actual_hash}")
    
    if EXPECTED_HASH == actual_hash:
        print("✓ Hash matches v0.95.1 release")
        sys.exit(0)
    else:
        print("✗ Hash mismatch!")
        sys.exit(1)
except Exception as e:
    print(f"Error: {e}")
    sys.exit(1)
EOF

Repository: holochain/network-services

Length of output: 249


🌐 Web query:

iroh v0.95.1 release SHA256 hash n0-computer/iroh

💡 Result:

No SHA256 hash for iroh v0.95.1 release was found in available sources. The GitHub releases page mentions "chore: Release iroh-base version 0.95.1" on 21 Nov (likely 2025), but no direct link to /releases/tag/v0.95.1 or asset checksums. A SourceForge mirror exists at https://sourceforge.net/projects/iroh.mirror/files/v0.95.1/ with binaries, but no SHA256 checksums listed. GitHub assets for nearby releases (v0.94.0, v0.96.0, v0.97.0) also lack explicit SHA256 hashes in snippets. The project uses BLAKE3 internally, but binaries typically provide SHA256 for verification.

Citations:


Update PR description to reflect v0.95.1 deployment.

The code downloads v0.95.1, not v0.97.0 as mentioned in the PR description. The commit message confirms this is an intentional downgrade. Update the PR description to match the actual deployed version.

The SHA256 verification approach is correct: iroh does not publish checksums, so using a self-computed hash pinned in the script is the appropriate verification method.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@iroh-relay-unyt/cloud-init.yaml` around lines 52 - 58, The PR description
incorrectly states v0.97.0 while the cloud-init script clearly downloads and
pins iroh-relay v0.95.1 (see the curl URL ".../v0.95.1/iroh-relay-v0.95.1..."
and the SHA "a7dbc3f7..."), so update the PR description to state that v0.95.1
is being deployed (and note the intentional downgrade referenced in the commit),
and optionally mention the pinned SHA256 verification used in the script for
clarity.

@cocogitto-bot
Copy link
Copy Markdown

cocogitto-bot bot commented Apr 7, 2026

✔️ 7add66a...cd41b64 - Conventional commits check succeeded.

@ThetaSinner ThetaSinner merged commit 3fd098f into main Apr 8, 2026
5 checks passed
@ThetaSinner ThetaSinner deleted the feat/iroh-relay-unyt branch April 8, 2026 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants