कोडिंग एजेंट कहता है कि फ़ीचर चल रहा है। शायद सच हो। उसके लिखे टेस्ट पास हो जाते हैं, और यह उससे कम साबित करता है जितना सब चाहेंगे। आप असल में चाहते हैं कि उपयोगकर्ताओं के पास पहुँचने से पहले इसे ब्राउज़र में खोलकर देखें, और यह भी बिना एजेंट से प्रोडक्शन छूने को कहे।
प्रीव्यू एनवायरनमेंट यही हल करते हैं, और इसके लिए किसी प्लैटफ़ॉर्म की ज़रूरत नहीं। अपने IPv4 वाला एक VPS, Docker, Caddy और दो छोटी स्क्रिप्ट हर ब्रांच को https://feature-login-form.preview.example.com जैसा अपना URL देती हैं, पासवर्ड के पीछे, हर पुश पर दोबारा बनता हुआ। एजेंट को ऐसी SSH कुंजी मिलती है जो प्रीव्यू शुरू और बंद कर सकती है, और कुछ नहीं।
हमने इसे एक टेस्ट सर्वर पर बनाया और लिखने से पहले पूरा चक्र चलाकर देखा: बनाना, खोलना, फिर से पुश करना, हटाना, कुंजी का दुरुपयोग करने की कोशिश।
क्या चाहिए
- सार्वजनिक IPv4 वाला VPS। AI-Agent-IP पर एक साथ कई प्रीव्यू आ जाते हैं।
- एक वाइल्डकार्ड DNS रिकॉर्ड:
*.preview.example.com→ सर्वर का IP। - आपके ऐप की रिपॉज़िटरी, जिसमें पोर्ट 8080 पर सर्व करने वाला
Dockerfileहो। कोई भी भाषा चलेगी। apt install -y docker.io caddy gitके साथ Ubuntu 24.04 या Debian 12/13।
रीड-ओनली डिप्लॉय कुंजी से रिपॉज़िटरी को एक बार /srv/app में क्लोन कर लें।
दो स्क्रिप्ट
/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 मेमोरी की सीमा है, ताकि बेकाबू हुई एक ब्रांच बाकी को न गिराए। और उसी ब्रांच को फिर चलाने पर वह वहीं दोबारा बन जाती है: पुश, 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 ने नया संस्करण दिखाया, और down ने कंटेनर और साइट हटा दी। उस कुंजी से cat /etc/shadow, up x; id, -x या a/../b जैसे ब्रांच नाम, या इंटरैक्टिव लॉगिन की कोशिश, सबको सिर्फ़ usage: up|down <branch> मिला। ब्रांच का नाम अक्षर या अंक से शुरू होना चाहिए और उसमें .. नहीं हो सकता।
एजेंट से जोड़ना
Claude Code या Codex में प्रोजेक्ट की निर्देश फ़ाइल में एक लाइन काफ़ी है:
ब्रांच पुश करने के बाद
ssh deploy@preview.example.com up <branch>चलाओ और छपा हुआ URL अपने सारांश में डालो। ब्रांच मर्ज या छोड़ दी जाए तोdownचलाओ।
अगर आपका CI पहले से हर पुश पर चलता है, तो वही कमांड वहाँ से बुलाएँ और कुंजी को एजेंट के एनवायरनमेंट से पूरी तरह दूर रखें। यह ज़्यादा सख़्त तरीका है, और निजी प्रोजेक्ट से आगे की हर चीज़ के लिए हम यही चुनेंगे।
किन बातों पर नज़र रखें
- डिस्क। हर बिल्ड इमेज लेयर छोड़ता है। हफ़्ते में एक बार cron से
docker image prune -fइसे काबू में रखता है;downप्रीव्यू की अपनी इमेज पहले ही हटा देता है। - ऐसी ब्रांच जिन्हें कोई बंद नहीं करता। एजेंट इंसानों से ज़्यादा ब्रांच खोलते हैं। एक हफ़्ते से पुराने
preview-*कंटेनरों की सूची बनाकर उन परpreview-downचलाने वाला cron आपको बीस भूले हुए प्रीव्यू मिलने से बचाता है। - सीक्रेट। प्रीव्यू टेस्ट क्रेडेंशियल से चलने चाहिए। basic auth पासवर्ड अजनबियों को बाहर रखता है, पर इससे प्रीव्यू को प्रोडक्शन डेटाबेस से जोड़ना ठीक नहीं हो जाता।
- बिल्ड का समय। इमेज सर्वर पर बनती हैं, इसलिए भारी बिल्ड चल रहे प्रीव्यू से CPU के लिए होड़ करता है। बिल्ड में कई मिनट लगते हों तो CI में बनाएँ और इमेज खींच लें।
एजेंट को खुद इस सर्वर पर रहना ज़रूरी नहीं। अगर रहता है, तो Claude Code के लिए VPS देखें, और उसे यह सर्वर खुद ऑर्डर करने और संभालने देने के लिए उसे EQVPS MCP सर्वर से जोड़ें। वही मशीन आपकी टीम का MCP गेटवे भी होस्ट कर सकती है। बिना किसी सर्वर के अविश्वसनीय कोड चलाने के लिए सैंडबॉक्स हैं, और अपने IPv4 वाले प्लान $8 प्रति माह से शुरू होते हैं।
टिप्पणियाँ
अभी तक कोई टिप्पणी नहीं। पहले बनें।