−25%

за Windows при годишно плащане, до 31.10. Към плановете

EQVPS
Започни

VPS за прегледи на coding агенти: всеки изпратен клон получава собствен адрес

Coding агентът е приключил клон и казва, че всичко работи. Дайте му сървър, където това може да се провери: всеки клон се сглобява в контейнер и се отваря на собствен HTTPS поддомейн зад парола, а SSH ключът на агента може само да пуска и спира прегледи.

Coding агентът казва, че функцията работи. Може би е така. Тестовете, които е написал, минават, а това доказва по-малко, отколкото на всички им се иска. Всъщност искате да отворите резултата в браузъра, преди да се доближи до потребителите, и то без да молите агента да пипа продукцията.

Средите за преглед решават това и за тях не ви трябва платформа. Един VPS със собствен IPv4, Docker, Caddy и два кратки скрипта дават на всеки клон собствен адрес като https://feature-login-form.preview.example.com, зад парола и с пресглобяване при всеки push. Агентът получава SSH ключ, който може да пуска и спира прегледи и нищо друго.

Сглобихме това на тестов сървър и минахме целия цикъл, преди да го опишем: създаване, отваряне, повторен push, премахване, опит за злоупотреба с ключа.

Какво е нужно

  • VPS с публичен IPv4. На AI-Agent-IP се побират няколко прегледа едновременно.
  • Wildcard DNS запис: *.preview.example.com → IP на сървъра.
  • Хранилището на приложението с Dockerfile, който сервира на порт 8080. Езикът е без значение.
  • Ubuntu 24.04 или Debian 12/13 с apt install -y docker.io caddy git.

Клонирайте хранилището веднъж в /srv/app с deploy ключ само за четене.

Двата скрипта

/usr/local/bin/preview-up сглобява клон и го публикува:

#!/usr/bin/env bash
# preview-up <branch> — build the branch and open it at https://<name>.preview.example.com
set -euo pipefail
BRANCH="$1"
NAME=$(printf '%s' "$BRANCH" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/--*/-/g; s/^-//; s/-$//' | cut -c1-40)
DIR=/srv/previews/$NAME
git -C /srv/app fetch --quiet origin "$BRANCH"
rm -rf "$DIR"; git -C /srv/app worktree prune
git -C /srv/app worktree add --quiet --force --detach "$DIR" FETCH_HEAD
docker build --quiet -t "preview-$NAME" "$DIR" >/dev/null
docker rm -f "preview-$NAME" >/dev/null 2>&1 || true
docker run -d --name "preview-$NAME" --restart unless-stopped --memory 512m -p 127.0.0.1::8080 "preview-$NAME" >/dev/null
PORT=$(docker port "preview-$NAME" 8080/tcp | head -1 | awk -F: '{print $NF}')
cat > "/etc/caddy/previews/$NAME.caddy" <<CADDY
$NAME.preview.example.com {
	import preview_common
	reverse_proxy 127.0.0.1:$PORT
}
CADDY
systemctl reload caddy
echo "https://$NAME.preview.example.com"

/usr/local/bin/preview-down го премахва:

#!/usr/bin/env bash
# preview-down <branch> — remove the branch preview
set -euo pipefail
NAME=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/--*/-/g; s/^-//; s/-$//' | cut -c1-40)
docker rm -f "preview-$NAME" >/dev/null 2>&1 || true
docker image rm "preview-$NAME" >/dev/null 2>&1 || true
rm -f "/etc/caddy/previews/$NAME.caddy"; rm -rf "/srv/previews/$NAME"; git -C /srv/app worktree prune
systemctl reload caddy

Някои решения тук са нарочни. Контейнерите се публикуват само на 127.0.0.1 със случаен порт, така че до тях не стига нищо освен през Caddy. Това е важно, защото публикуваните от Docker портове заобикалят ufw. Всеки преглед е ограничен до 512 MB памет, за да не повлече един разбеснял се клон останалите. А повторното пускане на същия клон го пресглобява на място: push, up, презареждане.

Caddy: един файл на преглед, една парола за всички

mkdir -p /etc/caddy/previews /srv/previews
chmod +x /usr/local/bin/preview-up /usr/local/bin/preview-down
caddy hash-password --plaintext 'choose-a-review-password'

Сложете хеша в /etc/caddy/Caddyfile:

(preview_common) {
	basicauth {
		reviewer PASTE-THE-HASH-HERE
	}
}
import /etc/caddy/previews/*.caddy

После systemctl reload caddy. Сега всеки преглед е зад един и същ вход, така че полуготов клон с дебъг изход не виси в отворения интернет. В по-новите версии на Caddy директивата се пише и basic_auth; basicauth работи и в двете.

Ключът на агента: up и down, нищо повече

Агентът трябва да пуска прегледи, а не да притежава сървъра. Създайте потребител deploy и скрипт-шлюз, който приема точно две команди:

#!/usr/bin/env bash
# /usr/local/bin/preview-gate — the only thing the agent's key may run
set -euo pipefail
read -r ACTION BRANCH EXTRA <<< "${SSH_ORIGINAL_COMMAND:-}"
[[ -z "${EXTRA:-}" && "${BRANCH:-}" =~ ^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$ && "$BRANCH" != *..* ]] || { echo "usage: up|down <branch>" >&2; exit 2; }
case "$ACTION" in
  up)   exec sudo /usr/local/bin/preview-up "$BRANCH" ;;
  down) exec sudo /usr/local/bin/preview-down "$BRANCH" ;;
  *)    echo "usage: up|down <branch>" >&2; exit 2 ;;
esac
useradd -m -s /bin/bash deploy
echo 'deploy ALL=(root) NOPASSWD: /usr/local/bin/preview-up, /usr/local/bin/preview-down' > /etc/sudoers.d/preview
chmod 440 /etc/sudoers.d/preview && visudo -cf /etc/sudoers.d/preview
chmod 755 /usr/local/bin/preview-gate

В /home/deploy/.ssh/authorized_keys вържете публичния ключ на агента към този скрипт:

restrict,command="/usr/local/bin/preview-gate" ssh-ed25519 AAAA... coding-agent

От машината, на която работи агентът:

ssh deploy@preview.example.com up feature/login-form
# https://feature-login-form.preview.example.com
ssh deploy@preview.example.com down feature/login-form

При нашия тест up сглоби и публикува клона, повторен up след нов commit сервира новата версия, а down премахна и контейнера, и сайта. Опитите с cat /etc/shadow, up x; id, имена на клонове като -x или a/../b и интерактивен вход с този ключ получиха само usage: up|down <branch>. Името на клона трябва да започва с буква или цифра и не може да съдържа ...

Свързване с агента

В Claude Code или Codex стига един ред във файла с инструкции на проекта:

След push на клон изпълни ssh deploy@preview.example.com up <branch> и добави изведения адрес към резюмето си. Изпълни down, когато клонът е слят или изоставен.

Ако вашият CI вече се пуска при всеки push, извиквайте същата команда оттам и дръжте ключа изцяло извън средата на агента. Това е по-строгият вариант и бихме го избрали за всичко отвъд личен проект.

На какво да внимавате

  • Диск. Всяка сглобка оставя слоеве на образи. Седмичен cron с docker image prune -f държи това под контрол; down вече трие собствения образ на прегледа.
  • Клонове, които никой не затваря. Агентите отварят повече клонове от хората. Cron задача, която изброява контейнерите preview-*, по-стари от седмица, и пуска за тях preview-down, ще ви спести двайсет забравени прегледа.
  • Тайни. Прегледите трябва да работят с тестови данни за достъп. Паролата за basic auth спира непознатите, но не прави приемливо да насочите преглед към продукционната база.
  • Време за сглобяване. Образите се сглобяват на сървъра, така че тежка сглобка се бори за CPU с работещите прегледи. Ако сглобяването отнема минути, сглобявайте в CI и теглете готовия образ.

Самият агент не е длъжен да живее на този сървър. Ако живее — вижте VPS за Claude Code, а за да поръчва и поддържа сам този сървър, свържете го с MCP сървъра на EQVPS. Същата машина може да държи и MCP шлюза на екипа ви. За недоверен код изобщо без сървър има пясъчници, а плановете със собствен IPv4 са от $8 на месец.

Въпроси

Защо да не оставим агента да деплойва направо в продукция?

Защото „тестовете минават“ и „работи“ не са едно и също, а агентът трябва да решава това последен. Прегледът дава на човек какво да отвори, преди нещо да стигне до потребителите, срещу една допълнителна команда.

Кой план е подходящ?

Зависи колко прегледа работят едновременно. AI-Agent-IP ($15/мес., 4 vCPU, 4 GB RAM, 40 GB диск) спокойно побира няколко малки уеб приложения с лимит от 512 MB всяко. Най-тежки са Docker сглобките, затова при големи образи следете диска и паметта.

Нужен ли е wildcard сертификат?

Не. Caddy сам взима отделен сертификат за всеки поддомейн на преглед. Wildcard е само DNS записът. Let's Encrypt позволява около 50 нови сертификата на регистриран домейн седмично, предостатъчно, ако агентът не създава стотици клонове.

Може ли агентът да счупи сървъра със своя SSH ключ?

С този ключ — не. Той е ограничен до една команда, която приема само „up <клон>“ или „down <клон>“ с безопасно име на клон. Shell достъп, допълнителни аргументи и пренасочване на портове се отказват.

Работи ли на NAT план?

Не. Прегледите трябва да са достъпни на портове 80 и 443 на собствени поддомейни, а за това трябва публичен IPv4. NAT плановете пренасочват само SSH порта.

Коментари

Още няма коментари. Бъди първият.

Остави коментар

Коментарите се модерират преди да се появят.