Signal Lab को सर्वर के रूप में चलाना
signal-lab-server Signal Lab है बिना विंडो के: वही इंजन, ब्राउज़र को वही इंटरफ़ेस परोसता हुआ। इसे उपकरणों के पास वाली किसी मशीन पर रखें — रैक PC, शो-कंट्रोल VM, साझा लैब बॉक्स — और नेटवर्क पर कहीं से भी Chrome, Firefox या Edge में खोलें: हर स्क्रीन डेस्कटॉप ऐप की तरह काम करती है, और रन, रिपोर्ट तथा एक्सपोर्ट ब्राउज़र से डाउनलोड होते हैं।
स्क्रिप्ट और पाइपलाइन उसी सर्वर का उपयोग उसके HTTP API के ज़रिए करते हैं, और signallab --server उस पर रन भेजता है।
डेस्कटॉप ऐप से कुछ चीज़ें अलग हैं:
- एक सर्वर एक इंजन। उस पर साइन इन हर पेज को वही चल रहे जॉब, वही सिग्नल और एमुलेटर लाइब्रेरी, और HTTP स्क्रीन की वही कुकी जार (कुकी रखें) दिखती है। जो लोग एक सर्वर साझा करते हैं, वे ये सब भी साझा करते हैं।
- सीक्रेट सर्वर के हैं, उसके एनवायरनमेंट या फ़ाइलों से पढ़े जाते हैं; ब्राउज़र से उन्हें सेट नहीं किया जा सकता (देखें सीक्रेट)।
- फ़ायरवॉल होस्ट का है। सर्वर उसे कभी नहीं बदलता; डेस्कटॉप ऐप की फ़ायरवॉल सूचना नहीं दिखती।
- यह अपनी इमेज के साथ अपडेट होता है, ऐप के अपडेटर से नहीं (देखें अपडेट करना)।
Linux होस्ट पर, एक ही कमांड में
इंटरनेट वाली Linux मशीन पर:
curl -fsSL https://raw.githubusercontent.com/ProAnima/SignalLab/main/deploy/install.sh | shस्क्रिप्ट:
- अगर Docker नहीं है तो उसे इंस्टॉल करती है — पहले पूछकर, Docker के अपने इंस्टॉलर (
get.docker.com) से; /opt/signallabमें एकcompose.yamlलिखती है (जब आप रूट न हों तो~/signallab);- इमेज खींचती है और सर्वर को होस्ट नेटवर्किंग के साथ शुरू करती है, ताकि OSC, UDP, ब्रॉडकास्ट, मल्टीकास्ट और डिस्कवरी असली नेटवर्क तक पहुँचें;
- तब तक प्रतीक्षा करती है जब तक सर्वर अपनी हेल्थ चेक का जवाब न दे (अधिकतम 90 सेकंड);
- खोलने के पते, साइन इन करने का एक्सेस टोकन, और अपडेट करने, लॉग पढ़ने तथा हटाने के कमांड छापती है;
- जब ufw या firewalld चालू हो, तो सर्वर का पोर्ट खोलने का प्रस्ताव देती है (देखें होस्ट का फ़ायरवॉल)।
sh -s -- के बाद विकल्प दें:
curl -fsSL https://raw.githubusercontent.com/ProAnima/SignalLab/main/deploy/install.sh | sh -s -- --version 1.0.0 --port 8430| विकल्प | यह क्या करता है | डिफ़ॉल्ट |
|---|---|---|
--version X.Y.Z | इमेज का संस्करण (latest या X.Y.Z; आगे लगा v हटा दिया जाता है)। | latest |
--port N | ब्राउज़र जिस पोर्ट का उपयोग करते हैं। | 1430 |
--listen IP:PORT | केवल एक पते पर सुनें। | 0.0.0.0:<port> |
--dir DIR | compose फ़ाइल कहाँ जाएगी। | रूट होने पर /opt/<name>, वरना ~/<name> |
--name NAME | कंटेनर और उसका डेटा वॉल्यूम: छोटे अक्षर, अंक, - और _। एक ही होस्ट पर दूसरे सर्वर के लिए अपना अलग नाम चाहिए। | signallab |
--image NAME | कोई दूसरी इमेज या रजिस्ट्री; पूरा NAME:TAG जैसा है वैसा ही उपयोग होता है। | ghcr.io/proanima/signallab |
--open-udp PORTS | फ़ायरवॉल चालू होने पर, इन पोर्ट पर UDP भी अंदर आने दें, मॉनिटर और waits के लिए: 9000,9100:9110। | |
--no-firewall | ufw या firewalld कभी न बदलें। | |
--yes, -y | हाँ कहें: Docker इंस्टॉल करें, फ़ायरवॉल खोलें, --purge के साथ डेटा मिटाएँ। | |
--uninstall | सर्वर रोकें और हटाएँ; डेटा रहता है। | |
--purge | --uninstall के साथ: डेटा और टोकन भी मिटाएँ। | |
--help, -h | विकल्प छापें। |
इसे Docker के Compose प्लगइन की ज़रूरत है (docker-compose-plugin पैकेज), जो Docker का इंस्टॉलर साथ लाता है। इमेज x86_64 और arm64 के लिए बनी है।
अपडेट करने के लिए इसे फिर चलाएँ: वही कमांड नवीनतम इमेज (या आपका दिया --version) खींचती है और सर्वर को रीस्टार्ट करती है; डेटा और टोकन रहते हैं। --name के साथ, वही नाम फिर दें।
अपनी सेटिंग्स — प्रयोग के सीक्रेट, SIGNALLAB_ALLOWED_HOSTS, HTTPS के पीछे SIGNALLAB_SECURE_COOKIE — compose फ़ाइल के बगल वाली compose.override.yaml में डालें। Docker Compose उसे मिला देता है, और स्क्रिप्ट हर रन पर compose.yaml फिर से लिखती है पर override को कभी नहीं छूती। जो compose.yaml उसने नहीं लिखी, वह compose.yaml.before-install के रूप में रखी जाती है।
# compose.override.yaml
services:
signallab:
environment:
SIGNALLAB_ALLOWED_HOSTS: lab-pc.example.com,192.0.2.10
SIGNALLAB_SECRET_API_TOKEN: ${API_TOKEN}इसे हटाने के लिए:
curl -fsSL https://raw.githubusercontent.com/ProAnima/SignalLab/main/deploy/install.sh | sh -s -- --uninstallडेटा अपने Docker वॉल्यूम में रहता है, और दोबारा इंस्टॉल करने पर वही टोकन लेकर वापस आता है। --uninstall --purge पूछने के बाद डेटा और टोकन भी मिटा देता है।
होस्ट का फ़ायरवॉल
होस्ट नेटवर्किंग के साथ सर्वर होस्ट के ही पोर्ट पर सुनता है, इसलिए यह होस्ट का फ़ायरवॉल तय करता है कि उस तक कौन पहुँचता है। जब ufw या firewalld चालू हो, तो स्क्रिप्ट सर्वर का TCP पोर्ट और --open-udp के UDP पोर्ट खोलने से पहले पूछती है, जो खोला उसे लिख लेती है (compose फ़ाइल के बगल .firewall में), और --uninstall ठीक वही फिर बंद कर देता है। आप मना करें, तो दूसरी मशीनों के ब्राउज़र सर्वर तक तभी पहुँचते हैं जब फ़ायरवॉल उन्हें आने दे, और मॉनिटर तथा waits दूसरी मशीनों को केवल उन UDP पोर्ट पर सुनते हैं जो वह खोलता है।
Docker
इमेज
ghcr.io/proanima/signallab, linux/amd64 और linux/arm64 के लिए, हर रिलीज़ के साथ प्रकाशित:
| टैग | यह क्या है |
|---|---|
X.Y.Z | वह रिलीज़। |
X.Y | उस श्रृंखला की नवीनतम स्थिर रिलीज़। |
latest | नवीनतम स्थिर रिलीज़। |
इसमें signal-lab-server, बना हुआ इंटरफ़ेस और कमांड लाइन signallab होते हैं, और यह सर्वर को इन सेटिंग्स के साथ शुरू करती है:
| वैरिएबल | इमेज में मान |
|---|---|
SIGNALLAB_LISTEN | 0.0.0.0:1430 |
SIGNALLAB_DATA_DIR | /data |
SIGNALLAB_UI_DIR | /usr/share/signal-lab/ui |
SIGNALLAB_GENERATE_TOKEN | true |
यह एक बिना विशेषाधिकार वाले उपयोगकर्ता (uid और gid 10001) के रूप में चलती है, केवल /data (एक वॉल्यूम) में लिखती है, पोर्ट 1430 खोलती है, और हर 30 सेकंड में अपनी हेल्थ जाँचती है।
इसे शुरू करना
docker run -d --name signallab --network host --restart unless-stopped \
-v signallab-data:/data --read-only --cap-drop ALL --security-opt no-new-privileges \
ghcr.io/proanima/signallab:1.0.0
docker logs signallab # on the first start: "Sign in with it:" and the tokenफिर http://<host>:1430 खोलें और टोकन से साइन इन करें। बाद में टोकन फिर देखने के लिए:
docker exec signallab cat /data/token--read-only, --cap-drop ALL और no-new-privileges वैकल्पिक हैं और कुछ खर्च नहीं करते: सर्वर को किसी विशेषाधिकार की ज़रूरत नहीं और वह केवल /data में लिखता है।
Docker Compose
रिपॉज़िटरी की deploy/compose.yaml Compose फ़ाइल जैसी ही है:
name: signallab
services:
signallab:
image: ${SIGNALLAB_IMAGE:-ghcr.io/proanima/signallab:latest}
container_name: signallab
network_mode: host
environment:
SIGNALLAB_GENERATE_TOKEN: "true"
volumes:
- signallab-data:/data
read_only: true
cap_drop: [ALL]
security_opt: ["no-new-privileges:true"]
restart: unless-stopped
stop_grace_period: 15s
volumes:
signallab-data:docker compose up -d
docker exec signallab cat /data/tokenकिसी संस्करण को पिन करने के लिए SIGNALLAB_IMAGE=ghcr.io/proanima/signallab:X.Y.Z सेट करें।
नेटवर्किंग
| Docker नेटवर्किंग | क्या काम करता है | क्या नहीं |
|---|---|---|
--network host (network_mode: host), Linux होस्ट पर | सब कुछ: LAN तक OSC, UDP, TCP, HTTP, WebSocket और MQTT, सुनने वाले पोर्ट, ब्रॉडकास्ट, मल्टीकास्ट, डिस्कवरी। | — |
| ब्रिज (डिफ़ॉल्ट), प्रकाशित पोर्ट के साथ | जिन होस्ट तक कंटेनर पहुँचता है उन्हें यूनिकास्ट; प्रकाशित पोर्ट पर लिसनर (-p 1430:1430 -p 9000:9000/udp)। | ब्रॉडकास्ट और मल्टीकास्ट; उन पोर्ट के जवाब जो प्रकाशित नहीं हैं। |
| Windows या macOS पर Docker Desktop | यूनिकास्ट और प्रकाशित लिसनर। | भौतिक नेटवर्क तक होस्ट नेटवर्किंग। Windows पर डेस्कटॉप ऐप उपयोग करें। |
डेटा वॉल्यूम
/data वह सब रखता है जो सर्वर सँभालता है: प्रयोग, सिग्नल और एमुलेटर लाइब्रेरी, रन रिपोर्ट, एक्सपोर्ट, और टोकन। ऊपर जैसा नामित वॉल्यूम शुरू में इमेज के उपयोगकर्ता के स्वामित्व में होता है। वहाँ माउंट किया गया होस्ट का कोई फ़ोल्डर uid 10001 से लिखने योग्य होना चाहिए:
sudo mkdir -p /srv/signallab && sudo chown 10001:10001 /srv/signallab
docker run -d --name signallab --network host -v /srv/signallab:/data ghcr.io/proanima/signallab:1.0.0Docker के बिना
सर्वर इमेज के रूप में प्रकाशित है। signal-lab-server को सीधे चलाने के लिए, इसे स्रोत से बनाएँ (देखें बनाना):
npm install
npm run build # the interface, into dist/
cargo run --release -p signal-lab-serverयह http://127.0.0.1:1430 पर dist/ परोसता है, केवल इसी मशीन के लिए, बिना टोकन की ज़रूरत के। नेटवर्क के लिए खोलने के लिए --listen और एक टोकन जोड़ें।
विकल्प
हर विकल्प का एक एनवायरनमेंट वैरिएबल होता है, कंटेनर के लिए। विकल्प वैरिएबल पर भारी पड़ते हैं।
| विकल्प | वैरिएबल | डिफ़ॉल्ट | यह क्या करता है |
|---|---|---|---|
--listen IP:PORT | SIGNALLAB_LISTEN | 127.0.0.1:1430 | कहाँ सुनें। लूपबैक के अलावा किसी भी पते के लिए टोकन चाहिए। |
--token-file PATH | SIGNALLAB_TOKEN_FILE | एक्सेस टोकन रखने वाली फ़ाइल (मान लें, कोई Docker सीक्रेट)। | |
--token TOKEN | SIGNALLAB_TOKEN | ख़ुद एक्सेस टोकन। फ़ाइल को प्राथमिकता दें: आर्ग्युमेंट मशीन के दूसरे उपयोगकर्ताओं को दिखते हैं। | |
--generate-token | SIGNALLAB_GENERATE_TOKEN | बंद | टोकन दिए बिना, लूपबैक से आगे के किसी पते पर: <data folder>/token में रखा टोकन उपयोग करें, पहली बार शुरू होने पर उसे बनाकर। |
--data-dir PATH | SIGNALLAB_DATA_DIR | उपयोगकर्ता के home फ़ोल्डर में Documents/SignalLab | डेटा फ़ोल्डर। |
--secrets-dir PATH | SIGNALLAB_SECRETS_DIR | /run/secrets/signallab | केवल पढ़ने योग्य सीक्रेट का फ़ोल्डर, हर नाम के लिए एक फ़ाइल। |
--ui-dir PATH | SIGNALLAB_UI_DIR | प्रोग्राम के बगल ui, वरना ./dist | बना हुआ इंटरफ़ेस। इसके बिना केवल API परोसी जाती है। |
--allowed-host NAME | SIGNALLAB_ALLOWED_HOSTS | होस्ट नाम जिनसे सर्वर तक पहुँचा जा सकता है, कॉमा से अलग। ये और लूपबैक नाम हमेशा स्वीकार होते हैं, टोकन के साथ या बिना; कुछ न दिया हो तो टोकन वाला सर्वर किसी भी नाम को जवाब देता है और बिना टोकन वाला केवल लूपबैक नामों को (देखें होस्ट नाम)। | |
--secure-cookie | SIGNALLAB_SECURE_COOKIE | बंद | सेशन कुकी केवल HTTPS पर भेजें। इसे HTTPS प्रॉक्सी के पीछे सेट करें। |
--log FILTER | SIGNALLAB_LOG | info | क्या लॉग करें: error, warn, info, debug, या प्रति मॉड्यूल (signal_lab_server=debug)। |
--log-format text|json | SIGNALLAB_LOG_FORMAT | text | लॉग पंक्तियाँ टेक्स्ट में, या हर पंक्ति पर एक JSON ऑब्जेक्ट। |
स्विच अपने वैरिएबल से true या false लेते हैं: SIGNALLAB_GENERATE_TOKEN=true.
| कमांड | यह क्या करती है |
|---|---|
signal-lab-server token | एक नया रैंडम टोकन छापती है: 64 हेक्साडेसिमल अक्षर। |
signal-lab-server healthcheck | जब --listen पर कोई सर्वर जवाब दे तो 0 के साथ बाहर निकलती है (इमेज की हेल्थ चेक)। |
signal-lab-server --version | संस्करण छापती है। |
signal-lab-server --help | हर विकल्प छापती है। |
एग्ज़िट कोड: Ctrl+C या SIGTERM से रुकने पर 0; सेटिंग्स अस्वीकार होने पर 2 (पहुँच योग्य पते पर टोकन नहीं, टोकन बहुत छोटा, टोकन दो बार दिया गया, पढ़ी न जा सकने वाली टोकन फ़ाइल, डेटा फ़ोल्डर के बिना --generate-token); पते पर सुन न पाने, या डेटा फ़ोल्डर में लिख न पाने पर 1। कारण stderr पर छपता है।
एक्सेस टोकन
टोकन के बिना सर्वर केवल लूपबैक पर सुनता है और केवल इस मशीन को परोसता है। किसी दूसरे पते पर उसे टोकन चाहिए, और उसके बिना वह शुरू होने से इनकार कर देता है। इसे देने के तीन तरीके हैं:
| कैसे | कब उपयोग करें |
|---|---|
--generate-token (इमेज में चालू) | कुछ सेट करने की ज़रूरत नहीं: पहली बार शुरू होने पर सर्वर एक टोकन बनाता है, उसे <data folder>/token में सहेजता है — केवल उसके अपने उपयोगकर्ता से पढ़ने योग्य — और उसे लॉग में एक बार छापता है। आगे के शुरू होने पर वही उपयोग होता है, इसलिए साइन इन किए ब्राउज़र और स्क्रिप्ट रीस्टार्ट और अपडेट के पार चलते रहते हैं। इसे एक डेटा फ़ोल्डर (--data-dir) चाहिए। |
--token-file PATH | किसी फ़ाइल में आपका अपना टोकन, जैसे कोई Docker सीक्रेट। अंत में लगा लाइन ब्रेक टोकन का हिस्सा नहीं होता। |
SIGNALLAB_TOKEN | एनवायरनमेंट में टोकन। |
टोकन में कम से कम 24 अक्षर होते हैं और कोई स्पेस या लाइन ब्रेक नहीं; इसे केवल एक ही तरीके से दें। signal-lab-server token अच्छा टोकन बनाती है:
docker run --rm ghcr.io/proanima/signallab:1.0.0 token > signallab_token.txtऔर Compose उसे एक सीक्रेट के रूप में देता है (फ़ाइल uid 10001 से पढ़ने योग्य होनी चाहिए):
services:
signallab:
environment:
SIGNALLAB_TOKEN_FILE: /run/secrets/signallab_token
secrets:
- signallab_token
secrets:
signallab_token:
file: ./signallab_token.txtस्पष्ट रूप से दिया टोकन --generate-token पर भारी पड़ता है, और तब कुछ नहीं बनाया जाता। बना हुआ टोकन बदलने के लिए, सर्वर रोकें, <data folder>/token मिटाएँ और उसे फिर शुरू करें: वह एक नया बनाकर छाप देगा। ख़राब टोकन फ़ाइल की सूचना दी जाती है, उसे कभी बदला नहीं जाता।
साइन इन करना
http://<host>:1430 खोलें। टोकन वाला सर्वर ब्राउज़र को पहले उसके साइन-इन पेज पर भेजता है, ब्राउज़र की भाषा में; टोकन एक बार पेस्ट करें, और ब्राउज़र 7 दिन तक साइन इन रहता है। इंटरफ़ेस में साइन आउट करें सेशन समाप्त कर देता है। सेशन सर्वर की मेमोरी में रखे जाते हैं: रीस्टार्ट से सभी साइन आउट हो जाते हैं।
लूपबैक पर टोकन के बिना कोई साइन-इन नहीं होता।
स्क्रिप्ट हर रिक्वेस्ट के साथ टोकन भेजती हैं, Authorization: Bearer <token> के रूप में:
curl -fsS http://192.0.2.10:1430/api/invoke/app_info \
-H "Authorization: Bearer $(cat signallab_token.txt)" \
-H "Content-Type: application/json" -d 'null'देखें HTTP API, और इन सबके पीछे के नियमों के लिए सर्वर की सुरक्षा।
डेटा फ़ोल्डर
सर्वर अपनी फ़ाइलें डेटा फ़ोल्डर में रखता है: प्रयोग, सिग्नल और एमुलेटर लाइब्रेरी, रन रिपोर्ट, एक्सपोर्ट, और बनाया गया टोकन। यह --data-dir (SIGNALLAB_DATA_DIR) होता है, इमेज में /data, और वरना जिस उपयोगकर्ता के रूप में यह चलता है उसके home फ़ोल्डर में Documents/SignalLab। --data-dir से दिया डेटा फ़ोल्डर न होने पर बना दिया जाता है और शुरू होने पर जाँचा जाता है: अगर सर्वर वहाँ न लिख सके, तो वह रुक जाता है और फ़ोल्डर का नाम बताता है। देखें फ़ाइलें और फ़ोल्डर।
डाउनलोड (रिपोर्ट, एक्सपोर्ट) केवल इसी फ़ोल्डर के भीतर से आते हैं।
सीक्रेट
प्रयोग सीक्रेट को {{secret.NAME}} के रूप में पढ़ते हैं। सर्वर पर ये केवल पढ़ने योग्य होते हैं, इनसे:
- एनवायरनमेंट वैरिएबल
SIGNALLAB_SECRET_NAME, वरना - सीक्रेट फ़ोल्डर में फ़ाइल
NAME(--secrets-dir, डिफ़ॉल्ट रूप से/run/secrets/signallab— Docker सीक्रेट लेआउट)।
किसी फ़ाइल का अंत में लगा लाइन ब्रेक मान का हिस्सा नहीं होता; खाली मान को सेट न किया हुआ माना जाता है; मान अधिकतम 16 KiB होता है। नाम अक्षर, अंक और _ होते हैं, अंक से शुरू नहीं होते। इंटरफ़ेस दिखाता है कि सर्वर पर कौन-से सीक्रेट सेट हैं, पर ब्राउज़र से कोई सेट करना अस्वीकार किया जाता है — मान कभी किसी कमज़ोर जगह नहीं जाते, और कभी वापस बाहर नहीं आते (देखें सीक्रेट)।
Compose के साथ, हर नाम के लिए एक सीक्रेट फ़ाइल:
services:
signallab:
secrets:
- source: api_token
target: /run/secrets/signallab/API_TOKEN
secrets:
api_token:
file: ./api_token.txtफ़ाइल uid 10001 से पढ़ने योग्य होनी चाहिए।
HTTPS प्रॉक्सी के पीछे
सर्वर सादा HTTP बोलता है। HTTPS के लिए, उसके आगे एक रिवर्स प्रॉक्सी रखें (Caddy, nginx, Traefik) जो:
Hostहेडर को बिना बदले आगे भेजे;- WebSocket अपग्रेड आगे भेजे (इंटरफ़ेस
/api/eventsके लिए एक खुला रखता है);
और सर्वर को --secure-cookie के साथ शुरू करें, ताकि सेशन कुकी केवल HTTPS पर जाए, और --allowed-host को वह नाम देकर जिससे लोग पहुँचते हैं।
अपडेट करना
सर्वर अपनी इमेज के साथ अपडेट होता है: डेस्कटॉप ऐप के अपडेटर का इससे कोई लेना-देना नहीं।
- स्क्रिप्ट से इंस्टॉल किया: वही कमांड फिर चलाएँ।
- Compose के साथ:
docker compose pull && docker compose up -d. docker runके साथ: नई इमेज खींचें, फिर कंटेनर हटाएँ और उसे उसी वॉल्यूम के साथ फिर शुरू करें।
डेटा और टोकन वॉल्यूम में हैं, इसलिए वे रहते हैं।
हेल्थ चेक
GET /api/health सबको जवाब देता है, बिना टोकन:
curl -s http://127.0.0.1:1430/api/health
# {"auth":true,"status":"ok","version":"1.0.0"}auth बताता है कि सर्वर टोकन माँगता है या नहीं। कमांड signal-lab-server healthcheck इसी मशीन पर वही पूछती है और जवाब आने पर 0 के साथ बाहर निकलती है; इमेज इसे हर 30 सेकंड में चलाती है (5 सेकंड का टाइमआउट, 3 प्रयास), इसलिए docker ps कंटेनर को स्वस्थ दिखाता है।
लॉग
सर्वर stdout पर लॉग करता है: docker logs -f signallab. डिफ़ॉल्ट स्तर (info) पर यह बताता है कि कहाँ सुनता है और उसे टोकन चाहिए या नहीं, उसके डेटा और इंटरफ़ेस फ़ोल्डर, शुरू किया गया हर जॉब — मॉनिटर, जनरेटर, स्टॉर्म, स्कैन, रन, एमुलेटर — क्लाइंट के पते के साथ, हर साइन इन, ग़लत टोकन के साथ हर साइन इन (चेतावनी के रूप में), और जब कोई पेज इवेंट से पीछे छूटता है। स्तर --log debug हर कमांड जोड़ देता है। पंक्तियाँ केवल टर्मिनल पर रंगीन होती हैं (NO_COLOR सेट होने पर कभी नहीं); --log-format json लॉग कलेक्टर के लिए हर पंक्ति पर एक JSON ऑब्जेक्ट लिखता है।
रोकना
Ctrl+C, docker stop या SIGTERM हर पेज का कनेक्शन बंद कर देता है, हर जॉब रोक देता है — चल रहा रन रुका हुआ बनकर समाप्त होता है — और 0 के साथ बाहर निकलता है। compose फ़ाइलें इसके लिए उसे 15 सेकंड देती हैं।