From 4cbfd92dad758d44e5a6a07542eef6fdfe11516c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20F=C3=B6rtsch?= Date: Tue, 3 Mar 2026 17:26:47 +0100 Subject: [PATCH] add legacy asteroids script, cache data, claude settings Co-Authored-By: Claude Opus 4.6 --- .claude/settings.local.json | 10 + asteroids/asteroids | 452 ++++++++++++++++++++++++++++++++++++ asteroids/asteroids.list | 9 + asteroids/cache/.gitkeep | 0 asteroids/cache/danger | 9 + asteroids/cache/foertsch | 9 + asteroids/cache/impstr | 9 + asteroids/cache/jef | 9 + asteroids/cache/kristina | 9 + asteroids/cache/serve | 9 + asteroids/cache/suprblox | 9 + asteroids/cache/wtp | 9 + 12 files changed, 543 insertions(+) create mode 100644 .claude/settings.local.json create mode 100755 asteroids/asteroids create mode 100644 asteroids/asteroids.list create mode 100644 asteroids/cache/.gitkeep create mode 100644 asteroids/cache/danger create mode 100644 asteroids/cache/foertsch create mode 100644 asteroids/cache/impstr create mode 100644 asteroids/cache/jef create mode 100644 asteroids/cache/kristina create mode 100644 asteroids/cache/serve create mode 100644 asteroids/cache/suprblox create mode 100644 asteroids/cache/wtp diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..1c505ab --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,10 @@ +{ + "permissions": { + "allow": [ + "WebFetch(domain:manual.uberspace.de)", + "WebFetch(domain:u8manual.uberspace.de)", + "Bash(rustc:*)", + "Bash(git:*)" + ] + } +} diff --git a/asteroids/asteroids b/asteroids/asteroids new file mode 100755 index 0000000..bfb83fe --- /dev/null +++ b/asteroids/asteroids @@ -0,0 +1,452 @@ +#!/usr/bin/env bash +# asteroids — Uberspace account inventory, status, SSH passthrough +# Lives in tools/asteroids/. Symlink to ~/.bin/asteroids via Ansible bin role. +set -euo pipefail + +# --- Resolve repo root (follows symlinks) --- +_src="${BASH_SOURCE[0]}" +while [ -L "$_src" ]; do + _dir="$(cd "$(dirname "$_src")" && pwd -P)" + _src="$(readlink "$_src")" + [[ "$_src" != /* ]] && _src="$_dir/$_src" +done +_TOOL_DIR="$(cd "$(dirname "$_src")" && pwd -P)" +_REPO_DIR="$(cd "$_TOOL_DIR/../.." && pwd -P)" +unset _src _dir + +# --- Config paths --- +UBERSPACES_REGISTRY="$_TOOL_DIR/asteroids.list" +UBERSPACE_CACHE_DIR="$_TOOL_DIR/cache" + +# --- Colors --- +if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then + C_RESET=$'\033[0m' + C_BOLD=$'\033[1m' + C_DIM=$'\033[2m' + C_CYAN=$'\033[36m' + C_BLUE=$'\033[34m' + C_GREEN=$'\033[32m' + C_YELLOW=$'\033[33m' + C_RED=$'\033[31m' +else + C_RESET="" C_BOLD="" C_DIM="" C_CYAN="" C_BLUE="" C_GREEN="" C_YELLOW="" C_RED="" +fi + +# --- Print helpers --- +_info() { printf "%b==>%b %s\n" "$C_CYAN" "$C_RESET" "$*"; } +_ok() { printf "%b[ok]%b %s\n" "$C_GREEN" "$C_RESET" "$*"; } +_warn() { printf "%b[warn]%b %s\n" "$C_YELLOW" "$C_RESET" "$*" >&2; } +_error() { printf "%b[error]%b %s\n" "$C_RED" "$C_RESET" "$*" >&2; } + +# --- Labeled field with tree formatting for multi-value (comma-separated) fields --- +_print_field() { + local label="$1" value="$2" + local -a items=() + + [[ -n "$value" ]] && IFS=',' read -ra items <<< "$value" + + local n="${#items[@]}" + + if [[ "$n" -eq 0 ]]; then + printf " %b%-9s%b (none)\n" "$C_DIM" "$label" "$C_RESET" + elif [[ "$n" -eq 1 ]]; then + printf " %b%-9s%b %s\n" "$C_DIM" "$label" "$C_RESET" "${items[0]}" + else + printf " %b%-9s%b ├── %s\n" "$C_DIM" "$label" "$C_RESET" "${items[0]}" + local i + for (( i=1; i "$tmp_file" + mv "$tmp_file" "$file" +} + +# --- Registry bootstrap --- +_ensure_registry() { + mkdir -p "$(dirname "$UBERSPACES_REGISTRY")" + touch "$UBERSPACES_REGISTRY" + mkdir -p "$UBERSPACE_CACHE_DIR" +} + +# --- Usage --- +usage() { + cat <<-EOF + asteroids — Uberspace account manager + + Usage: asteroids [args] + + Inventory: + add Register an account (version: 7 or 8) + list|ls Show all registered accounts + remove|rm Deregister an account + + Status: + status Aggregate overview from local cache + status --refresh Refresh all accounts via SSH, then show summary + status Refresh + show status for one account (SSH) + + Passthrough: + Run any uberspace subcommand via SSH + + Examples: + asteroids add danger cetus.uberspace.de 7 + asteroids add impstr pandora.uberspace.de 8 + asteroids list + asteroids danger status + asteroids danger mail domain list + asteroids danger web backend list + asteroids danger port add + EOF +} + +# --- Inventory commands --- +cmd_add() { + local name="$1" + local server="$2" + local version="${3:-}" + _ensure_registry + _file_remove_where_first_field_equals "$UBERSPACES_REGISTRY" "$name" + printf "%s %s %s\n" "$name" "$server" "$version" >> "$UBERSPACES_REGISTRY" + local ver_info="" + [[ -n "$version" ]] && ver_info=" (u${version})" + _ok "Registered: $name @ $server${ver_info}" +} + +cmd_list() { + _ensure_registry + if ! awk '!/^[[:space:]]*#/ && NF >= 2 { found=1; exit } END { exit !found }' "$UBERSPACES_REGISTRY"; then + _warn "No accounts registered. Use: asteroids add " + return 0 + fi + printf " %b%-12s %-28s %s%b\n" "$C_BOLD" "NAME" "SERVER" "VER" "$C_RESET" + printf "%b%s%b\n" "$C_DIM" "$(printf '─%.0s' $(seq 1 49))" "$C_RESET" + awk '!/^[[:space:]]*#/ && NF >= 2 { printf " %-12s %-28s %s\n", $1, $2, ($3 ? "u"$3 : "?") }' "$UBERSPACES_REGISTRY" +} + +cmd_remove() { + local name="$1" + _ensure_registry + if ! awk -v n="$name" '$1 == n { found=1; exit } END { exit !found }' "$UBERSPACES_REGISTRY"; then + _error "'$name' not found in registry." + return 1 + fi + _file_remove_where_first_field_equals "$UBERSPACES_REGISTRY" "$name" + rm -f "$UBERSPACE_CACHE_DIR/$name" + _ok "Removed: $name" +} + +# --- Lookup --- +_lookup() { + local name="$1" + local server + server=$(awk -v n="$name" '!/^[[:space:]]*#/ && $1 == n { print $2; exit }' "$UBERSPACES_REGISTRY") + if [[ -z "$server" ]]; then + _error "'$name' not found in registry." + return 1 + fi + printf '%s\n' "$server" +} + +_lookup_version() { + local name="$1" + awk -v n="$name" '!/^[[:space:]]*#/ && $1 == n { print ($3 ? $3 : "?"); exit }' "$UBERSPACES_REGISTRY" +} + +# --- SSH passthrough --- +_ssh_passthrough() { + local name="$1" + shift + local server + server=$(_lookup "$name") || return 1 + ssh -t -o BatchMode=no "$name@$server" "$@" +} + +# --- Per-account status (live SSH refresh) --- +cmd_status_one() { + local name="$1" + local server + server=$(_lookup "$name") || return 1 + _info "Refreshing status for $name @ $server ..." + + local updated version + updated=$(date -u '+%Y-%m-%dT%H:%M:%S') + version=$(_lookup_version "$name") + + local raw="" + if ! raw=$(ssh -o BatchMode=no "$name@$server" bash -s "$version" <<'ENDSSH' +version="$1" +set +e +u +tmpdir=$(mktemp -d) +trap 'rm -rf "$tmpdir"' EXIT + +# bash -s is non-interactive, non-login: .bashrc/.profile are not sourced, +# so PATH may be minimal. Extend it to cover all standard system locations. +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$HOME/bin:$HOME/.local/bin:$PATH" + +# All data fetched in parallel. Each job gets stdin from /dev/null to prevent +# them from racing on the shared heredoc stdin (which causes silent failures +# when the uberspace CLI reads from stdin for config/token checks). +if [[ "$version" == "8" ]]; then + uberspace web backend list "$tmpdir/ports" 2>"$tmpdir/e.ports" & +else + uberspace port list "$tmpdir/ports" 2>"$tmpdir/e.ports" & +fi +uberspace web domain list "$tmpdir/web" 2>"$tmpdir/e.web" & +uberspace mail domain list "$tmpdir/mdom" 2>"$tmpdir/e.mdom" & +if [[ "$version" == "8" ]]; then + uberspace mail address list "$tmpdir/musr" 2>"$tmpdir/e.musr" & +else + uberspace mail user list "$tmpdir/musr" 2>"$tmpdir/e.musr" & +fi +wait + +# Emit any command errors to stderr — they flow to the user's terminal. +for _ef in "$tmpdir"/e.*; do + [ -s "$_ef" ] && cat "$_ef" >&2 +done + +# parse_list: strips v8 Rich-table chrome (box-drawing separator rows + +# column header) while leaving v7 plain-text output untouched. +# Uses the 2-byte UTF-8 prefix of U+25xx box-drawing chars (\xe2\x94) +# instead of a literal char to avoid locale issues on the remote host. +parse_list() { + local f="$1" + [ -s "$f" ] || return 0 + if grep -q $'\xe2\x94' "$f" 2>/dev/null; then + grep -v $'\xe2\x94' "$f" \ + | awk 'NR==1{next} /^[[:space:]]*$/{next} {gsub(/^[[:space:]]+|[[:space:]]+$/,""); print}' + else + grep -v '^[[:space:]]*$' "$f" + fi +} + +ports=$(parse_list "$tmpdir/ports" | tr '\n' ',' | sed 's/,$//') +web=$(parse_list "$tmpdir/web" | tr '\n' ',' | sed 's/,$//') +mdom=$(parse_list "$tmpdir/mdom" | tr '\n' ',' | sed 's/,$//') +musr=$(parse_list "$tmpdir/musr" | tr '\n' ',' | sed 's/,$//') + +printf 'VERSION=%s\n' "${version:-?}" +printf 'PORTS=%s\n' "$ports" +printf 'WEB=%s\n' "$web" +printf 'MDOM=%s\n' "$mdom" +printf 'MUSR=%s\n' "$musr" +ENDSSH +); then + _error "SSH connection to $name @ $server failed." + return 1 + fi + + local ports web mdom musr + ports=$(printf '%s\n' "$raw" | awk '/^PORTS=/{sub(/^PORTS=/,""); print; exit}' | tr -d '\r') + web=$(printf '%s\n' "$raw" | awk '/^WEB=/{sub(/^WEB=/,""); print; exit}' | tr -d '\r') + mdom=$(printf '%s\n' "$raw" | awk '/^MDOM=/{sub(/^MDOM=/,""); print; exit}' | tr -d '\r') + musr=$(printf '%s\n' "$raw" | awk '/^MUSR=/{sub(/^MUSR=/,""); print; exit}' | tr -d '\r') + + # If nothing came back at all, the remote commands likely couldn't run. + if [[ -z "$ports" && -z "$web" && -z "$mdom" && -z "$musr" ]]; then + _warn "No data from $name — check stderr above, or run manually:" + printf " ssh %s@%s 'which uberspace && uberspace --version'\n" "$name" "$server" >&2 + fi + + mkdir -p "$UBERSPACE_CACHE_DIR" + { + printf 'UPDATED=%s\n' "$updated" + printf 'NAME=%s\n' "$name" + printf 'SERVER=%s\n' "$server" + printf 'VERSION=%s\n' "$version" + printf 'PORTS=%s\n' "$ports" + printf 'WEB_DOMAINS=%s\n' "$web" + printf 'MAIL_DOMAINS=%s\n' "$mdom" + printf 'MAIL_USERS=%s\n' "$musr" + } > "$UBERSPACE_CACHE_DIR/$name" + + printf '\n' + _print_account_block "$name" "$server" "$version" \ + "$ports" "$web" "$mdom" "$musr" \ + "$updated" "${C_DIM}${updated}${C_RESET}" +} + +# --- Aggregate status (from cache, or --refresh to repopulate) --- +cmd_status_all() { + local refresh=false + while [[ $# -gt 0 ]]; do + case "$1" in + --refresh) refresh=true ;; + *) _error "Unknown option: $1"; return 1 ;; + esac + shift + done + + _ensure_registry + + if $refresh; then + local names=() + while IFS= read -r line; do + [[ "$line" =~ ^[[:space:]]*# || -z "${line// }" ]] && continue + names+=("${line%% *}") + done < "$UBERSPACES_REGISTRY" + if [[ "${#names[@]}" -eq 0 ]]; then + _warn "No accounts registered. Use: asteroids add " + return 0 + fi + for n in "${names[@]}"; do + cmd_status_one "$n" || true + done + return 0 + fi + + local found=0 + for f in "$UBERSPACE_CACHE_DIR"/*; do [[ -f "$f" ]] && found=1 && break; done + if [[ "$found" -eq 0 ]]; then + _warn "No cached status — last refresh: never" + printf " Run: %basteroids status --refresh%b\n" "$C_BLUE" "$C_RESET" >&2 + return 0 + fi + + local now stale_count=0 first=1 + now=$(date -u '+%s') + + for cache in "$UBERSPACE_CACHE_DIR"/*; do + [[ -f "$cache" ]] || continue + local name="" server="" version="" ports="" web="" mdom="" musr="" updated="" + while IFS= read -r line; do + case "$line" in + NAME=*) name="${line#NAME=}" ;; + SERVER=*) server="${line#SERVER=}" ;; + VERSION=*) version="${line#VERSION=}" ;; + PORTS=*) ports="${line#PORTS=}" ;; + WEB_DOMAINS=*) web="${line#WEB_DOMAINS=}" ;; + MAIL_DOMAINS=*) mdom="${line#MAIL_DOMAINS=}" ;; + MAIL_USERS=*) musr="${line#MAIL_USERS=}" ;; + UPDATED=*) updated="${line#UPDATED=}" ;; + esac + done < "$cache" + + local age_str="unknown" age_secs=0 + if [[ -n "$updated" ]]; then + local epoch="" + epoch=$(date -u -d "$updated" '+%s' 2>/dev/null \ + || date -u -j -f '%Y-%m-%dT%H:%M:%S' "$updated" '+%s' 2>/dev/null \ + || true) + if [[ -n "$epoch" ]]; then + age_secs=$(( now - epoch )) + if (( age_secs < 60 )); then age_str="${age_secs}s ago" + elif (( age_secs < 3600 )); then age_str="$(( age_secs / 60 ))m ago" + elif (( age_secs < 86400 )); then age_str="$(( age_secs / 3600 ))h ago" + else age_str="$(( age_secs / 86400 ))d ago" + fi + (( age_secs >= 86400 )) && (( stale_count++ )) || true + else + age_str="$updated" + fi + fi + + local time_plain="$age_str" time_display="${C_DIM}${age_str}${C_RESET}" + if (( age_secs >= 86400 )); then + time_plain="${age_str} !" + time_display="${C_DIM}${age_str} ${C_RESET}${C_YELLOW}!${C_RESET}" + fi + + [[ "$first" -eq 0 ]] && printf '\n' + first=0 + _print_account_block "$name" "$server" "$version" \ + "$ports" "$web" "$mdom" "$musr" \ + "$time_plain" "$time_display" + done + + if (( stale_count > 0 )); then + printf '\n' + _warn "${stale_count} account(s) have stale cache (>24h) — run: asteroids status --refresh" + fi +} + +# --- Dispatch --- +if [[ $# -eq 0 ]]; then + usage; exit 0 +fi + +case "$1" in + add) + shift + if [[ $# -lt 3 ]]; then + printf "%bUsage:%b asteroids add \n" "$C_BLUE" "$C_RESET" >&2 + printf " Example: asteroids add danger cetus.uberspace.de 7\n" >&2 + exit 1 + fi + cmd_add "$1" "$2" "$3" + ;; + list|ls) + cmd_list + ;; + remove|rm) + shift + if [[ $# -lt 1 ]]; then + printf "%bUsage:%b asteroids remove \n" "$C_BLUE" "$C_RESET" >&2 + exit 1 + fi + cmd_remove "$1" + ;; + status) + shift + cmd_status_all "$@" + ;; + help|-h|--help) + usage + ;; + *) + # Named account: asteroids [status | ] + _name="$1"; shift + _ensure_registry + _lookup "$_name" > /dev/null || exit 1 + if [[ "${1:-}" == "status" ]]; then + cmd_status_one "$_name" + else + _ssh_passthrough "$_name" uberspace "$@" + fi + ;; +esac diff --git a/asteroids/asteroids.list b/asteroids/asteroids.list new file mode 100644 index 0000000..04b007c --- /dev/null +++ b/asteroids/asteroids.list @@ -0,0 +1,9 @@ +# name server +danger cetus.uberspace.de 7 +serve prospero.uberspace.de 8 +impstr pandora.uberspace.de 8 +foertsch cressida.uberspace.de 7 +jef caliban.uberspace.de 7 +kristina tucana.uberspace.de 7 +suprblox vega.uberspace.de 7 +wtp larissa.uberspace.de 8 diff --git a/asteroids/cache/.gitkeep b/asteroids/cache/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/asteroids/cache/danger b/asteroids/cache/danger new file mode 100644 index 0000000..8d17a9d --- /dev/null +++ b/asteroids/cache/danger @@ -0,0 +1,9 @@ +UPDATED=2026-02-25T17:31:06 +NAME=danger +SERVER=cetus.uberspace.de +VERSION=7 +QUOTA= +PORTS= +WEB_DOMAINS=danger.uber.space,rhqq2.de,ws.rhqq2.de +MAIL_DOMAINS=danger.uber.space,rhqq2.de +MAIL_USERS=No mailboxes found. diff --git a/asteroids/cache/foertsch b/asteroids/cache/foertsch new file mode 100644 index 0000000..ec8a01e --- /dev/null +++ b/asteroids/cache/foertsch @@ -0,0 +1,9 @@ +UPDATED=2026-02-25T17:31:26 +NAME=foertsch +SERVER=cressida.uberspace.de +VERSION=7 +QUOTA= +PORTS= +WEB_DOMAINS=ampelkarten.de,arctic-law.com,doedde.de,foertsch.uber.space,juicyshop.de,newvendo.de,shop.juicyshop.de,themenundtexte.de,tomis-motorradshop.de,undrowear.de,www.juicyshop.de,www.undrowear.de,xn--ddde-5qa.de,xn--frtsch-wxa.de +MAIL_DOMAINS=ampelkarten.de,arctic-law.com,foertsch.uber.space,netfelix.jetzt,themenundtexte.de,xn--frtsch-wxa.de +MAIL_USERS=conrad,felix,hermes,hermine,juicy,paul,support,vmunde diff --git a/asteroids/cache/impstr b/asteroids/cache/impstr new file mode 100644 index 0000000..bf87132 --- /dev/null +++ b/asteroids/cache/impstr @@ -0,0 +1,9 @@ +UPDATED=2026-02-25T17:31:24 +NAME=impstr +SERVER=pandora.uberspace.de +VERSION=8 +QUOTA= +PORTS=Domain Path Destination Port Prefix,* / APACHE None keep,* /StiggerDLBot PORT 8001 remove,* /movie APACHE None keep +WEB_DOMAINS=Domain,impstr.uber.space +MAIL_DOMAINS=Domain Alias of DNS state,impstr.uber.space VALID +MAIL_USERS=Name Domain sysmail catchall alias Alias of Forwards,abuse impstr.u… no no yes sysmail@i…,hostmaster impstr.u… no no yes sysmail@i…,postmaster impstr.u… no no yes sysmail@i…,sysmail impstr.u… yes no no uberspac… diff --git a/asteroids/cache/jef b/asteroids/cache/jef new file mode 100644 index 0000000..6854707 --- /dev/null +++ b/asteroids/cache/jef @@ -0,0 +1,9 @@ +UPDATED=2026-02-25T17:31:42 +NAME=jef +SERVER=caliban.uberspace.de +VERSION=7 +QUOTA= +PORTS= +WEB_DOMAINS=bitwarden.jef-sachsen.de,email.jef-sachsen.de,eud-sachsen.de,eurom.at,jef-leipzig.de,jef-sachsen.de,jef.uber.space,mail.jef-sachsen.de,newsletter.jef-sachsen.de +MAIL_DOMAINS=eud-sachsen.de,eurom.at,jef-leipzig.de,jef-sachsen.de,jef.uber.space,mail.jef-sachsen.de +MAIL_USERS=+,abuse,admin,annika.fleischer,anton.hussing,bitwarden,bjoern.donath,catchall,emely.schaefer,fabian.brueder,felix.foertsch,finnja.klinger,hannes.lauter,hostmaster,info,johannes.kropp,klaas.wibker,kontakt,kristina.oertel,laura.greiff,laurenz.frenzel,maria-teresa.roelke,natalie.semin,no-reply,nora.sandner,paula.kirchner,postmaster,susan.wolf,tanja.schmidt,tim.meglitsch,vincent.kaienburg,vorstand diff --git a/asteroids/cache/kristina b/asteroids/cache/kristina new file mode 100644 index 0000000..abe2c42 --- /dev/null +++ b/asteroids/cache/kristina @@ -0,0 +1,9 @@ +UPDATED=2026-02-25T17:32:10 +NAME=kristina +SERVER=tucana.uberspace.de +VERSION=7 +QUOTA= +PORTS= +WEB_DOMAINS=kristina.uber.space,kristinaschoenfeldt.de +MAIL_DOMAINS=kristina.uber.space,kristinaschoenfeldt.de +MAIL_USERS=mail,noreply diff --git a/asteroids/cache/serve b/asteroids/cache/serve new file mode 100644 index 0000000..f858a03 --- /dev/null +++ b/asteroids/cache/serve @@ -0,0 +1,9 @@ +UPDATED=2026-02-25T17:31:22 +NAME=serve +SERVER=prospero.uberspace.de +VERSION=8 +QUOTA= +PORTS=Domain Path Destination Port Prefix,* / APACHE None keep,* /sticker-cloner PORT 8080 remove +WEB_DOMAINS=Domain,serve.uber.space +MAIL_DOMAINS=Domain Alias of DNS state,serve.uber.space VALID +MAIL_USERS=Name Domain sysmail catchall alias Alias of Forwards,abuse serve.ub… no no yes sysmail@s…,hostmaster serve.ub… no no yes sysmail@s…,postmaster serve.ub… no no yes sysmail@s…,sysmail serve.ub… yes no no uberspac… diff --git a/asteroids/cache/suprblox b/asteroids/cache/suprblox new file mode 100644 index 0000000..af5ddc2 --- /dev/null +++ b/asteroids/cache/suprblox @@ -0,0 +1,9 @@ +UPDATED=2026-02-25T17:32:28 +NAME=suprblox +SERVER=vega.uberspace.de +VERSION=7 +QUOTA= +PORTS= +WEB_DOMAINS=superblocks-leipzig.de,suprblox.de,suprblox.uber.space,www.superblocks-leipzig.de +MAIL_DOMAINS=suprblox.de,suprblox.uber.space +MAIL_USERS=No mailboxes found. diff --git a/asteroids/cache/wtp b/asteroids/cache/wtp new file mode 100644 index 0000000..e456198 --- /dev/null +++ b/asteroids/cache/wtp @@ -0,0 +1,9 @@ +UPDATED=2026-02-25T17:32:45 +NAME=wtp +SERVER=larissa.uberspace.de +VERSION=8 +QUOTA= +PORTS=Domain Path Destination Port Prefix,* / APACHE None keep,* /api PORT 3000 remove +WEB_DOMAINS=Domain,wtp.uber.space +MAIL_DOMAINS=Domain Alias of DNS state,wtp.uber.space VALID +MAIL_USERS=Name Domain sysmail catchall alias Alias of Forwards,abuse wtp.uber… no no yes sysmail@w…,hostmaster wtp.uber… no no yes sysmail@w…,postmaster wtp.uber… no no yes sysmail@w…,sysmail wtp.uber… yes no no uberspac…