--- /dev/null
+#!rsc by Vados
+# RouterOS script: AM-awg3vdsInstall
+# Script comment: awg3vds installer
+#
+#
+# requires RouterOS, version=7.19
+# requires device-mode, fetch, scheduler
+# ============================================================
+# AWG Proxy -- MikroTik RouterOS (protocol: v3)
+# 2026-09-12 08:41:48
+# ============================================================
+# 0. Check prerequisites
+:if ([:len [/system/package/find where name="container" disabled=no]] = 0) do={
+ :put "Container package is not installed or disabled. Install it and reboot."
+ :error "Usage: /system/device-mode/update container=yes"
+}
+
+# Check storage device
+{
+ :local targetDisk "usb1"
+ :local diskFree 0
+ :do { :set diskFree [/disk/get [find where mount-point=$targetDisk] free] } on-error={}
+ :if ($diskFree = 0) do={
+ :local baseDisk $targetDisk
+ :local dashPos [:find $targetDisk "-part"]
+ :if ([:typeof $dashPos] != "nil") do={ :set baseDisk [:pick $targetDisk 0 $dashPos] }
+ :if ([:len [/disk/find where slot=$baseDisk]] > 0) do={
+ :put ("Device $baseDisk found but $targetDisk is not available.")
+ :put "Format $baseDisk as ext4? ALL DATA WILL BE ERASED!"
+ :put "Press 'y' to confirm, any other key to cancel:"
+ :local key [/terminal/inkey timeout=30]
+ :if ($key = 121 || $key = 89) do={
+ :put "Formatting $baseDisk as ext4..."
+ /disk/format-drive $baseDisk file-system=ext4 label=$baseDisk
+ :delay 3s
+ :do { :set diskFree [/disk/get [find where mount-point=$targetDisk] free] } on-error={}
+ :if ($diskFree = 0) do={
+ :put ("ERROR: $targetDisk not found after formatting.")
+ :put "Check mount point: /disk/print"
+ :error ("Mount point $targetDisk not found after formatting")
+ }
+ :put ("Formatted OK. Free: " . ($diskFree / 1048576) . " MB")
+ } else={
+ :error "Formatting cancelled"
+ }
+ } else={
+ :put ("ERROR: Storage device $targetDisk not found.")
+ :put "Check available disks: /disk/print"
+ :error ("Storage not found: " . $targetDisk)
+ }
+ } else={
+ :put ("Storage OK: $targetDisk (" . ($diskFree / 1048576) . " MB free)")
+ }
+}
+
+# 1. Uninstall and update scripts
+/system/script/add name=awg3vds-uninstall comment=awg3vds source={
+ :put "Uninstallimg AmneziaWG..."
+ :log info "Uninstallimg AmneziaWG..."
+ :local prevDNS ""
+ :do { :set prevDNS [/container/envs/get [find where list="awg3vds-env" key="AWG_PREV_DNS"] value] } on-error={}
+ :local prevTmpdir ""
+ :do { :set prevTmpdir [/container/envs/get [find where list="awg3vds-env" key="AWG_PREV_TMPDIR"] value] } on-error={}
+ /ip/route/remove [find where comment=awg3vds-tunnel]
+ /ip/route/remove [find where comment=awg3vds-dns]
+ :do { /system/scheduler/remove [find where comment~"awg3vds"] } on-error={}
+ :do { /system/script/remove [find where comment~"awg3vds" and name!="awg3vds-uninstall"] } on-error={}
+ /container/stop [find where interface=veth-awg3vds]
+ :delay 7s
+ /container/remove [find where interface=veth-awg3vds]
+ /container/envs/remove [find where list="awg3vds-env"]
+ :if ($prevDNS != "") do={
+ :do { /ip/dns/set servers="$prevDNS" } on-error={}
+ :put ("DNS restored to: $prevDNS")
+ }
+ /container/config set tmpdir=$prevTmpdir
+ :put ("Container tmpdir restored to: $prevTmpdir")
+ /ip/firewall/nat/remove [find where out-interface="wg-awg3vds"]
+ /ip/firewall/nat/remove [find where src-address="172.19.14.100/30"]
+ /ip/address/remove [find where address="172.19.14.101/30"]
+ :do { /ipv6/firewall/nat/remove [find where comment=awg3vds-v6-nat] } on-error={}
+ :do { /ipv6/firewall/nat/remove [find where comment=awg3vds-dstnat6] } on-error={}
+ :do { /ipv6/firewall/filter/remove [find where comment=awg3vds-v6-out] } on-error={}
+ :do { /ipv6/firewall/filter/remove [find where comment=awg3vds-awg-in6] } on-error={}
+ :do { /ipv6/nd/remove [find where interface=veth-awg3vds] } on-error={}
+ :do { /ipv6/nd/prefix/remove [find where interface=veth-awg3vds] } on-error={}
+ :do { /ipv6/address/remove [find where comment=awg3vds-v6] } on-error={}
+ /interface/veth/remove [find where name="veth-awg3vds"]
+ /interface/wireguard/peers/remove [find where interface="wg-awg3vds"]
+ /ip/address/remove [find where interface="wg-awg3vds"]
+ /interface/wireguard/remove [find where name="wg-awg3vds"]
+ :do { /file/remove [find where name~"awg3vds.+tar"] } on-error={}
+ :do { /file/remove [find where name="usb1/awg3vds"] } on-error={}
+ :do { /file/remove [find where name="usb1/ru_ranges_timeout.rsc"] } on-error={}
+ :do { /file/remove [find where name="usb1/ru_ranges.rsc"] } on-error={}
+ :do { /file/remove [find where name~"usb1/pull"] } on-error={}
+ :put "Uninstall AmneziaWG Proxy complete!"
+ :log info "Uninstall AmneziaWG Proxy complete!"
+# /system/script/remove [find where name=awg3vds-uninstall]
+}
+/system/script/add name=awg3vds-update comment=awg3vds source={
+ :local cids [/container/find where interface=veth-awg3vds and !stopped]
+ :if ([:len $cids] = 0) do={ :set cids [/container/find where interface=veth-awg3vds and start-on-boot=yes] }
+ :if ([:len $cids] = 0) do={ :set cids [/container/find where interface=veth-awg3vds] }
+ :if ([:len $cids] = 0) do={
+ :put "Container awg3vds not found"
+ :log warning "awg3vds-update: container not found"
+ } else={
+ :local cid [:pick $cids ([:len $cids] - 1)]
+ :local img [/container/get $cid remote-image]
+ :local ver [/system/resource/get version]
+ :local dotPos [:find $ver "."]
+ :local rest [:pick $ver ($dotPos + 1) [:len $ver]]
+ :local endPos [:find $rest "."]
+ :if ([:typeof $endPos] = "nil") do={ :set endPos [:find $rest " "] }
+ :local minor [:tonum [:pick $rest 0 $endPos]]
+ :local wantRunning true
+ :do { :if ([/container/get $cid start-on-boot] != true) do={ :set wantRunning false } } on-error={}
+ :local healthy true
+ :if ([:len [/container/get $cid image-id]] = 0) do={ :set healthy false }
+ :if ($wantRunning) do={
+ :do { :if ([/container/get $cid stopped] = true) do={ :set healthy false } } on-error={}
+ }
+ :if (!$healthy && $wantRunning && [:len [/container/get $cid image-id]] > 0) do={
+ :put "Container is down - trying to start it"
+ :do { /container/start $cid } on-error={}
+ :delay 8s
+ :set healthy true
+ :do { :if ([/container/get $cid stopped] = true) do={ :set healthy false } } on-error={}
+ :if ($healthy) do={
+ :put "Container is back up"
+ :log info "awg3vds: container restarted by the update script"
+ }
+ }
+ :if ($img = "" && $minor >= 22 && !$healthy) do={
+ :local imgTag "latest"
+ :local rbModel ""
+ :do { :set rbModel [[:parse ":return [/system/routerboard/get model]"]] } on-error={}
+ :local bName [/system/resource/get board-name]
+ :if ($rbModel~"E50UG" || $rbModel~"E60iUGS" || $bName~"E50UG" || $bName~"E60iUGS" || $bName~"^hEX") do={
+ :set imgTag "latest-armv5"
+ }
+ :set img ("ghcr.io/timbrs/awg-proxy:" . $imgTag)
+ :put ("Container is down and was installed from a file - pointing it at " . $img)
+ :log warning "awg3vds-update: broken file install, switching to the registry"
+ :do { /container/set $cid remote-image=$img } on-error={}
+ }
+ :if ($img = "") do={
+ :put "Installed from a local image file - /container/repull only updates registry installs."
+ :put "Reinstall with the configurator on RouterOS 7.22+ to switch to one-command updates."
+ } else={
+ :if ($minor < 22) do={
+ :put ("/container/repull needs RouterOS 7.22+, this router runs " . $ver)
+ :if (!$healthy) do={ :log error ("awg3vds-update: container is down, and " . $ver . " has no /container/repull") }
+ } else={
+ :local rootDir [/container/get $cid root-dir]
+ :if ([:pick $rootDir 0 1] = "/") do={ :set rootDir [:pick $rootDir 1 [:len $rootDir]] }
+ :local rootDirOrig $rootDir
+ :local slashPos [:find $rootDir "/"]
+ :local disk $rootDir
+ :if ([:typeof $slashPos] != "nil") do={ :set disk [:pick $rootDir 0 $slashPos] }
+ :local verPath "awg-update-check.json"
+ :if ($disk != "disk1") do={
+ :do { /file/make-directory ($disk . "/pull") } on-error={}
+ /container/config set tmpdir=($disk . "/pull")
+ :set verPath ($disk . "/pull/awg-update-check.json")
+ }
+ :local newVer ""
+ :do {
+ /tool/fetch url="https://api.github.com/repos/timbrs/amneziawg-mikrotik-c/releases/latest" dst-path=$verPath http-max-redirect-count=10
+ :local relJson [/file/get [find where name=$verPath] contents]
+ :local tagPos [:find $relJson "\"tag_name\":\""]
+ :if ([:typeof $tagPos] != "nil") do={
+ :local tagRest [:pick $relJson ($tagPos + 12) [:len $relJson]]
+ :set newVer [:pick $tagRest 0 [:find $tagRest "\""]]
+ }
+ :do { /file/remove [find where name=$verPath] } on-error={}
+ } on-error={ :put "Could not read the published version" }
+ :local curVer ""
+ :do { :set curVer [/container/envs/get [find where list="awg3vds-env" key="AWG_IMAGE_VER"] value] } on-error={}
+ :local doPull true
+ :if ($healthy) do={
+ :if ($newVer = "") do={
+ :set doPull false
+ :put "Published version unknown - leaving the healthy container alone"
+ }
+ :if ($newVer != "" && $newVer = $curVer) do={
+ :set doPull false
+ :put ("Already on " . $curVer . " - nothing to pull, tunnel untouched")
+ }
+ }
+ :if ($doPull) do={
+ :if (!$healthy) do={
+ :put "Container is not healthy - pulling the image again"
+ :log warning "awg3vds-update: container unhealthy, forcing a pull"
+ }
+ :put ("Updating " . $img . " ...")
+ :log info "awg3vds: repull started"
+ :do { /file/remove [find where name~($disk . "/awg3vds.backup")] } on-error={}
+ :do { [:parse "/container/set $cid ignore-remote-image-change=yes"] } on-error={}
+ /container/repull $cid
+ :local waited 0
+ :while ([:len [/container/get $cid image-id]] = 0 && $waited < 180) do={
+ :delay 3s
+ :set waited ($waited + 3)
+ }
+ :if ([:len [/container/get $cid image-id]] = 0) do={
+ :put "Pull failed - retrying once in 15s"
+ :delay 15s
+ /container/repull $cid
+ :set waited 0
+ :while ([:len [/container/get $cid image-id]] = 0 && $waited < 180) do={
+ :delay 3s
+ :set waited ($waited + 3)
+ }
+ }
+ :local backUp false
+ :if ([:len [/container/get $cid image-id]] > 0) do={
+ :set backUp true
+ :if ($wantRunning) do={
+ :delay 3s
+ :do { /container/start $cid } on-error={}
+ :delay 8s
+ :set backUp false
+ :do { :if ([/container/get $cid stopped] != true) do={ :set backUp true } } on-error={}
+ }
+ }
+ :if (!$backUp) do={
+ :put "Container did not come back up - rebuilding it from the registry"
+ :log warning "awg3vds-update: container did not come back, rebuilding"
+ :do { /container/stop $cid } on-error={}
+ :delay 5s
+ :do { /container/remove $cid } on-error={}
+ :delay 3s
+ :do { /file/remove [find where name~($disk . "/awg3vds.backup")] } on-error={}
+ :do { /container/add remote-image=$img interface=veth-awg3vds envlist=awg3vds-env hostname=awg3vds root-dir=$rootDirOrig logging=no start-on-boot=yes comment=awg3vds } on-error={}
+ :set waited 0
+ :local newCids [/container/find where interface=veth-awg3vds and start-on-boot=yes]
+ :if ([:len $newCids] = 0) do={ :set newCids [/container/find where interface=veth-awg3vds] }
+ :while ([:len $newCids] > 0 && [:len [/container/get [:pick $newCids ([:len $newCids] - 1)] image-id]] = 0 && $waited < 180) do={
+ :delay 3s
+ :set waited ($waited + 3)
+ }
+ :if ([:len $newCids] > 0 && [:len [/container/get [:pick $newCids ([:len $newCids] - 1)] image-id]] > 0) do={
+ :do { /container/set [:pick $newCids ([:len $newCids] - 1)] shm-size=4M } on-error={}
+ :do { [:parse "/container/set [find where interface=veth-awg3vds] ignore-remote-image-change=yes"] } on-error={}
+ :do { [:parse "/container/set [find where interface=veth-awg3vds] restart-policy=on-failure restart-interval=30s restart-max-count=10"] } on-error={}
+ :do { /container/start [:pick $newCids ([:len $newCids] - 1)] } on-error={}
+ :delay 8s
+ :do { :if ([/container/get [:pick $newCids ([:len $newCids] - 1)] stopped] != true) do={ :set backUp true } } on-error={}
+ }
+ :if ($backUp) do={
+ :put "Container rebuilt"
+ :log info "awg3vds: container rebuilt after update"
+ } else={
+ :put "Rebuild failed - the tunnel is down. Netwatch keeps the router online meanwhile."
+ :put "This script runs again on the next schedule and will retry."
+ :log error "awg3vds-update: rebuild failed, tunnel is down"
+ }
+ }
+ :if ($backUp && $newVer != "") do={
+ :do { /container/envs/remove [find where list="awg3vds-env" key="AWG_IMAGE_VER"] } on-error={}
+ :do { /container/envs/add list=awg3vds-env key=AWG_IMAGE_VER value=$newVer } on-error={}
+ }
+ :do { /file/remove [find where name~($disk . "/awg3vds.backup")] } on-error={}
+ :if ($backUp) do={
+ :put ("Update done: " . $img)
+ :log info "awg3vds: update done"
+ }
+ }
+ }
+ }
+ }
+}
+# Daily update check. Skipped if scheduler is not allowed by device-mode
+{
+ :local schedOk true
+ :do { :set schedOk [/system/device-mode/get scheduler] } on-error={}
+ :if ($schedOk) do={
+ /system/scheduler/add name=awg3vds-update on-event="/system/script/run awg3vds-update" start-time=04:30:00 interval=1d comment=awg3vds
+ } else={
+ :put "WARNING: scheduler not allowed by device-mode, no daily update check."
+ :put "Update by hand: /system/script/run awg3vds-update"
+ :put "To allow it: /system/device-mode/update scheduler=yes"
+ }
+}
+
+# 2. Network infrastructure
+/interface/veth/add name=veth-awg3vds address=172.19.14.102/30 gateway=172.19.14.101
+/ip/address/add address=172.19.14.101/30 interface=veth-awg3vds
+/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.14.100/30
+
+# 3. WireGuard interface (MikroTik derives the public key automatically)
+/interface/wireguard/add name=wg-awg3vds private-key="AEU+diR7SCIEOtwOWSWR3pEpkw6v0Qro2LRF0SnPpnw=" listen-port=41430 mtu=1320 disabled=yes
+/interface/wireguard/peers/add interface=wg-awg3vds public-key="MMWjD/TPNpupCvhXLCAw0xHXV4N6jgtQkg9Pl6pcQhU=" preshared-key="r9B13bB7gUTqde6zgBDfffFIUN6puP2gOWdK5zVS3nw=" endpoint-address=172.19.14.102 endpoint-port=51820 allowed-address=10.31.31.0/24,0.0.0.0/0 persistent-keepalive=14
+/ip/address/add address=10.31.31.2/24 interface=wg-awg3vds
+
+# 4. Container environment variables
+/container/envs/add list=awg3vds-env key=AWG_LISTEN value=":51820"
+/container/envs/add list=awg3vds-env key=AWG_REMOTE value="89.110.101.17:31031"
+/container/envs/add list=awg3vds-env key=AWG_JC value="5"
+/container/envs/add list=awg3vds-env key=AWG_JMIN value="510"
+/container/envs/add list=awg3vds-env key=AWG_JMAX value="952"
+/container/envs/add list=awg3vds-env key=AWG_S1 value="138"
+/container/envs/add list=awg3vds-env key=AWG_S2 value="105"
+/container/envs/add list=awg3vds-env key=AWG_H1 value="457615290-457646150"
+/container/envs/add list=awg3vds-env key=AWG_H2 value="1307355386-1307363580"
+/container/envs/add list=awg3vds-env key=AWG_H3 value="2441734303-2441781787"
+/container/envs/add list=awg3vds-env key=AWG_H4 value="3719180788-3719195844"
+/container/envs/add list=awg3vds-env key=AWG_SERVER_PUB value="MMWjD/TPNpupCvhXLCAw0xHXV4N6jgtQkg9Pl6pcQhU="
+/container/envs/add list=awg3vds-env key=AWG_CLIENT_PUB value=[/interface/wireguard/get [find name=wg-awg3vds] public-key]
+/container/envs/add list=awg3vds-env key=AWG_S3 value="51"
+/container/envs/add list=awg3vds-env key=AWG_S4 value="22"
+/container/envs/add list=awg3vds-env key=AWG_I1 value="<b 0x281f01000001000000000000><rc 7><rd 2><r 109>"
+/container/envs/add list=awg3vds-env key=AWG_I2 value="<rc 13><b 0xf0773b61e8e9e015129b><t><r 63><rd 6>"
+/container/envs/add list=awg3vds-env key=AWG_I3 value="<rc 6><b 0x021b4d><t><r 47><rd 8>"
+/container/envs/add list=awg3vds-env key=AWG_I4 value="<rc 15><b 0x0329d6><t><r 52><rd 8>"
+/container/envs/add list=awg3vds-env key=AWG_I5 value="<rc 13><b 0x220483a81a8cd7e37e66><t><r 22><rd 4>"
+/container/envs/add list=awg3vds-env key=AWG_HEADER_PROTECTION_KEY value="FkGxbrW6ds3YPWNl48OWFEccrLC4tF94z9MnoKUZUsM="
+/container/envs/add list=awg3vds-env key=AWG_CONTENT_PADDING value="54-119"
+/container/envs/add list=awg3vds-env key=AWG_REKEY_AFTER value="102-130"
+/container/envs/add list=awg3vds-env key=AWG_REKEY_TIMEOUT value="6-8"
+/container/envs/add list=awg3vds-env key=AWG_REJECT_AFTER value="174-195"
+/container/envs/add list=awg3vds-env key=AWG_KEEPALIVE_TIMEOUT value="14-21"
+/container/envs/add list=awg3vds-env key=AWG_MAX_HANDSHAKE_ATTEMPTS value="12-14"
+/container/envs/add list=awg3vds-env key=AWG_NO_DF value="1"
+# Perf: give each forwarding thread its own core (4+ core routers)
+{ :if ([/system/resource/get cpu-count] >= 4) do={ /container/envs/add list=awg3vds-env key=AWG_CPU_C2S value="1"; /container/envs/add list=awg3vds-env key=AWG_CPU_S2C value="2" } }
+# NOTE on MTU: the value above assumes this tunnel is the last hop.
+# If the server forwards your traffic into another tunnel, subtract
+# that tunnel's overhead too, or every packet leaves in two fragments.
+
+# 5. DNS
+{ :local rawDNS [/ip/dns/get servers]; :local prevDNS ""; :foreach s in=[:toarray $rawDNS] do={ :if ([:len $prevDNS] > 0) do={ :set prevDNS "$prevDNS,$s" } else={ :set prevDNS $s } }; /container/envs/add list=awg3vds-env key=AWG_PREV_DNS value="$prevDNS" }
+# NOTE: replaces existing DNS configuration
+/ip/dns/set servers=89.110.101.17,8.8.4.4
+# DNS 89.110.101.17 идёт общим маршрутом (в активный туннель / default),
+# без привязки к wg-awg3vds: иначе падение туннеля уносит с собой резолв.
+# DNS 8.8.4.4 идёт общим маршрутом (в активный туннель / default),
+# без привязки к wg-awg3vds: иначе падение туннеля уносит с собой резолв.
+
+# 6. Download, create and start container
+:local prevTmpdir [/container/config/get tmpdir]
+/container/envs/add list=awg3vds-env key=AWG_PREV_TMPDIR value=$prevTmpdir
+/container/config set tmpdir=usb1/pull memory-high=200M
+{
+ :local arch [/system/resource/get architecture-name]
+ :local ver [/system/resource/get version]
+ :local dotPos [:find $ver "."]
+ :local rest [:pick $ver ($dotPos + 1) [:len $ver]]
+ :local endPos [:find $rest "."]
+ :if ([:typeof $endPos] = "nil") do={ :set endPos [:find $rest " "] }
+ :local minor [:tonum [:pick $rest 0 $endPos]]
+ :local suffix ""
+ :if ($minor <= 20) do={ :set suffix "-7.20-Docker" }
+ :local file ""
+ :local imgTag "latest"
+ :if ($arch = "arm64") do={ :set file ("awg-proxy-arm64" . $suffix . ".tar.gz") }
+ :if ($arch = "arm") do={
+ :set file ("awg-proxy-arm" . $suffix . ".tar.gz")
+ :local rbModel ""
+ :do { :set rbModel [[:parse ":return [/system/routerboard/get model]"]] } on-error={}
+ :local bName [/system/resource/get board-name]
+ :if ($rbModel~"E50UG" || $rbModel~"E60iUGS" || $bName~"E50UG" || $bName~"E60iUGS" || $bName~"^hEX") do={
+ :set file ("awg-proxy-armv5" . $suffix . ".tar.gz")
+ :set imgTag "latest-armv5"
+ }
+ }
+ :if ($arch ~ "x86") do={ :set file ("awg-proxy-amd64" . $suffix . ".tar.gz") }
+ :if ($file = "") do={ :error "Unsupported architecture: $arch" }
+ :local url "https://github.com/timbrs/amneziawg-mikrotik-c/releases/latest/download/$file"
+ :local filePath ("usb1/pull/" . $file)
+ :local altPath ("usb1/" . $file)
+ :local rootDir "usb1/awg3vds"
+ :do { /file/make-directory usb1/pull } on-error={}
+ :if ([:len [/file/find where name=$filePath]] = 0 && [:len [/file/find where name=$altPath]] > 0) do={
+ :set filePath $altPath
+ }
+ :local added false
+ :local fetched false
+ :if ([:len [/file/find where name=$filePath]] > 0) do={
+ :put ("Using image already on the router: " . $filePath)
+ /container/add file=$filePath interface=veth-awg3vds envlist=awg3vds-env hostname=awg3vds root-dir=$rootDir logging=no start-on-boot=yes comment=awg3vds
+ :set added true
+ }
+ :if (!$added && $minor >= 22) do={
+ :local img ("ghcr.io/timbrs/awg-proxy:" . $imgTag)
+ :put ("Pulling " . $img)
+ /container/add remote-image=$img interface=veth-awg3vds envlist=awg3vds-env hostname=awg3vds root-dir=$rootDir logging=no start-on-boot=yes comment=awg3vds
+ :local cids [/container/find where interface=veth-awg3vds]
+ :local waited 0
+ :while ([:len $cids] > 0 && [:len [/container/get [:pick $cids 0] image-id]] = 0 && $waited < 120) do={
+ :delay 2s
+ :set waited ($waited + 2)
+ }
+ :if ([:len $cids] > 0 && [:len [/container/get [:pick $cids 0] image-id]] > 0) do={
+ :set added true
+ :put ("Pulled from registry in " . $waited . "s")
+ :do {
+ :local verPath ($filePath . ".rel")
+ /tool/fetch url="https://api.github.com/repos/timbrs/amneziawg-mikrotik-c/releases/latest" dst-path=$verPath http-max-redirect-count=10
+ :local relJson [/file/get [find where name=$verPath] contents]
+ :local tagPos [:find $relJson "\"tag_name\":\""]
+ :local tagRest [:pick $relJson ($tagPos + 12) [:len $relJson]]
+ /container/envs/add list=awg3vds-env key=AWG_IMAGE_VER value=[:pick $tagRest 0 [:find $tagRest "\""]]
+ /file/remove [find where name=$verPath]
+ } on-error={}
+ } else={
+ :put "Registry unavailable, falling back to the release archive"
+ :do { /container/remove $cids } on-error={}
+ :delay 2s
+ }
+ }
+ :if (!$added) do={
+ :local freeStorage 0
+ :do { :set freeStorage [/disk/get [find where mount-point="usb1"] free] } on-error={}
+ :if ($freeStorage < 262144) do={
+ :put ("WARNING: Low disk space (" . ($freeStorage / 1024) . "KB free). Need at least 256KB.")
+ :put "See: https://github.com/timbrs/amneziawg-mikrotik-c#insufficient-disk-space"
+ :error "Insufficient disk space"
+ }
+ :put ("Fetching: $url -> " . $filePath)
+ /tool/fetch url=$url dst-path=$filePath http-max-redirect-count=10
+ /container/add file=$filePath interface=veth-awg3vds envlist=awg3vds-env hostname=awg3vds root-dir=$rootDir logging=no start-on-boot=yes comment=awg3vds
+ :set fetched true
+ }
+ :if ($minor > 20) do={ [:parse "/container/set [find where interface=veth-awg3vds] shm-size=4M"] }
+ :do { [:parse "/container/set [find where interface=veth-awg3vds] ignore-remote-image-change=yes"] } on-error={}
+ :do { [:parse "/container/set [find where interface=veth-awg3vds] restart-policy=on-failure restart-interval=30s restart-max-count=10"] } on-error={}
+ :if ($fetched) do={ /file/remove $filePath }
+ :local freeMem [/system/resource/get free-memory]
+ :if ($freeMem < 16777216) do={
+ :put ("WARNING: Low memory (" . ($freeMem / 1048576) . "MB free). 16MB+ recommended.")
+ }
+ /container/start [find where interface=veth-awg3vds]
+ :do { /file/remove [find where name="console-dump.txt"] } on-error={}
+ :put "Waiting for container to start..."
+ :delay 5s
+ /interface/wireguard/enable wg-awg3vds
+ :put "WireGuard interface enabled"
+ :put "Installation complete!"
+ :put "Ping test: 89.110.101.17 via AmneziaWG tunnel. repeat 10 times"
+ /ping 89.110.101.17 count=10 interval=1
+ :put ""
+ :put "======= NEXT STEPS ======="
+ :put ""
+ :put "Route specific traffic through the AmneziaWG tunnel:"
+ :put ""
+ :put " Route a single host:"
+ :put " /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg3vds comment=awg3vds-tunnel"
+ :put ""
+ :put " Route a subnet:"
+ :put " /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg3vds comment=awg3vds-tunnel"
+ :put ""
+ :put " Remove a route:"
+ :put " /ip/route/remove [find where comment=awg3vds-tunnel dst-address=8.8.8.8/32]"
+ :put ""
+ :put " Show all tunnel routes:"
+ :put " /ip/route/print where comment=awg3vds-tunnel"
+ :put ""
+ :put " Update the container:"
+ :put " /system/script/run awg3vds-update"
+ :put ""
+ :put " Uninstall:"
+ :put " /system/script/run awg3vds-uninstall"
+ :put "========================="
+}
+
+# 7. Verification
+# /container/print
+# /interface/wireguard/print
+# /interface/wireguard/peers/print