8 मिनट पढ़ें

SSH cancellation के बाद remote process cleanup कैसे काम करता है?

SSH cancellation के बाद remote process cleanup के लिए process groups, durable status, child-process tests और disconnects के लिए स्पष्ट योजना चाहिए।

SSH cancellation के बाद remote process cleanup कैसे काम करता है?

रद्द किया गया agent run और रुकी हुई remote command एक ही घटना नहीं हैं। स्थानीय process साफ़ तौर पर बंद हो सकता है, जबकि SSH transport अभी जीवित हो। Transport गायब हो सकता है, जबकि remote shell चलता रहे। Shell समाप्त हो सकता है, जबकि उसके children किसी दूसरे process group में चलते रहें। यदि आप इन घटनाओं को एक ही «cancelled» status में मिला देंगे, तो किसी दिन database migration, package install, test worker या deployment helper agent के «रुकने» की सूचना देने के बाद भी चलता मिलेगा।

समाधान केवल कोई चालाक signal trap नहीं है। आपको ऐसा cancellation contract चाहिए जो remote run का नाम तय करे, उसके descendants के चारों ओर समाप्त किए जा सकने वाला boundary बनाए, बाधित run की जाँच के लिए पर्याप्त output बचाए और controller के गायब हो जाने पर remote host की जिम्मेदारी स्पष्ट करे। Agent को side effects वाली commands चलाने देने से पहले इस contract को बनाकर अच्छी तरह जाँचें।

SSH cancellation चार अलग-अलग चरणों से गुजरता है

Cancellation request को चार सीमाएँ पार करनी होती हैं: agent रुकने का निर्णय लेता है, local supervisor अपने SSH client को रोकता या signal करता है, SSH protocol channel event या signal पहुँचाता है और remote host उस घटना पर कार्रवाई करता है। हर चरण स्वतंत्र रूप से विफल हो सकता है।

RFC 4254 इन अवधारणाओं को अलग रखता है। इसमें channel close message और TERM, INT तथा HUP जैसे नामों के लिए अलग signal channel request परिभाषित हैं। Channel close transport event है। इसका अर्थ «हर remote descendant को SIGTERM भेजो» नहीं है। RFC यह भी कहता है कि close से पहले भेजा गया data, संभव हो तो, पहुँचाया जाना चाहिए। «संभव हो तो» तब बहुत व्यापक अर्थ रखता है जब laptop sleep में चला जाए, network route टूट जाए या local process को force-quit किया जाए।

यह अंतर एक आम खराब design को सामने लाता है:

  1. Agent ssh host long-command शुरू करता है।
  2. User cancel दबाता है।
  3. Agent runner अपने local child process को समाप्त कर देता है।
  4. UI job को cancelled दिखाती है।
  5. long-command, या उसका कोई child, host पर चलता रहता है।

पाँचवीं पंक्ति कोई दुर्लभ स्थिति नहीं है। जब server के पास command समाप्त करने का कोई कारण नहीं होता, या connection टूटने से पहले command session से अलग हो जाती है, तो यही सामान्य परिणाम है।

एक अच्छा cancellation contract साफ़ बताता है कि local side क्या करने की कोशिश करेगा और remote side की जिम्मेदारी क्या है:

  • Launcher random run ID के साथ एक पहचान योग्य remote run बनाता है।
  • Remote wrapper workload को अपने process group या session में शुरू करता है।
  • सामान्य cancel पर उस process group को TERM भेजा जाता है और परिणाम दर्ज होता है।
  • तय grace period के बाद ही wrapper KILL तक जाता है।
  • जब controller वापस नहीं आता, तो remote deadline या lease काम समाप्त करती है।
  • Output और अंतिम status SSH stream के बाद भी सुरक्षित रहते हैं।

जब तक आपको दो में से एक परिणाम न मिल जाए, task को cancelled न कहें: confirmed final remote record या स्पष्ट «state unknown» परिणाम। Connection loss के बाद झूठी निश्चितता incident response को धीमा करती है, क्योंकि सभी लोग गलत आधार से शुरुआत करते हैं।

Children साफ़ करने के लिए remote PID पर्याप्त नहीं है

Remote shell PID को समाप्त करना तभी सुरक्षित है जब shell कभी fork न करे, pipeline न शुरू करे, background process न चलाए और ऐसा tool न बुलाए जो helpers बनाए। Commands का यह समूह बहुत छोटा है।

इस सामान्य remote command को देखें:

build-assets | tee build.log &
wait

Shell का एक PID है, लेकिन pipeline में कई processes हैं। Shell समाप्त होने के बाद भी tee log लिख सकता है। Compiler worker processes शुरू कर सकता है। Package manager काम किसी service को सौंप सकता है। यदि आप kill -TERM "$shell_pid" भेजते हैं, तो बड़े group के केवल एक सदस्य को समाप्त करेंगे और बाकी के बारे में बहुत कम जान पाएँगे।

Short-lived remote run के लिए process groups cancellation की सही इकाई देते हैं। Linux में हर process किसी process group में और हर process group किसी session में होता है। Terminal-generated signals foreground process group को जाते हैं, इसलिए terminal behavior वास्तविकता से अधिक जादुई लग सकता है। Linux setpgid(2) documentation यह भी स्पष्ट करती है कि child अपने parent का process group inherit करता है, जब तक कुछ उसे बदल न दे।

Agent-controlled command के लिए workload का नया session बनाएँ। Session leader का PID सामान्यतः उसके PGID और SID के बराबर होता है। तब kill में negative PID process group को संबोधित करता है:

kill -TERM -- -"$pgid"

शुरुआती minus sign process समाप्त करने और process group समाप्त करने के बीच का अंतर है। -- भी महत्वपूर्ण है। यह malformed value को option की तरह parse होने से रोकता है।

यह मानकर न चलें कि workload PID ही उसका process group ID है। Launch के समय इसकी जाँच करें। Shell wrapper, service manager या setpgid बुलाने वाला program tree बदल सकता है। आपके test kit में रखने लायक सबसे छोटी inspection command यह है:

ps -o pid=,ppid=,pgid=,sid=,stat=,etime=,command= -p "$pid"

सामान्य output इस तरह दिखता है:

24182  24177  24182  24182 Ss       00:03 bash ./worker.sh /tmp/agent-runs/6c4...

यहाँ PID, PGID और SID एक जैसे हैं। इससे प्रमाण मिलता है कि kill -TERM -- -24182 अपेक्षित boundary को target करता है। यदि PGID run record से मेल नहीं खाता, तो अनुमान लगाने के बजाय launch विफल करें।

Process group की भी सीमाएँ हैं। कोई child setsid बुला सकता है, container runtime process को कहीं और भेज सकता है और workload service manager से group के बाहर कुछ चलाने को कह सकता है। यह व्यवहार कभी-कभी उचित होता है, लेकिन इसका अर्थ है कि cancellation guarantee उस handoff पर समाप्त हो गई। Detached work को अलग job type मानें, जिसकी अपनी identity, stop operation और audit record हो।

Cancellation wrapper को वास्तविक cleanup path चाहिए

Remote shell wrapper को workload PID का मालिक होना चाहिए, expected termination signals को trap करना चाहिए, workload group को target करना चाहिए, थोड़ी देर प्रतीक्षा करनी चाहिए और final record लिखना चाहिए। pkill command-name, ढीली process list या account के हर process को समाप्त करने जैसी shortcuts न अपनाएँ। ये तब तक काम करती हैं जब तक दो agent runs एक user साझा न करें, hostname बदल न जाए या command name किसी और के काम से न मिल जाए।

यह Linux-oriented fixture जानबूझकर सरल है। यह सुरक्षित run directory बनाता है, एक workload को नए session में शुरू करता है, output files में लिखता है और wrapper को TERM, INT या HUP मिलने पर workload group समाप्त करता है।

#!/usr/bin/env bash
set -Eeuo pipefail

run_id=${1:?run ID required}
shift
run_dir="${HOME}/.agent-runs/${run_id}"
umask 077
mkdir -p "$run_dir"

child_pid=""
child_pgid=""
finished=0

write_status() {
  local state=$1
  local code=${2:-}
  local tmp="$run_dir/status.tmp"
  printf '{"run_id":"%s","state":"%s","exit_code":"%s"}\n' \
    "$run_id" "$state" "$code" >"$tmp"
  mv "$tmp" "$run_dir/status.json"
}

stop_group() {
  if [[ -z ${child_pgid:-} ]]; then
    return
  fi

  kill -TERM -- "-$child_pgid" 2>/dev/null || true
  for _ in 1 2 3 4 5; do
    if ! kill -0 -- "-$child_pgid" 2>/dev/null; then
      return
    fi
    sleep 1
  done
  kill -KILL -- "-$child_pgid" 2>/dev/null || true
}

cancel() {
  local signal=$1
  trap - TERM INT HUP
  write_status "cancelling:$signal"
  stop_group
  wait "$child_pid" 2>/dev/null || true
  write_status "cancelled:$signal"
  finished=1
  exit 143
}

trap 'cancel TERM' TERM
trap 'cancel INT' INT
trap 'cancel HUP' HUP

write_status "starting"
setsid "$@" >"$run_dir/stdout.log" 2>"$run_dir/stderr.log" &
child_pid=$!
child_pgid=$(ps -o pgid= -p "$child_pid" | tr -d ' ')

if [[ "$child_pgid" != "$child_pid" ]]; then
  printf 'unexpected PGID for %s: %s\n' "$child_pid" "$child_pgid" \
    >"$run_dir/stderr.log"
  kill -TERM "$child_pid" 2>/dev/null || true
  write_status "launch_failed"
  exit 70
fi

printf '%s\n' "$child_pid" >"$run_dir/pid"
printf '%s\n' "$child_pgid" >"$run_dir/pgid"
write_status "running"

set +e
wait "$child_pid"
code=$?
set -e

if [[ $finished -eq 0 ]]; then
  write_status "finished" "$code"
fi
exit "$code"

यह wrapper एक खास failure रोकता है: cancellation signal wrapper तक पहुँचता है, लेकिन wrapper केवल खुद को समाप्त करता है और workload को छोड़ देता है। यह जानबूझकर अलग किए गए descendants को रोकने का वादा नहीं करता और उसे ऐसा वादा करना भी नहीं चाहिए।

Linux setsid(2) manual के अनुसार setsid() नया session बनाता है और caller को नए process group का leader बनाता है, शुरुआत में उसके पास controlling terminal नहीं होता। util-linux का setsid command program को उस नए session में चलाता है और ज़रूरत पड़ने पर fork करता है। इसलिए यह command run के लिए व्यावहारिक boundary है, कोई जादुई cleanup switch नहीं।

Wrapper को सीमित रखें। उसका काम launch, record, stop और report करना है। Business logic उसमें न छिपाएँ। Workload की अपनी transaction handling, temporary-file cleanup और idempotency rules होनी चाहिए।

साफ़ transport disconnect cleanup की गारंटी नहीं है

SSH users अक्सर terminal test से बहुत अधिक निष्कर्ष निकालते हैं। वे PTY के साथ command चलाते हैं, terminal बंद करते हैं, SIGHUP के बाद process को समाप्त होते देखते हैं और मान लेते हैं कि disconnect cleanup काम करता है। फिर agent बिना PTY वाला non-interactive SSH channel इस्तेमाल करता है और व्यवहार बदल जाता है।

PTY terminal semantics बनाता है। Terminal hangup से SIGHUP पहुँच सकता है, लेकिन यह controlling terminals और foreground process groups पर लागू परिस्थितियों पर निर्भर करता है। Linux manual SIGHUP को controlling terminal के hangup या controlling process की मृत्यु के रूप में बताता है। इसका अर्थ यह नहीं कि हर SSH disconnection SSH से शुरू किए गए हर process को signal भेजता है।

Agents के लिए non-interactive SSH सामान्यतः बेहतर default है, क्योंकि इससे output साफ़ रहता है और shell startup surprises कम होते हैं। इससे terminal behavior पर आकस्मिक निर्भरता भी हटती है। PTY तभी इस्तेमाल करें जब remote program को इसकी आवश्यकता हो, जैसे कोई पुराना installer जो इसके बिना चलने से इनकार करे। तब यह भी दर्ज करें कि PTY command के behavior का हिस्सा है और इसकी अलग जाँच करें।

तीन disconnect cases को अलग नाम देना उपयोगी है:

Client जानबूझकर cancel भेजता है

Local supervisor के पास अभी live connection है और वह protocol signal भेज सकता है, या recorded PGID को signal करने वाली अलग authenticated control command खोल सकता है। यह सबसे अच्छी स्थिति है। Remote wrapper TERM प्राप्त करता है, cleanup करता है और cancelled:TERM लिखता है।

जब तक आपने अपने इस्तेमाल किए गए client library और invocation की जाँच न कर ली हो, केवल यह मानकर न चलें कि SSH client को local SIGINT मिलने पर वह यही काम करेगा। Terminal client, embedded SSH library और MCP tool local cancellation को अलग तरह से map कर सकते हैं। कोई socket बंद करेगा, कोई local process समाप्त करेगा और कोई SSH signal request भेज सकता है। ये उस interface के अलग implementations हैं जिसे users «cancel» कहते हैं।

Local client crash होता है या network खो जाता है

Remote command चलती रह सकती है। Server temporary routing problem और ऐसे user में अंतर नहीं कर सकता जो चाहता है कि काम चलता रहे, जब तक आपका protocol उसे यह जानकारी न दे। Remote lease इसका ईमानदार समाधान है।

Launch के समय remote run directory में deadline_epoch लिखें। Run अधिकृत रहने तक local supervisor उसे renew करे। Remote watchdog उसकी जाँच करे और expire होने के बाद उसी process-group cleanup path को बुलाए। Lease duration operation के अनुसार चुनें। पाँच मिनट की lease shell command के लिए ठीक हो सकती है, लेकिन लंबे और सामान्य quiet phases वाले build के लिए जोखिमभरी हो सकती है।

Remote host विफल होता है या reboot करता है

आप process और final status दोनों खो सकते हैं। केवल SSH connection समाप्त होने के कारण «cancelled» या «completed» report न करें। बाद में reconciliation host के journal, deployment state, lock record या application-specific result को पढ़े, तब तक run को unknown रखें।

कठिनाई status word emit करने में नहीं है। कठिनाई ऐसे status word को emit करने से इनकार करना है जिसे आपका system support नहीं कर सकता।

Partial output observation साबित करता है, completion नहीं

कमांड और क्रेडेंशियल अलग रखें
एजेंट को SSH private key नहीं, बल्कि कमांड के परिणाम मिलते हैं।

Stream केवल यह बताता है कि «client को अब तक कौन से bytes मिले?» यह नहीं बताता कि «remote command ने पीछे कौन-सी state छोड़ी?» यह गलती तब दिखती है जब remote program आखिरी file flush करने से पहले done लिख देता है, या command पूरी होने के बाद लेकिन SSH exit status client तक पहुँचने से पहले network टूट जाता है।

दो records रखें:

  • stdout.log और stderr.log workload के लिखते समय diagnostic output रखते हैं।
  • status.json छोटा final record है, जिसे wrapper exit देखने या cancellation संभालने के बाद atomic रूप से लिखता है।

Wrapper में mv महत्वपूर्ण है। उसी directory में temporary status file लिखें और फिर उसे सही नाम पर rename करें। Readers को या तो पिछली पूरी file दिखेगी या नई पूरी file। उन्हें आधे JSON document को parse करके मनगढ़ंत result नहीं बनाना चाहिए।

Output के लिए भी rules चाहिए। stdout terminal के बजाय file होने पर command output को बहुत buffer कर सकती है। यदि progress महत्वपूर्ण है, तो workload से stderr पर स्पष्ट line-oriented status लिखवाएँ या application-level progress file इस्तेमाल करें। हर command के लिए PTY देकर buffering की समस्या न सुलझाएँ। इससे behavior बदलता है और stdout तथा stderr मिल सकते हैं, जिससे audit और failure analysis खराब होते हैं।

Agent UI या journal में इन स्थितियों को अलग रखें:

Remote recordStream stateअर्थ
finished, exit code मौजूदcompleteWrapper ने सामान्य completion देखी।
cancelled:TERMअचानक समाप्त हो सकता हैWrapper ने cancellation शुरू की और अपना group रोक दिया।
केवल cancelling:TERMdisconnectedCleanup शुरू हुआ, लेकिन final record नहीं देखा गया। Reconcile करें।
केवल running, lease validdisconnectedWork अभी चल रहा हो सकता है। बिना जाँच दोबारा शुरू न करें।
कोई usable record नहींdisconnectedState unknown है। Relaunch से पहले side effects जाँचें।

इन logs में access tokens, unredacted configuration dumps या credentials न रखें। SSH credential injection private key को agent से दूर रख सकता है, लेकिन remote command अपने environment या configuration से पढ़े गए secrets फिर भी print कर सकती है। Output retention command design का हिस्सा है, बाद में जोड़ी जाने वाली चीज़ नहीं।

एक सोई हुई shell के बजाय process trees जाँचें

trap 'exit' TERM; sleep 600 कमजोर cancellation test है। यह साबित करता है कि एक foreground shell एक signal पा सकती है। यह children, process groups, देर से होने वाले cleanup, output persistence या गलत समय पर गायब होने वाली remote shell को test नहीं करता।

ऐसा workload इस्तेमाल करें जो दिखाई देने वाला process tree बनाए और हर signal दर्ज करे। इसे disposable Linux host पर worker.sh के रूप में save करें:

#!/usr/bin/env bash
set -Eeuo pipefail
run_dir=${1:?run directory required}

note() {
  printf '%s pid=%s pgid=%s %s\n' \
    "$(date +%s)" "$$" "$(ps -o pgid= -p $$ | tr -d ' ')" "$1" \
    >>"$run_dir/worker.log"
}

trap 'note TERM; exit 143' TERM
trap 'note INT; exit 130' INT
trap 'note HUP; exit 129' HUP

(
  trap 'note grandchild_TERM; exit 143' TERM
  trap 'note grandchild_HUP; exit 129' HUP
  while :; do
    note grandchild_tick
    sleep 1
  done
) &
grandchild=$!

note "started grandchild=$grandchild"
while :; do
  note parent_tick
  sleep 1
done

इसे random run ID के साथ wrapper से चलाएँ। दूसरी SSH session में process tree और run directory देखें:

run_id=cancel-test-$(date +%s)
ssh host.example './remote-wrapper.sh '"$run_id"' ./worker.sh "$HOME/.agent-runs/'"$run_id"'"'

वास्तविक launcher में exact quoting अलग होगी। यह ठीक है। जो नहीं बदलना चाहिए वह test evidence है: remote run ID, wrapper PID, workload PGID, log locations और final status ज़रूर चाहिए।

फिर इन failure paths को एक-एक करके test करें:

  1. Wrapper PID को TERM भेजें। पुष्टि करें कि parent और grandchild दोनों ने termination दर्ज की और ps को recorded PGID में कोई process नहीं मिला।
  2. Workload PID को सीधे TERM भेजें। पुष्टि करें कि उसके child behavior को सुरक्षित मानकर नहीं चला जा सकता। इससे समझ आता है कि wrapper group को target क्यों करता है।
  3. Remote signal भेजे बिना local SSH client को समाप्त करें। पुष्टि करें कि remote lease expire होने तक workload सक्रिय रहता है। यदि वह तुरंत रुकता है, तो इसका कारण, जैसे PTY hangup behavior, दर्ज करें और इसे सार्वभौमिक परिणाम न मानें।
  4. Wrapper के cancelling:TERM लिखने के बाद लेकिन final record लिखने से पहले disconnect करें। पुष्टि करें कि reconciliation अधूरी observation और नए running job में अंतर कर सकती है।
  5. एक ही account के अंतर्गत दो runs शुरू करें, एक को cancel करें और साबित करें कि दूसरा चलता रहे। इससे खतरनाक broad pkill और account-wide cleanup logic पकड़ी जाती है।

Testing के दौरान ps और pgrep -a -g "$pgid" इस्तेमाल करें और grace period के बाद फिर जाँचें। Process table देखने के बाद ही remote status file और logs जाँचें। यदि status record «cancelled» कहता है, लेकिन workers अभी जीवित हैं, तो यह supervisor का bug है, मामूली reporting mismatch नहीं।

Parent-death signals नियंत्रित worker के भीतर ही मदद करते हैं

SSH को एक gateway से चलाएँ
Bundled MCP shim का उपयोग करके एजेंट की SSH कार्रवाइयों को signed Sallyport macOS app के माध्यम से भेजें।

Linux में PR_SET_PDEATHSIG उपलब्ध है। इससे कोई process kernel से कह सकता है कि उसे बनाने वाला thread समाप्त होने पर उसे signal भेजा जाए। यह तब उपयोगी हो सकता है जब आपके नियंत्रण में छोटा native helper हो, जो तुरंत child बनाता है और helper समाप्त होने पर child को भी रोकना चाहता है। सामान्य स्थितियों में यह setting execve के बाद बनी रहती है, लेकिन manual credential changes सहित महत्वपूर्ण exceptions दर्ज करता है।

यह अपने-आप remote agent cleanup हल नहीं करता।

पहला, SSH server process वह parent नहीं भी हो सकता जिसकी आपको चिंता है। दूसरा, signal उस process पर लागू होता है जिसने setting लगाई है, अपने-आप सभी descendants पर नहीं। तीसरा, जो remote command fork, double-fork करती है या काम किसी अन्य service को सौंपती है, वह यह संबंध पीछे छोड़ देती है। चौथा, यह Linux-specific है, जो mixed Unix hosts वाली fleet में महत्वपूर्ण है।

इसे केवल वहाँ अतिरिक्त सुरक्षा के रूप में इस्तेमाल करें जहाँ process tree आपका अपना हो। उदाहरण के लिए, छोटा Linux worker एक controlled child को exec करने से पहले PR_SET_PDEATHSIG लगा सकता है, जबकि outer wrapper process group और lease का मालिक बना रहे। इससे अलग scope वाले दो failure detectors मिलते हैं। इससे group boundary या remote final record को छोड़ने की अनुमति नहीं मिलती।

यही चेतावनी workload के अंदर nohup, disown और setsid पर लागू होती है। जब कोई व्यक्ति जानबूझकर terminal से आगे काम चलाना चाहता है, तब ये उपयोगी हैं। लेकिन agent run cancel करने पर काम रोकने के वादे के साथ ये असंगत हैं। Launch के समय यह चुनाव स्पष्ट करें।

दूसरा control channel अक्सर पहले को समाप्त करने से बेहतर है

कार्रवाई का पूरा रिकॉर्ड जाँचें
इसके एन्क्रिप्टेड, hash-chained audit log को key के बिना sp audit verify से ऑफलाइन जाँचा जा सकता है।

जब agent चल रही SSH call cancel करता है, तो उसका अपना local execution context पहले ही समाप्त हो रहा हो सकता है। उस खत्म होते process पर अंतिम protocol signal भेजने की जिम्मेदारी डालना races को आमंत्रित करता है। अलग supervisor process को cancellation और reconciliation का मालिक होना चाहिए।

एक काम करने योग्य design इस तरह है:

  1. Supervisor cryptographically random run ID बनाता है और remote wrapper invoke करता है।
  2. Wrapper run ID वाली directory में अपना PID, PGID, start time और status दर्ज करता है।
  3. Supervisor output consume करना शुरू करने से पहले run ID और remote host दर्ज करता है।
  4. Cancel पर supervisor fresh control action खोलता है, run record पढ़ता है, expected ownership और age verify करता है और recorded PGID को signal करता है।
  5. Supervisor final status record में terminal state आने तक या reporting deadline तक poll करता है।

किसी भी signal से पहले control action को run record validate करना चाहिए। कम-से-कम जाँचें कि record directory expected user की है, PID अभी मौजूद है, recorded PGID ps से मेल खाता है और start time शुरू किए गए process के अनुरूप है। Linux PIDs दोबारा इस्तेमाल कर सकता है। Stale PID file और unconditional kill से failed cleanup script कई सप्ताह बाद किसी असंबंधित काम को समाप्त कर सकती है।

Cleanup command को «मेरे पिछले task का process kill करो» जैसे generic agent instruction के पीछे न रखें। Agent को opaque run handle दें। Supervisor उस handle को narrowly scoped remote action में बदले। इससे auditing भी साफ़ रहती है: reviewer देखता है कि run 6c4... ने एक host पर PGID 24182 रद्द करने का अनुरोध किया, न कि agent ने arbitrary kill command बनाई।

Autonomous coding agents के लिए Sallyport, SSH key agent को दिखाए बिना SSH actions चला सकता है। इससे credential custody cancellation contract से अलग रहती है, हालांकि उस contract को फिर भी run handle, group check, remote lease और final record चाहिए।

सही retry decision side effect पर निर्भर करता है

केवल file पढ़ने वाली command को unknown disconnect के बाद सामान्यतः दोबारा चलाया जा सकता है। User बनाना, migration लागू करना, certificate rotate करना या deployment शुरू करना ऐसा काम नहीं है। SSH layer यह नहीं बता सकती कि operation दोहराने के लिए सुरक्षित हो गया है या नहीं।

Side effects वाली remote commands को run ID से निकला idempotency token दें। Remote program को token को operation के result के साथ store करना चाहिए और वही token फिर मिलने पर मौजूदा result लौटाना चाहिए। यदि यह संभव न हो, तो preflight query जोड़ें जो बताए कि requested change पहले ही हुआ है या नहीं।

Idempotency के बदले cleanup का उपयोग न करें। Perfect TERM भी remote API के request स्वीकार करने के बाद लेकिन command के response print करने से पहले पहुँच सकता है। KILL भी database transaction commit होने के बाद पहुँच सकता है। Process cleanup यह बताता है कि worker अभी execute कर रहा है या नहीं। यह external effects को rollback नहीं करता।

Control plane में तीन outcomes रखें: completed, confirmed cleanup के साथ cancelled और unknown। Unknown असुविधाजनक है, लेकिन अगले operator को आवश्यक जानकारी देता है: कोई और action जारी करने से पहले remote state देखें। यह wishful thinking पर आधारित हरे Retry button से कहीं बेहतर है।

Cancellation feature तब तैयार है जब आप हर boundary पर इसे interrupt कर सकें और surviving process tree, disk पर मौजूद output, final record और retry decision समझा सकें। यदि disposable host पर ऐसा नहीं कर सकते, तो रात के दो बजे production host पर इस पर भरोसा न करें।

सामान्य प्रश्न

AI एजेंट द्वारा रद्द किए जाने पर remote SSH command को कैसे रोकें?

रद्दीकरण को स्थानीय SSH client के बंद होने की तरह न देखें, बल्कि अलग नियंत्रण कार्रवाई मानें। रिकॉर्ड किए गए remote process group को signal भेजें, सीमित cleanup अवधि तक प्रतीक्षा करें और फिर durable record से run की अंतिम स्थिति जाँचें। यदि नियंत्रण पथ पहले ही समाप्त हो चुका है, तो remote lease या watchdog को यह तय करना चाहिए कि काम कब रोका जाए।

क्या SSH connection बंद करने से remote command समाप्त हो जाती है?

नहीं। SSH channel बंद होने से channel समाप्त होता है, लेकिन इससे यह सुनिश्चित नहीं होता कि server command या उसके descendants को SIGTERM भेजेगा। PTY terminal hangup signals के कारण व्यवहार बदल सकता है, लेकिन फिर भी इसे ऐसा cleanup contract न मानें जिस पर निर्भर किया जा सके।

Remote shell script से शुरू हुए child processes को कैसे समाप्त करें?

हर remote run के लिए अलग process group या session बनाएँ और negative PGID को signal भेजें, जैसे kill -TERM -- -12345। भरोसा करने से पहले PGID की जाँच करें। केवल shell PID को समाप्त करने पर background children, pipelines और subprocesses बचे रह सकते हैं।

क्या remote agent commands के लिए setsid का उपयोग करना चाहिए?

सामान्य setsid invocation शुरू की गई command के लिए नया session और process group बनाती है, इसलिए supervisor के पास समाप्त करने के लिए एक group रहता है। ऐसे काम के लिए इसका उपयोग न करें जिसे cancellation के बाद भी चलते रहना चाहिए। ps से बने हुए SID और PGID की जाँच करें, क्योंकि wrappers और service managers process tree बदल सकते हैं।

SSH disconnect या network failure के बाद cleanup कैसे करें?

यदि काम का चलते रहना असुरक्षित या महँगा होगा, तो remote process के लिए अधिकतम runtime या renewable lease रखें। Lease remote host पर होनी चाहिए, क्योंकि client के connection टूटने पर यह भरोसा नहीं किया जा सकता कि वह अपने गायब होने की सूचना देगा। Cancellation signal काम को जल्दी रोक सकता है, जबकि lease उस स्थिति को संभालती है जिसमें signal कभी पहुँचता ही नहीं।

SSH connection टूटने के बाद क्या partial output पर भरोसा किया जा सकता है?

Observation के लिए stream output रखें, लेकिन उसे remote file या journal में भी लिखें। अलग completion record केवल command के exit होने और cleanup पूरा होने के बाद लिखें। टूटा हुआ stream यह बताता है कि delivery विफल हुई, यह नहीं कि command पूरी हुई या नहीं।

क्या PR_SET_PDEATHSIG remote process cleanup के लिए पर्याप्त है?

यह Linux-specific है और केवल किसी process को उसके direct parent की मृत्यु का पता लगाने में मदद करता है। PR_SET_PDEATHSIG grandchildren को अपने-आप cover नहीं करता और privilege changes इस setting को साफ कर सकते हैं। इसे purpose-built worker में स्थानीय अतिरिक्त सुरक्षा के रूप में इस्तेमाल करें, arbitrary commands के लिए अकेले stop mechanism के रूप में नहीं।

क्या agent को remote SSH commands के लिए PTY देना चाहिए?

आमतौर पर नहीं। Terminal signal delivery, buffering और program behavior बदलता है, जबकि non-interactive command अधिक साफ machine output देती है। PTY तभी दें जब remote program को सचमुच terminal semantics चाहिए, और तब hangup behavior की अलग जाँच करें।

SSH cancellation audit record में क्या होना चाहिए?

Random run ID, remote host identity, शुरुआती PID, PGID, SID, launch time, requested command और अंतिम परिणाम दर्ज करें। Remote record को अन्य users से सुरक्षित रखें और अंतिम status update को atomic बनाएँ। केवल PID कमजोर प्रमाण है, क्योंकि PIDs दोबारा इस्तेमाल हो सकते हैं।

क्या agent SSH पर destructive work सुरक्षित रूप से चला सकता है?

बिना सीमा वाली SSH command भेजकर और स्थानीय process बंद होते ही job को cancelled कहकर काम न चलाएँ। Run handle, process-group check, cleanup deadline और final remote status अनिवार्य करें। Sallyport SSH credential को agent से दूर रख सकता है, लेकिन cancellation ईमानदार होगी या नहीं, यह remote command का design तय करता है।

Sallyport

Sallyport आपके AI एजेंट के लिए API कॉल और SSH कमांड चलाता है। कुंजियाँ आपके Mac पर एक लोकल वॉल्ट में रहती हैं; आप हर रन को स्वीकृत करते हैं और हर क्रिया एक सीलबंद जर्नल में दर्ज होती है।

© 2026 Sallyport · Apache-2.0 के तहत ओपन सोर्स · Oleg Sotnikov