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 на месец.
Коментари
Още няма коментари. Бъди първият.