Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
37e8413
Add Garlic Routing Overlay architecture doc and envelope wire format
Aug 8, 2026
56c56d9
Add Garlic Envelope cryptographic primitives (Phase 3)
Aug 8, 2026
a4d46af
Add layered (onion) encryption for Garlic circuits (Phase 4)
Aug 8, 2026
6cf38a9
Wire an optional Garlic transport tag into core.Core (Phase 6/7)
Aug 8, 2026
97bea98
Add circuit state and bounded replay protection (Phase 5)
Aug 8, 2026
8b8a350
Add long-term identity, GID, and rendezvous abstraction (Phase 8-10)
Aug 8, 2026
c742e92
Add bundling and packet size normalization (Phase 11)
Aug 8, 2026
e4299dc
Add per-peer rate limiting (Phase 12)
Aug 8, 2026
788d29b
Circuit.Seal: also return the shared per-call packet counter
Aug 8, 2026
4b19596
Add capability negotiation message format (Phase 6)
Aug 8, 2026
388d64c
Add bounded relay-side per-circuit replay state
Aug 8, 2026
829875f
Add the Garlic manager: ties every prior phase to a running core.Core
Aug 8, 2026
3b20d5e
Add end-to-end integration test through two legacy nodes (Phase 13)
Aug 8, 2026
ff28b10
Add LoadIdentityFromPrivateKey (derive public key from private key)
Aug 8, 2026
715281c
Wire Garlic into the yggdrasil daemon: config, admin API, main.go
Aug 8, 2026
f7b01da
Add adversarial fuzz tests for every untrusted-input parser (Phase 13)
Aug 8, 2026
ec9d419
Add benchmarks for the per-packet crypto/protocol hot path (Phase 14)
Aug 8, 2026
0f65778
Add remaining Garlic documentation set
Aug 8, 2026
0da45ab
Fix admin handlers to accept list/numeric args as strings
Aug 8, 2026
108503b
Add docs/garlic-testing.md: verified real-network testing walkthrough
Aug 8, 2026
5539d0c
Correct threat model: ironwood's network layer exposes real src/dst keys
Aug 9, 2026
bb91202
Add per-hop packet size randomization against traffic correlation
Aug 9, 2026
b8e9c77
Add random pre-send jitter against timing correlation
Aug 9, 2026
c4ee0ff
Add PingCapability (RTT) and HopCount helpers for diverse hop selection
Aug 9, 2026
5758cd8
Add gossip-based peer discovery restricted to verified Garlic nodes
Aug 9, 2026
3898ae5
Add topology-aware diverse hop selection (Sybil mitigation)
Aug 9, 2026
f9b5a17
Add multipath circuit pools (spread traffic across independent paths)
Aug 9, 2026
0c06774
garlic: wire real bundling into the send path for cover traffic
Aug 9, 2026
36c42ec
docs: update Garlic docs for padding/jitter/discovery/selection/multi…
Aug 9, 2026
2faf038
Add .rpm packaging and a one-command install.sh; document Garlic in R…
Aug 9, 2026
20f4349
Add design spec for yggdashboard
Aug 9, 2026
07a9834
Add implementation plan for yggdashboard Phase 1
Aug 9, 2026
ad9adb4
Add design spec for Garlic crypto/protocol hardening (Parts 1-6)
Aug 9, 2026
89230c3
Add implementation plan for Garlic crypto/protocol hardening
Aug 9, 2026
e24f3c3
garlic: widen CircuitID to 128-bit random
Aug 9, 2026
41fc752
garlic: two-stage HKDF key derivation with reserved direction labels
Aug 9, 2026
ba14b6a
garlic: add per-hop NextHopEphemeral to LayerPlaintext
Aug 9, 2026
0aa062a
garlic: chained per-hop ephemeral keys - fixes cross-hop ephemeral-ke…
Aug 9, 2026
75ea4de
garlic: fix stale fuzz seed key derivation, add NextHopEphemeral guar…
Aug 9, 2026
b6c1b4f
garlic: bump capability version to garlic-v2
Aug 9, 2026
80b391c
garlic: add independent Ed25519 signing identity for service descriptors
Aug 9, 2026
c75ae17
garlic: add signed ServiceDescriptor type
Aug 9, 2026
4514e27
garlic: authenticate service descriptors end to end (Rendezvous, Publ…
Aug 9, 2026
ac0a078
garlic: guard against circuit ID collisions; document replay-cache ev…
Aug 9, 2026
8521f07
garlic: add fuzz coverage for LayerPlaintext and ServiceDescriptor pa…
Aug 9, 2026
74c3b27
garlic: fix misplaced doc comment in fuzz_test.go
Aug 9, 2026
2a58880
docs: update Garlic threat model for the crypto hardening pass
Aug 9, 2026
af18dec
docs: update Garlic protocol spec for wire format changes (garlic-v2)
Aug 10, 2026
c9812b3
docs: terminology/staleness sweep across garlic-architecture.md, garl…
Aug 10, 2026
4f266c3
docs: fix overclaimed garlic-v1/v2 negotiation enforcement in archite…
Aug 10, 2026
735b354
docs: fix second overclaim about discovery-path capability enforcemen…
Aug 10, 2026
5f49685
garlic: fix stale package doc comment describing pre-hardening epheme…
Aug 10, 2026
cae573d
garlic: enforce MaxDescriptorLifetime and reject future-dated descrip…
Aug 10, 2026
124a27e
garlic, cmd/yggdrasil: preserve existing X25519 identity on the Signi…
Aug 10, 2026
67f6013
garlic: expose signing public key from getGarlicIdentity admin handler
Aug 10, 2026
4800c0e
Merge pull request #1 from luisakrivonogih/worktree-garlic-crypto-har…
luisakrivonogih Aug 10, 2026
8c6c412
Add design spec for yggdashboard v2 (local operator dashboard)
Aug 10, 2026
f501e03
Add implementation plan for yggdashboard v2
Aug 10, 2026
761dd6b
config: add Dashboard config block, disabled/loopback by default
Aug 10, 2026
93d511b
core, admin: add node uptime, expose via getSelf
Aug 10, 2026
d174e75
garlic: add read-only Circuit accessors for hops, traffic, closed state
Aug 10, 2026
1e40a6d
garlic: add CircuitManager.List for admin-facing circuit enumeration
Aug 10, 2026
a6193e3
garlic: add local-only security drop counters, never sent over the wire
Aug 10, 2026
d5d0d6e
garlic: track previous/next hop and traffic per relayed circuit
Aug 10, 2026
5ab418b
garlic: expose traffic/security totals and circuit listing over the a…
Aug 10, 2026
95d891e
garlic: strengthen getGarlicCircuits no-secret-leak test with a popul…
Aug 10, 2026
8e3c36f
dashboard: add Node.js dashboard process supervisor package
Aug 11, 2026
305685c
dashboard: reap child process in Stop() to avoid zombie/goroutine leak
Aug 11, 2026
c942968
cmd/yggdrasil: spawn the dashboard subprocess when enabled
Aug 11, 2026
5084bf8
yggdashboard: scaffold SvelteKit 5 project
Aug 11, 2026
18ca204
yggdashboard: add admin socket protocol client (JSON framing, keepali…
Aug 11, 2026
5bce2e1
yggdashboard: fix admin-client race between connect and request regis…
Aug 11, 2026
29325aa
yggdashboard: fix admin-client sync-throw hang and stale-socket state…
Aug 11, 2026
52af8e8
yggdashboard: add wire types and env-based config
Aug 11, 2026
ac5dd2f
yggdashboard: add poller with bounded history and Garlic-disabled han…
Aug 11, 2026
95ebd5a
yggdashboard: fix poller history mutation and in-flight-tick-after-st…
Aug 11, 2026
4d5c2ee
yggdashboard: add shared poller instance, response builders, and /api…
Aug 11, 2026
94764f5
yggdashboard: hand-pick Garlic stats/knownPeers/circuits fields, add …
Aug 11, 2026
eb0128e
yggdashboard: add client API types and reactive polled-resource store
Aug 11, 2026
46172ed
yggdashboard: add format helpers, style tokens, and shared status/met…
Aug 11, 2026
997213c
yggdashboard: guard CopyableKey's clipboard write against rejection
Aug 12, 2026
5810dc1
yggdashboard: add nav/status bar layout and the overview page
Aug 12, 2026
84944cd
yggdashboard: add connections page with sortable/filterable peer tabl…
Aug 12, 2026
6883bc4
yggdashboard: track selected peer by key so the detail panel reflects…
Aug 12, 2026
698829f
yggdashboard: add circuits page, respecting the originator-vs-relay v…
Aug 12, 2026
d6b381d
yggdashboard: add Garlic overview page with identity, circuits, and s…
Aug 12, 2026
7e36692
yggdashboard: add network graph page (Yggdrasil + Garlic layers, d3-f…
Aug 12, 2026
cc39541
yggdashboard: add builder unit tests and empty/disabled-state compone…
Aug 12, 2026
e7a4bfb
yggdashboard: add README and complete end-to-end verification
Aug 18, 2026
c38ed23
yggdashboard: fix final-review findings - disconnected status, poller…
Aug 18, 2026
cedd8fd
Merge remote-tracking branch 'fork/develop' into develop
Aug 18, 2026
20c8e6b
install.sh: build, install, and enable the operator dashboard
Aug 18, 2026
5262de1
docs: add design spec for Garlic autonomous routing (auto-discovery, …
Aug 23, 2026
e48d54f
docs: add implementation plan for Garlic autonomous routing
Aug 23, 2026
de98cfe
chore: ignore .worktrees/ (SDD isolated workspace directory)
Aug 23, 2026
f551eb4
garlic: add SelfVerified trust tier to discovered peers
Aug 23, 2026
5b22ca8
garlic: carry SelfVerified through candidatePool, tag verified/gossip…
Aug 23, 2026
75d7ef4
garlic: add SelectPathWithGuardPolicy (self-verified first hop)
Aug 23, 2026
f79fc85
garlic: add CapabilityAutoCircuit flag, advertise unconditionally
Aug 23, 2026
203a943
garlic: add gossip-pull wire message and msgTypeCircuitDataV3 tagged …
Aug 23, 2026
6282ce8
garlic: add tagged auto-pool delivery channel and send helper
Aug 23, 2026
78df8ad
garlic: add autoPool field to Garlic struct (Task 7 completeness fix)
Aug 23, 2026
8287ec1
garlic: add Config.BootstrapPeers, resolved at startup
Aug 23, 2026
c7ff08d
garlic: add AutoCreateCircuit
Aug 23, 2026
9c5dde9
garlic: add auto-pool fill/rotate, cover traffic, and background loop…
Aug 23, 2026
51ce891
garlic: add auto-pool/gossip-pull admin RPCs, selfVerified in getGarl…
Aug 23, 2026
a99240a
config: add Garlic auto-discovery/auto-pool/cover-traffic settings
Aug 23, 2026
15fc616
cmd/yggdrasil: wire Garlic auto-pool/bootstrap/cover-traffic config
Aug 23, 2026
39a30d7
install.sh: support GARLIC_BOOTSTRAP_PEERS for multi-server bootstrap
Aug 23, 2026
de461dd
yggdashboard: show self-verified/gossiped badge and auto-pool status
Aug 23, 2026
7d6ac87
docs: document Garlic auto-discovery/auto-pool/cover-traffic wire add…
Aug 23, 2026
2541929
config: gofmt GarlicConfig and fix literal %% in a config comment
Aug 23, 2026
f1d9b55
garlic: fix auto-pool phantom entries, unsolicited self-verification,…
Aug 23, 2026
6f959a7
docs: correct auto-pool hop verification, self-verified erosion, and …
Aug 23, 2026
9985aea
garlic: stop the maintenance ticker starving the cover-traffic timer
Aug 24, 2026
1dcb75b
garlic: fill the auto-pool one circuit at a time, not in one burst
Aug 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

.worktrees/
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,46 @@ allows pretty much any IPv6-capable application to communicate securely with
other Yggdrasil nodes. Yggdrasil does not require you to have IPv6 Internet
connectivity - it also works over IPv4.

## Garlic Routing Overlay (experimental, this branch)

This branch adds an experimental, optional privacy-enhanced routing layer on
top of Yggdrasil: onion/garlic-style circuits, capability negotiation,
per-hop packet-size and timing randomization, gossip-based peer discovery,
topologically diverse hop selection, multipath circuits, and cover-traffic
bundling. It is fully backward compatible - a node with `Garlic.Enabled:
false` (the default) behaves exactly like vanilla Yggdrasil, and ordinary
Yggdrasil nodes transparently carry Garlic traffic without needing to know
it exists or upgrading anything.

Start here:

- [docs/garlic-architecture.md](docs/garlic-architecture.md) - design and integration rationale
- [docs/garlic-protocol.md](docs/garlic-protocol.md) - wire format, what's actually implemented
- [docs/garlic-threat-model.md](docs/garlic-threat-model.md) - what this does and does not protect against (read before relying on it for anything)
- [docs/garlic-security.md](docs/garlic-security.md) - self-review of the implementation
- [docs/garlic-compatibility.md](docs/garlic-compatibility.md) - why old and new nodes keep interoperating
- [docs/garlic-testing.md](docs/garlic-testing.md) - manual walkthrough via `yggdrasilctl`

### Quick install for testing

To build, package, install, and enable Garlic on a Linux server in one
step (auto-detects Debian/Ubuntu-family `apt`/`.deb` vs Fedora/RHEL/CentOS-family
`dnf`/`yum`/`.rpm`):

```sh
curl -fsSL https://raw.githubusercontent.com/luisakrivonogih/yggdrasil-go/develop/install.sh | sudo sh
```

This builds an actual `.deb` or `.rpm` from source, installs it the same
way the official packages install (systemd service,
`/etc/yggdrasil/yggdrasil.conf`), sets `Garlic.Enabled: true` in the
generated config, restarts the service, and prints the resulting Garlic
identity and stats so you can confirm it actually started. See
[install.sh](install.sh) for the environment variables it honors
(`REPO_URL`, `REPO_BRANCH`, `WORKDIR`, `ENABLE_GARLIC`), and
[docs/garlic-testing.md](docs/garlic-testing.md) for how to build a circuit
and send traffic through it once the service is running.

## Supported Platforms

Yggdrasil works on a number of platforms, including Linux, macOS, Ubiquiti
Expand Down
106 changes: 106 additions & 0 deletions cmd/yggdrasil/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"regexp"
"strings"
"syscall"
"time"

"suah.dev/protect"

Expand All @@ -24,9 +25,11 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
"github.com/yggdrasil-network/yggdrasil-go/src/garlic"
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"

"github.com/yggdrasil-network/yggdrasil-go/src/core"
"github.com/yggdrasil-network/yggdrasil-go/src/dashboard"
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
"github.com/yggdrasil-network/yggdrasil-go/src/tun"
"github.com/yggdrasil-network/yggdrasil-go/src/version"
Expand All @@ -37,6 +40,8 @@ type node struct {
tun *tun.TunAdapter
multicast *multicast.Multicast
admin *admin.AdminSocket
garlic *garlic.Garlic
dashboard *dashboard.Process
}

// The main function is responsible for configuring and starting Yggdrasil.
Expand Down Expand Up @@ -286,6 +291,93 @@ func main() {
}
}

// Set up the Garlic Routing Overlay (experimental, optional). When
// cfg.Garlic.Enabled is false (the default), this block does nothing
// and n.garlic stays nil - behavior is identical to a build with no
// Garlic support at all. See docs/garlic-architecture.md.
{
if cfg.Garlic.Enabled {
var identity *garlic.Identity
switch {
case len(cfg.Garlic.PrivateKey) > 0 && len(cfg.Garlic.SigningPrivateKey) > 0:
if identity, err = garlic.LoadIdentityFromPrivateKeys(cfg.Garlic.PrivateKey, cfg.Garlic.SigningPrivateKey); err != nil {
panic(err)
}
case len(cfg.Garlic.PrivateKey) > 0:
// Upgrade path: a node that was already running Garlic
// before Garlic.SigningPrivateKey existed has
// Garlic.PrivateKey configured but not the new signing
// key. Keep the existing X25519 identity stable and
// generate only a fresh signing identity for this run -
// regenerating both would silently reset an already-
// stable Garlic identity on every restart, not just add
// a new one.
if identity, err = garlic.LoadIdentityFromPrivateKey(cfg.Garlic.PrivateKey); err != nil {
panic(err)
}
logger.Warnln("Garlic.PrivateKey configured but no Garlic.SigningPrivateKey - generated a fresh signing identity for this run only; your Garlic X25519 identity remains stable")
default:
if identity, err = garlic.NewIdentity(); err != nil {
panic(err)
}
logger.Warnln("No Garlic.PrivateKey/SigningPrivateKey configured - generated ephemeral Garlic identity keys for this run only")
}
lifetime, err := time.ParseDuration(cfg.Garlic.CircuitLifetime)
if err != nil {
panic(fmt.Sprintf("invalid Garlic.CircuitLifetime %q: %v", cfg.Garlic.CircuitLifetime, err))
}
gcfg := garlic.DefaultConfig()
gcfg.PathLength = cfg.Garlic.PathLength
gcfg.CircuitLifetime = lifetime
gcfg.MaxCircuits = cfg.Garlic.MaxCircuits
gcfg.MaxCircuitsPerPeer = cfg.Garlic.MaxCircuitsPerPeer
gcfg.MaxRelayCircuits = cfg.Garlic.MaxRelayCircuits
gcfg.PaddingEnabled = cfg.Garlic.Padding.Enabled
gcfg.MinPaddedSize = cfg.Garlic.Padding.MinSize
gcfg.MaxPaddedSize = cfg.Garlic.Padding.MaxSize
gcfg.JitterEnabled = cfg.Garlic.Jitter.Enabled
if gcfg.MinJitter, err = time.ParseDuration(cfg.Garlic.Jitter.MinDelay); err != nil {
panic(fmt.Sprintf("invalid Garlic.Jitter.MinDelay %q: %v", cfg.Garlic.Jitter.MinDelay, err))
}
if gcfg.MaxJitter, err = time.ParseDuration(cfg.Garlic.Jitter.MaxDelay); err != nil {
panic(fmt.Sprintf("invalid Garlic.Jitter.MaxDelay %q: %v", cfg.Garlic.Jitter.MaxDelay, err))
}
gcfg.MaxDiscoveredPeers = cfg.Garlic.MaxDiscoveredPeers
gcfg.MinHopCount = cfg.Garlic.MinHopCount
gcfg.BootstrapPeers = cfg.Garlic.BootstrapPeers
gcfg.AutoPoolEnabled = cfg.Garlic.AutoPoolEnabled
gcfg.AutoPoolSize = cfg.Garlic.AutoPoolSize
if gcfg.AutoRotationInterval, err = time.ParseDuration(cfg.Garlic.AutoRotationInterval); err != nil {
panic(fmt.Sprintf("invalid Garlic.AutoRotationInterval %q: %v", cfg.Garlic.AutoRotationInterval, err))
}
gcfg.CoverTrafficEnabled = cfg.Garlic.CoverTrafficEnabled
if gcfg.CoverTrafficInterval, err = time.ParseDuration(cfg.Garlic.CoverTrafficInterval); err != nil {
panic(fmt.Sprintf("invalid Garlic.CoverTrafficInterval %q: %v", cfg.Garlic.CoverTrafficInterval, err))
}
n.garlic = garlic.New(n.core, identity, gcfg, garlic.NewStaticRendezvous())
logger.Printf("Your Garlic public key is %s", hex.EncodeToString(identity.PublicKey))
if n.admin != nil {
n.garlic.SetupAdminHandlers(n.admin)
}
}
}

// Set up the local operator dashboard (optional, disabled by
// default). A failure here is always a warning, never fatal - the
// dashboard must never be the reason yggdrasil itself won't start.
{
if cfg.Dashboard.Enabled {
dcfg := dashboard.Config{
Listen: cfg.Dashboard.Listen,
Path: cfg.Dashboard.Path,
AdminListen: cfg.AdminListen,
}
if n.dashboard, err = dashboard.Start(dcfg, logger); err != nil {
logger.Warnln("Dashboard not started:", err)
}
}
}

//Windows service shutdown
minwinsvc.SetOnExit(func() {
logger.Infof("Shutting down service ...")
Expand Down Expand Up @@ -313,6 +405,14 @@ func main() {
if len(cfg.MulticastInterfaces) > 0 {
promises = append(promises, "mcast")
}
if cfg.Dashboard.Enabled {
// Only relevant on OpenBSD, where protect.Pledge actually
// enforces this - "proc" is needed to signal/wait on the
// already-spawned dashboard child process at shutdown. The
// exec() itself already happened above, before this pledge
// call, so "exec" doesn't need to be a standing promise.
promises = append(promises, "proc")
}
if err := protect.Pledge(strings.Join(promises, " ")); err != nil {
panic(fmt.Sprintf("pledge: %v: %v", promises, err))
}
Expand All @@ -327,6 +427,12 @@ func main() {
<-ctx.Done()

// Shut down the node.
if n.dashboard != nil {
_ = n.dashboard.Stop()
}
if n.garlic != nil {
n.garlic.Close()
}
_ = n.admin.Stop()
_ = n.multicast.Stop()
_ = n.tun.Stop()
Expand Down
4 changes: 2 additions & 2 deletions contrib/deb/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ then
fi

PKGBRANCH=$(basename `git name-rev --name-only HEAD`)
PKGNAME=$(sh contrib/semver/name.sh)
PKGVERSION=$(sh contrib/semver/version.sh --bare)
PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
PKGVERSION=${PKGVERSION:-$(sh contrib/semver/version.sh --bare)}
PKGARCH=${PKGARCH-amd64}
PKGFILE=$PKGNAME-$PKGVERSION-$PKGARCH.deb
PKGREPLACES=yggdrasil
Expand Down
149 changes: 149 additions & 0 deletions contrib/rpm/generate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/bin/sh

# This is a lazy script to create an .rpm for Fedora/RHEL/CentOS and other
# rpm-based distributions. It installs yggdrasil and enables it in systemd.
# Mirrors contrib/deb/generate.sh - same build, same /etc/yggdrasil layout,
# same systemd units - just packaged as an rpm instead of a deb. You can
# give it the PKGARCH= argument, using the same values as the deb script,
# i.e. PKGARCH=arm64 sh contrib/rpm/generate.sh
#
# Requires rpmbuild (the "rpm-build" package on Fedora/RHEL/CentOS).

if [ `pwd` != `git rev-parse --show-toplevel` ]
then
echo "You should run this script from the top-level directory of the git repo"
exit 1
fi

if ! command -v rpmbuild >/dev/null 2>&1; then
echo "rpmbuild not found - install it first, e.g.:"
echo " dnf install -y rpm-build (Fedora/RHEL/CentOS)"
echo " zypper install -y rpm-build (openSUSE)"
exit 1
fi

PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
PKGVERSION=${PKGVERSION:-$(sh contrib/semver/version.sh --bare)}
PKGARCH=${PKGARCH-amd64}

# RPM's Version/Release fields can't contain "-". git describe --bare gives
# e.g. "0.5.14-29-g36c42ec" for a dev build (29 commits past tag v0.5.14) or
# just "0.5.14" on an exact tag - split that into a valid Version+Release.
RPMVERSION=$(echo "$PKGVERSION" | cut -d- -f1)
RPMRELEASE=$(echo "$PKGVERSION" | sed "s/^$RPMVERSION-\{0,1\}//" | sed 's/-/./g')
if [ -z "$RPMRELEASE" ]; then RPMRELEASE=1; fi

GOLDFLAGS="-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultConfig=/etc/yggdrasil/yggdrasil.conf"
GOLDFLAGS="${GOLDFLAGS} -X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix:///var/run/yggdrasil/yggdrasil.sock"

# Same PKGARCH vocabulary as contrib/deb/generate.sh; translated to the
# native rpm arch name for the package metadata/filename below.
if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build -l "${GOLDFLAGS}"; RPMARCH=x86_64
elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build -l "${GOLDFLAGS}"; RPMARCH=i686
elif [ $PKGARCH = "mipsel" ]; then GOARCH=mipsle GOOS=linux ./build -l "${GOLDFLAGS}"; RPMARCH=mipsel
elif [ $PKGARCH = "mips" ]; then GOARCH=mips64 GOOS=linux ./build -l "${GOLDFLAGS}"; RPMARCH=mips64
elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=6 ./build -l "${GOLDFLAGS}"; RPMARCH=armv6hl
elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build -l "${GOLDFLAGS}"; RPMARCH=aarch64
elif [ $PKGARCH = "armel" ]; then GOARCH=arm GOOS=linux GOARM=5 ./build -l "${GOLDFLAGS}"; RPMARCH=armv5tel
else
echo "Specify PKGARCH=amd64,i386,mips,mipsel,armhf,arm64,armel"
exit 1
fi

PKGFILE=$PKGNAME-$RPMVERSION-$RPMRELEASE.$RPMARCH.rpm
echo "Building $PKGFILE"

TOPDIR=/tmp/$PKGNAME-rpmbuild
rm -rf $TOPDIR
mkdir -p $TOPDIR/BUILD $TOPDIR/RPMS $TOPDIR/SOURCES $TOPDIR/SPECS $TOPDIR/SRPMS $TOPDIR/BUILDROOT

# Binaries and units already built above - this spec only packages them, it
# does not compile anything itself (rpmbuild has no Go toolchain dependency
# this way, same philosophy as the deb script's hand-rolled data.tar.gz).
cat > $TOPDIR/SPECS/$PKGNAME.spec << EOF
Name: $PKGNAME
Version: $RPMVERSION
Release: $RPMRELEASE
Summary: Yggdrasil Network
License: LGPLv3
URL: https://github.com/yggdrasil-network/yggdrasil-go/
Requires: systemd
# Statically-linked-ish Go binary - rpm's automatic dependency scanner has
# nothing useful to add here and can misfire on Go's ELF metadata.
AutoReqProv: no

%description
Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6
network. It is lightweight, self-arranging, supported on multiple platforms and
allows pretty much any IPv6-capable application to communicate securely with
other Yggdrasil nodes.

%install
mkdir -p %{buildroot}/usr/bin
mkdir -p %{buildroot}/usr/lib/systemd/system
install -m 0755 $PWD/yggdrasil %{buildroot}/usr/bin/yggdrasil
install -m 0755 $PWD/yggdrasilctl %{buildroot}/usr/bin/yggdrasilctl
install -m 0644 $PWD/contrib/systemd/yggdrasil.service.debian %{buildroot}/usr/lib/systemd/system/yggdrasil.service
install -m 0644 $PWD/contrib/systemd/yggdrasil-default-config.service.debian %{buildroot}/usr/lib/systemd/system/yggdrasil-default-config.service

%files
/usr/bin/yggdrasil
/usr/bin/yggdrasilctl
/usr/lib/systemd/system/yggdrasil.service
/usr/lib/systemd/system/yggdrasil-default-config.service

%pre
getent group yggdrasil >/dev/null || groupadd --system yggdrasil || true
exit 0

%post
systemctl daemon-reload >/dev/null 2>&1 || true

if [ ! -d /etc/yggdrasil ]; then
mkdir -p /etc/yggdrasil
chown root:yggdrasil /etc/yggdrasil
chmod 750 /etc/yggdrasil
fi

if [ -f /etc/yggdrasil/yggdrasil.conf ]; then
mkdir -p /var/backups
echo "Backing up configuration file to /var/backups/yggdrasil.conf.\`date +%Y%m%d\`"
cp /etc/yggdrasil/yggdrasil.conf /var/backups/yggdrasil.conf.\`date +%Y%m%d\`

echo "Normalising and updating /etc/yggdrasil/yggdrasil.conf"
/usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.\`date +%Y%m%d\` > /etc/yggdrasil/yggdrasil.conf

chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf
chmod 640 /etc/yggdrasil/yggdrasil.conf
else
echo "Generating initial configuration file /etc/yggdrasil/yggdrasil.conf"
(umask 037 && /usr/bin/yggdrasil -genconf > /etc/yggdrasil/yggdrasil.conf)

chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf
chmod 640 /etc/yggdrasil/yggdrasil.conf
fi

systemctl enable yggdrasil >/dev/null 2>&1 || true
systemctl restart yggdrasil >/dev/null 2>&1 || true
exit 0

%preun
if [ "\$1" = "0" ]; then
if command -v systemctl >/dev/null; then
systemctl stop yggdrasil >/dev/null 2>&1 || true
systemctl disable yggdrasil >/dev/null 2>&1 || true
fi
fi
exit 0

%changelog
* $(date "+%a %b %d %Y") Yggdrasil <noreply@yggdrasil-network.github.io> - $RPMVERSION-$RPMRELEASE
- See https://github.com/yggdrasil-network/yggdrasil-go/blob/develop/CHANGELOG.md
EOF

rpmbuild --define "_topdir $TOPDIR" --target "$RPMARCH" -bb "$TOPDIR/SPECS/$PKGNAME.spec"

find $TOPDIR/RPMS -name '*.rpm' -exec cp {} "./$PKGFILE" \;
rm -rf $TOPDIR

echo "Built $PKGFILE"
Loading
Loading