feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
#!/usr/bin/env bash
|
2026-10-07 15:49:49 +02:00
|
|
|
# Sets up furtka-gateway's control-plane, plugged into a Traefik and
|
|
|
|
|
# wg-easy YOU already run. Read the README's "Existing Traefik + wg-easy"
|
|
|
|
|
# section first — in particular the routing requirement, which this script
|
|
|
|
|
# can't set up or verify for you. On an empty server, use
|
|
|
|
|
# examples/starter-stack/ instead.
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
#
|
|
|
|
|
# Safe to re-run — existing .env values become the defaults for each
|
2026-10-07 15:49:49 +02:00
|
|
|
# prompt (press Enter to keep them).
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
#
|
|
|
|
|
# Usage:
|
2026-10-07 15:49:49 +02:00
|
|
|
# ops/deploy.sh prompts, writes .env, starts the
|
|
|
|
|
# control-plane, installs its Traefik
|
|
|
|
|
# route, health-checks it
|
|
|
|
|
# ops/deploy.sh --non-interactive same, but no prompts: every value
|
|
|
|
|
# comes from .env (fails if one is missing)
|
|
|
|
|
# ops/deploy.sh --render-only only (re)write .env and
|
|
|
|
|
# rendered/control-plane.yml — touches
|
|
|
|
|
# nothing live (not even your Traefik's
|
|
|
|
|
# dynamic directory)
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
|
cd "$REPO_ROOT"
|
|
|
|
|
|
|
|
|
|
RENDER_ONLY=0
|
2026-10-07 15:49:49 +02:00
|
|
|
INTERACTIVE=1
|
|
|
|
|
for arg in "$@"; do
|
|
|
|
|
case "$arg" in
|
|
|
|
|
--render-only) RENDER_ONLY=1 ;;
|
|
|
|
|
--non-interactive) INTERACTIVE=0 ;;
|
|
|
|
|
*) echo "unknown option: $arg" >&2; exit 2 ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
|
|
|
|
|
ENV_FILE=.env
|
|
|
|
|
|
|
|
|
|
# ---- load existing .env (if any) so prompts default to current values ----
|
|
|
|
|
# Deliberately NOT `source`d: bash expands `$VAR`/`$$` during sourcing,
|
|
|
|
|
# which is wrong for values meant to be taken literally. Plain string
|
|
|
|
|
# splitting instead.
|
|
|
|
|
load_env_file() {
|
|
|
|
|
local file="$1" line key value
|
|
|
|
|
[[ -f "$file" ]] || return 0
|
|
|
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
|
|
|
[[ -z "$line" || "$line" == \#* ]] && continue
|
|
|
|
|
key="${line%%=*}"
|
|
|
|
|
value="${line#*=}"
|
|
|
|
|
printf -v "$key" '%s' "$value"
|
|
|
|
|
done < "$file"
|
|
|
|
|
}
|
|
|
|
|
load_env_file "$ENV_FILE"
|
|
|
|
|
|
|
|
|
|
ask() {
|
|
|
|
|
# ask VARNAME "Prompt text" "fallback-default"
|
|
|
|
|
local __var="$1" __prompt="$2" __fallback="${3:-}"
|
|
|
|
|
local __current="${!__var:-}"
|
|
|
|
|
local __default="${__current:-$__fallback}"
|
2026-10-07 15:49:49 +02:00
|
|
|
local __ans=""
|
|
|
|
|
if [[ "$INTERACTIVE" -eq 1 ]]; then
|
|
|
|
|
if [[ -n "$__default" ]]; then
|
|
|
|
|
read -r -p "$__prompt [$__default]: " __ans
|
|
|
|
|
else
|
|
|
|
|
read -r -p "$__prompt: " __ans
|
|
|
|
|
fi
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
fi
|
|
|
|
|
printf -v "$__var" '%s' "${__ans:-$__default}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ask_secret() {
|
|
|
|
|
local __var="$1" __prompt="$2"
|
|
|
|
|
local __current="${!__var:-}"
|
2026-10-07 15:49:49 +02:00
|
|
|
local __ans=""
|
|
|
|
|
if [[ "$INTERACTIVE" -eq 1 ]]; then
|
|
|
|
|
if [[ -n "$__current" ]]; then
|
|
|
|
|
read -r -s -p "$__prompt [unchanged, Enter to keep]: " __ans
|
|
|
|
|
else
|
|
|
|
|
read -r -s -p "$__prompt: " __ans
|
|
|
|
|
fi
|
|
|
|
|
echo
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
fi
|
|
|
|
|
printf -v "$__var" '%s' "${__ans:-$__current}"
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-07 15:49:49 +02:00
|
|
|
require() {
|
|
|
|
|
# require VARNAME — fail if empty
|
|
|
|
|
if [[ -z "${!1:-}" ]]; then
|
|
|
|
|
echo "$1 is required" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
require_cmd() {
|
|
|
|
|
command -v "$1" >/dev/null 2>&1 || { echo "missing required command: $1" >&2; exit 1; }
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-07 15:49:49 +02:00
|
|
|
require_cmd openssl
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
if [[ "$RENDER_ONLY" -eq 0 ]]; then
|
|
|
|
|
require_cmd docker
|
|
|
|
|
require_cmd curl
|
|
|
|
|
fi
|
|
|
|
|
|
2026-10-07 15:49:49 +02:00
|
|
|
echo "== furtka-gateway: connect to your existing Traefik + wg-easy =="
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
echo
|
|
|
|
|
|
|
|
|
|
# ---- tenancy mode ----
|
2026-10-07 15:49:49 +02:00
|
|
|
ask GATEWAY_MODE "Mode (single / shared)" "single"
|
|
|
|
|
case "$GATEWAY_MODE" in
|
|
|
|
|
single|shared) ;;
|
|
|
|
|
*) echo "GATEWAY_MODE must be single or shared" >&2; exit 1 ;;
|
|
|
|
|
esac
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
|
|
|
|
|
# ---- wg-easy ----
|
|
|
|
|
echo
|
2026-10-07 15:49:49 +02:00
|
|
|
ask WG_EASY_URL "Your wg-easy's admin API URL, as the control-plane will reach it (e.g. http://wg-easy:51821)" ""
|
|
|
|
|
require WG_EASY_URL
|
|
|
|
|
ask WG_EASY_API_VERSION "Your wg-easy image's major version (14 or 15 — check the tag)" "15"
|
|
|
|
|
case "$WG_EASY_API_VERSION" in
|
|
|
|
|
14|15) ;;
|
|
|
|
|
*) echo "WG_EASY_API_VERSION must be 14 or 15 (different, incompatible APIs)" >&2; exit 1 ;;
|
|
|
|
|
esac
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
if [[ "$WG_EASY_API_VERSION" == "15" ]]; then
|
|
|
|
|
ask WG_EASY_ADMIN_USERNAME "wg-easy admin username" "admin"
|
2026-10-07 15:49:49 +02:00
|
|
|
ask GATEWAY_WG_ALLOWED_IPS "wg-easy's WireGuard peer subnet (new clients get split-tunnel to this)" "10.8.0.0/24"
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
fi
|
|
|
|
|
ask_secret WG_EASY_ADMIN_PASSWORD "wg-easy admin password"
|
2026-10-07 15:49:49 +02:00
|
|
|
require WG_EASY_ADMIN_PASSWORD
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
|
|
|
|
|
# ---- Traefik ----
|
|
|
|
|
echo
|
2026-10-07 15:49:49 +02:00
|
|
|
ask GATEWAY_DYNAMIC_DIR_HOST "Host directory your Traefik's file provider watches" ""
|
|
|
|
|
require GATEWAY_DYNAMIC_DIR_HOST
|
|
|
|
|
ask GATEWAY_CERT_RESOLVER "Your Traefik's certResolver name (blank if a default cert applies automatically)" ""
|
|
|
|
|
ask GATEWAY_CONTROL_PLANE_HOST "Gateway API hostname (what you enter on the box)" ""
|
|
|
|
|
require GATEWAY_CONTROL_PLANE_HOST
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
|
|
|
|
|
echo
|
2026-10-07 15:49:49 +02:00
|
|
|
echo "How does your Traefik reach the control-plane container?"
|
|
|
|
|
echo " 1) it's on the same docker network as my Traefik (recommended)"
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
echo " 2) separate host — I'll expose it at a reachable host:port myself"
|
2026-10-07 15:49:49 +02:00
|
|
|
ask GATEWAY_REACHABILITY_MODE "Choice (1 or 2)" "1"
|
|
|
|
|
case "$GATEWAY_REACHABILITY_MODE" in
|
|
|
|
|
1)
|
|
|
|
|
ask GATEWAY_TRAEFIK_NETWORK "Your Traefik's docker network name (see: docker inspect <traefik> --format '{{json .NetworkSettings.Networks}}')" ""
|
|
|
|
|
require GATEWAY_TRAEFIK_NETWORK
|
|
|
|
|
GATEWAY_CONTROL_PLANE_BIND="127.0.0.1:8090"
|
|
|
|
|
GATEWAY_CONTROL_PLANE_TARGET="furtka-gateway-control-plane:8090"
|
|
|
|
|
;;
|
|
|
|
|
2)
|
|
|
|
|
echo
|
|
|
|
|
echo "WARNING: this binds the control-plane API beyond loopback. Its"
|
|
|
|
|
echo "registration response carries a WireGuard private key in plaintext"
|
|
|
|
|
echo "JSON — put real access control (firewall/security group) around"
|
|
|
|
|
echo "whatever bind address you give it."
|
|
|
|
|
ask GATEWAY_CONTROL_PLANE_BIND "Bind address:port to expose it on (e.g. 10.0.0.5:8090)" ""
|
|
|
|
|
require GATEWAY_CONTROL_PLANE_BIND
|
|
|
|
|
ask GATEWAY_CONTROL_PLANE_TARGET "The same host:port, as your Traefik will reach it" "$GATEWAY_CONTROL_PLANE_BIND"
|
|
|
|
|
GATEWAY_TRAEFIK_NETWORK=""
|
|
|
|
|
;;
|
|
|
|
|
*) echo "GATEWAY_REACHABILITY_MODE must be 1 or 2" >&2; exit 1 ;;
|
|
|
|
|
esac
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
|
|
|
|
|
# ---- tokens (generated once, kept on re-run) ----
|
|
|
|
|
GATEWAY_BOX_TOKEN="${GATEWAY_BOX_TOKEN:-$(openssl rand -hex 32)}"
|
|
|
|
|
GATEWAY_ADMIN_TOKEN="${GATEWAY_ADMIN_TOKEN:-$(openssl rand -hex 32)}"
|
|
|
|
|
|
|
|
|
|
# ---- write .env ----
|
|
|
|
|
echo
|
|
|
|
|
echo "Writing $ENV_FILE"
|
|
|
|
|
{
|
|
|
|
|
echo "# Generated by ops/deploy.sh — re-run it to change any of these."
|
|
|
|
|
echo "GATEWAY_MODE=$GATEWAY_MODE"
|
|
|
|
|
echo "WG_EASY_URL=$WG_EASY_URL"
|
|
|
|
|
echo "WG_EASY_API_VERSION=$WG_EASY_API_VERSION"
|
|
|
|
|
[[ "$WG_EASY_API_VERSION" == "15" ]] && echo "WG_EASY_ADMIN_USERNAME=$WG_EASY_ADMIN_USERNAME"
|
|
|
|
|
[[ "$WG_EASY_API_VERSION" == "15" ]] && echo "GATEWAY_WG_ALLOWED_IPS=$GATEWAY_WG_ALLOWED_IPS"
|
|
|
|
|
echo "WG_EASY_ADMIN_PASSWORD=$WG_EASY_ADMIN_PASSWORD"
|
|
|
|
|
echo "GATEWAY_DYNAMIC_DIR_HOST=$GATEWAY_DYNAMIC_DIR_HOST"
|
|
|
|
|
echo "GATEWAY_CERT_RESOLVER=$GATEWAY_CERT_RESOLVER"
|
|
|
|
|
echo "GATEWAY_CONTROL_PLANE_HOST=$GATEWAY_CONTROL_PLANE_HOST"
|
2026-10-07 15:49:49 +02:00
|
|
|
echo "GATEWAY_REACHABILITY_MODE=$GATEWAY_REACHABILITY_MODE"
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
echo "GATEWAY_CONTROL_PLANE_BIND=$GATEWAY_CONTROL_PLANE_BIND"
|
|
|
|
|
echo "GATEWAY_CONTROL_PLANE_TARGET=$GATEWAY_CONTROL_PLANE_TARGET"
|
|
|
|
|
[[ -n "$GATEWAY_TRAEFIK_NETWORK" ]] && echo "GATEWAY_TRAEFIK_NETWORK=$GATEWAY_TRAEFIK_NETWORK"
|
2026-10-07 15:49:49 +02:00
|
|
|
[[ -n "${GATEWAY_CONTROL_PLANE_IMAGE:-}" ]] && echo "GATEWAY_CONTROL_PLANE_IMAGE=$GATEWAY_CONTROL_PLANE_IMAGE"
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
echo "GATEWAY_BOX_TOKEN=$GATEWAY_BOX_TOKEN"
|
|
|
|
|
echo "GATEWAY_ADMIN_TOKEN=$GATEWAY_ADMIN_TOKEN"
|
|
|
|
|
} > "$ENV_FILE"
|
|
|
|
|
chmod 600 "$ENV_FILE"
|
|
|
|
|
|
2026-10-07 15:49:49 +02:00
|
|
|
# ---- join your Traefik's network from the compose file itself ----
|
|
|
|
|
# An override (auto-loaded by `docker compose`, gitignored) rather than a
|
|
|
|
|
# `docker network connect` after startup: the control-plane talks to
|
|
|
|
|
# wg-easy as soon as it starts, so the network must be there from the
|
|
|
|
|
# first second, and it must survive container recreation.
|
|
|
|
|
if [[ -n "$GATEWAY_TRAEFIK_NETWORK" ]]; then
|
|
|
|
|
echo "Writing docker-compose.override.yaml (joins network: $GATEWAY_TRAEFIK_NETWORK)"
|
|
|
|
|
cat > docker-compose.override.yaml <<EOF
|
|
|
|
|
# Generated by ops/deploy.sh — joins your Traefik's docker network.
|
|
|
|
|
services:
|
|
|
|
|
control-plane:
|
|
|
|
|
networks:
|
|
|
|
|
- default
|
|
|
|
|
- traefik
|
|
|
|
|
networks:
|
|
|
|
|
traefik:
|
|
|
|
|
name: $GATEWAY_TRAEFIK_NETWORK
|
|
|
|
|
external: true
|
|
|
|
|
EOF
|
|
|
|
|
else
|
|
|
|
|
rm -f docker-compose.override.yaml
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---- render the control-plane's Traefik route (not yet installed) ----
|
|
|
|
|
echo "Writing rendered/control-plane.yml"
|
|
|
|
|
mkdir -p rendered
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
# Only the http: block onward — the .example's header comment explains the
|
2026-10-07 15:49:49 +02:00
|
|
|
# placeholders BY NAME, which these substitutions would otherwise corrupt.
|
|
|
|
|
sed -n '/^http:/,$p' traefik/dynamic/control-plane.yml.example > rendered/control-plane.yml
|
|
|
|
|
sed -i "s|GATEWAY_CONTROL_PLANE_HOST|$GATEWAY_CONTROL_PLANE_HOST|" rendered/control-plane.yml
|
|
|
|
|
sed -i "s|GATEWAY_CONTROL_PLANE_TARGET|$GATEWAY_CONTROL_PLANE_TARGET|" rendered/control-plane.yml
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
if [[ -z "$GATEWAY_CERT_RESOLVER" ]]; then
|
2026-10-07 15:49:49 +02:00
|
|
|
# No resolver on your Traefik — drop the tls: block so its default cert
|
|
|
|
|
# applies, mirroring what a blank GATEWAY_CERT_RESOLVER means for every
|
|
|
|
|
# app route (see traefikconf.py).
|
|
|
|
|
sed -i "/^ tls:$/,/^ certResolver:.*$/d" rendered/control-plane.yml
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
else
|
2026-10-07 15:49:49 +02:00
|
|
|
sed -i "s|certResolver: le|certResolver: $GATEWAY_CERT_RESOLVER|" rendered/control-plane.yml
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ "$RENDER_ONLY" -eq 1 ]]; then
|
|
|
|
|
echo
|
2026-10-07 15:49:49 +02:00
|
|
|
echo "--render-only: nothing live was touched. Inspect .env and rendered/control-plane.yml."
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2026-10-07 15:49:49 +02:00
|
|
|
# ---- bring the control-plane up (always built from this checkout) ----
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
echo
|
2026-10-07 15:49:49 +02:00
|
|
|
echo "== docker compose up -d --build =="
|
|
|
|
|
docker compose up -d --build
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "== health check =="
|
2026-10-07 15:49:49 +02:00
|
|
|
healthy=0
|
|
|
|
|
for _ in $(seq 1 30); do
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
if curl -sf "http://${GATEWAY_CONTROL_PLANE_BIND}/healthz" >/dev/null 2>&1; then
|
2026-10-07 15:49:49 +02:00
|
|
|
healthy=1
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
break
|
|
|
|
|
fi
|
2026-10-07 15:49:49 +02:00
|
|
|
sleep 2
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
done
|
2026-10-07 15:49:49 +02:00
|
|
|
if [[ "$healthy" -ne 1 ]]; then
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
echo "control-plane did not become healthy — check: docker compose logs control-plane" >&2
|
2026-10-07 15:49:49 +02:00
|
|
|
echo "(a wrong WG_EASY_URL or wg-easy password shows up there)" >&2
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
exit 1
|
|
|
|
|
fi
|
2026-10-07 15:49:49 +02:00
|
|
|
echo " ok: control-plane is up"
|
|
|
|
|
|
|
|
|
|
# ---- install the route into your Traefik's dynamic directory ----
|
|
|
|
|
mkdir -p "$GATEWAY_DYNAMIC_DIR_HOST"
|
|
|
|
|
cp rendered/control-plane.yml "$GATEWAY_DYNAMIC_DIR_HOST/control-plane.yml"
|
|
|
|
|
echo " installed $GATEWAY_DYNAMIC_DIR_HOST/control-plane.yml"
|
|
|
|
|
|
|
|
|
|
public_ok=0
|
|
|
|
|
for _ in $(seq 1 20); do
|
|
|
|
|
if curl -sf -m 10 "https://$GATEWAY_CONTROL_PLANE_HOST/healthz" >/dev/null 2>&1; then
|
|
|
|
|
public_ok=1
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
sleep 3
|
|
|
|
|
done
|
|
|
|
|
if [[ "$public_ok" -eq 1 ]]; then
|
|
|
|
|
echo " ok: https://$GATEWAY_CONTROL_PLANE_HOST/healthz"
|
|
|
|
|
else
|
|
|
|
|
echo " WARNING: https://$GATEWAY_CONTROL_PLANE_HOST/healthz not reachable."
|
|
|
|
|
echo " Check that DNS points at your Traefik and that Traefik watches"
|
|
|
|
|
echo " $GATEWAY_DYNAMIC_DIR_HOST (providers.file.directory)."
|
|
|
|
|
fi
|
feat: ship control-plane standalone, bring your own Traefik/wg-easy
Restructure the deployment model: instead of bundling and owning its own
Traefik + wg-easy, the control-plane now plugs into a Traefik and wg-easy
the operator already runs (as sibling containers, or otherwise reachable).
This avoids duplicating infra self-hosters commonly already have, and
sidesteps having to run a second ACME/DNS setup entirely — the operator's
own Traefik already has a working cert resolver.
- New control_plane/wgeasy_v15.py: a wg-easy v15 REST client, verified live
against a real running container (auth model, route shapes, and the new
per-client PresharedKey all differ materially from v14's API). Threaded
the preshared key through to the box side (furtka/wireguard.py in the
furtka repo) so v15's handshake actually completes.
- GATEWAY_CERT_RESOLVER is now a real env var instead of a GATEWAY_MODE-
derived hardcoded "le", so it can mirror whatever resolver name (if any)
the operator's own Traefik already has configured.
- Root docker-compose.yaml/ops/deploy.sh: control-plane only now. deploy.sh
asks how the operator's Traefik reaches it (same docker network, joined
via `docker network connect`, or a separate host) instead of prompting
for DNS-01 providers and port remaps single-parallel mode needed.
- single-parallel mode is retired outright — nothing needs it once
furtka-gateway doesn't own a second Traefik/wg-easy to remap ports for.
- The old bundled stack (own Traefik+wg-easy, HTTP-01) moves to
examples/starter-stack/ as the documented zero-infra on-ramp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3jVwAqESRjPdW9P9nvfem
2026-09-19 13:34:29 +02:00
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "== done =="
|
2026-10-07 15:49:49 +02:00
|
|
|
echo "Before publishing apps, make sure your Traefik can reach wg-easy's peer"
|
|
|
|
|
echo "subnet — see the README's routing section. Quick check after a box connects:"
|
|
|
|
|
echo " docker exec <your-traefik> ping -c1 10.8.0.<box>"
|
|
|
|
|
echo
|
|
|
|
|
echo "Connect a Furtka box: open its web UI -> Settings -> Gateway, and enter"
|
|
|
|
|
echo " Gateway URL: https://$GATEWAY_CONTROL_PLANE_HOST"
|
|
|
|
|
echo " Registration token: the GATEWAY_BOX_TOKEN value in $REPO_ROOT/.env"
|