Synx का परिचय: रिमोट डेवलपमेंट के लिए फ़ाइल सिंक
रिमोट डेवलपमेंट हमेशा एक ही आकार पर आकर टिकता है, और अगर आपने इसे जिया है तो आप इसे एक वाक्य में पहचान लेंगे: जिस मशीन पर आप सोचते हैं और जिस मशीन पर काम चलता है, वे एक मशीन नहीं हैं।
लैपटॉप वह जगह है जहाँ आप सोचते हैं। वहाँ वह एडिटर है जिसकी कीबाइंडिंग आपकी उँगलियों में बसी है, आपकी थीम, आपका fuzzy finder, आपका डिफ़ टूल, आपका क्लिपबोर्ड, आपका language server ठीक वैसा ट्यून किया हुआ जैसा आपको पसंद है, और वह AI एजेंट जिसे आपने इसी प्रोजेक्ट पर लगा रखा है। यही इकलौती मशीन है जिस पर आप सचमुच तेज़ हैं।
सर्वर वह जगह है जहाँ काम होता है। आठ के बजाय चौंसठ कोर। वह RAM, या GPU, या वह इकलौता कर्नेल जिसके ख़िलाफ़ आपका ड्राइवर बनता है, या वह प्राइवेट नेटवर्क जहाँ से डेटाबेस तक पहुँचा जा सकता है। यह वह मशीन है जहाँ रिलीज़ बिल्ड ग्यारह मिनट के बजाय नब्बे सेकंड में ख़त्म होती है, जहाँ टेस्ट सूट हर कोर पर फैल जाती है, जहाँ कंटेनर प्रोडक्शन से मेल खाता है क्योंकि वह उसी इमेज से बना है।
कोई भी दूसरे का काम नहीं कर सकती, और आप चाहते भी नहीं कि करे। आप यहाँ एडिट करते रहना चाहते हैं और वहाँ बिल्ड करते रहना चाहते हैं।
इससे पूरा रिमोट डेवलपमेंट एक बेरौनक़ सवाल पर सिमट आता है: फ़ाइलें एक तरफ़ से दूसरी तरफ़ कैसे पहुँचें — लगातार, दोनों दिशाओं में, इतनी तेज़ी से कि आप भूल जाएँ कि यह हो भी रहा है? इसे सही कर लीजिए और पूरा सेटअप ग़ायब हो जाता है — आप टाइप करते हैं, सेव दबाते हैं, एक टर्मिनल पर जाते हैं जो संयोग से किसी और महाद्वीप पर है, और वह बिल्ड हो जाता है। इसे ग़लत कर दीजिए और आपका पूरा दिन अपने ही सोर्स ट्री का कूरियर बनने में बीतता है।
वे जवाब जो टिकते नहीं
बस मशीन पर ही काम करो। SSH से घुसिए, vim खोलिए, और समस्या भाप बन जाती है — कॉपी हमेशा एक ही रहती है। कुछ लोग यहाँ सचमुच ख़ुश हैं और मैं उन्हें बहलाने नहीं जा रहा। पर इसकी क़ीमत आपका पूरा लोकल माहौल है: एडिटर की कॉन्फ़िग, एक्सटेंशन, आपका GUI git क्लाइंट, क्लिपबोर्ड, स्क्रीनशॉट टूल, आपकी अपनी मशीन पर चलने वाला AI असिस्टेंट। और अब हर कीस्ट्रोक नेटवर्क के पीछे है। फ़ाइबर पर ठीक है। होटल के वाई-फ़ाई पर आप हर अक्षर को उतरते हुए महसूस करते हैं।
रिमोट को नेटवर्क पर माउंट कर लो। SSHFS रिमोट डायरेक्टरी को एक लोकल पाथ बना देता है, जो सुनने में ठीक वही लगता है जो आप चाहते हैं — जब तक आप यह न देखें कि अब तार पर क्या जा रहा है: हर stat()। आपके एडिटर का फ़ाइल वॉचर, आपके language server का इंडेक्सर, आपका git status — हर एक हज़ारों पाथ चलता है, और हर चाल एक राउंड ट्रिप है। रिपोर्टें एक जैसी हैं: जैसे ही कोई चीज़ थोक में फ़ाइलें छूती है — कोई git checkout या npm install — प्रदर्शन खाई में गिर जाता है। macOS पर हाल और बुरा है, क्योंकि वहाँ FUSE का क़िस्सा सालों से असहज है। यह अमूर्तन ठीक उस पल तक प्यारा है जब तक लेटेंसी इसे बेकार न कर दे।
rsync को एक लूप में चलाना। ईमानदार जुगाड़, जिसे हर किसी ने कम से कम एक बार लिखा है: fswatch | rsync, या एक while true; sleep 1। एक-दिशा स्नैपशॉट के लिए यह सही है। लाइव में यह बिगड़ता है — या तो हर कीस्ट्रोक पर चलकर लिंक भर देता है, या बैच बनाकर आपके सेव से पीछे रह जाता है। यह डिज़ाइन से ही एकतरफ़ा है, तो अगर आप चाहते हैं कि जनरेट हुई फ़ाइलें या बिल्ड आउटपुट वापस आएँ तो आप दूसरा चलाते हैं — और अब सच्चाई की कोई साझा समझ रखे बिना दो प्रोसेस एक ही ट्री पर दौड़ रहे हैं। लोगों का घबराना जायज़ है: ग़लत फ़्लैग के साथ ग़लत दिशा में चला rsync असली काम मिटा देता है।
IDE को ही करने दो। VS Code Remote-SSH और JetBrains Gateway इसे ठीक से हल करते हैं — एडिटर के लिए: एक सर्वर कंपोनेंट रिमोट पर चलता है, UI लोकल रहता है। अगर आपकी पूरी दुनिया एक ही IDE के अंदर है, तो यह अच्छा जवाब है। पर फ़ाइलें अब भी सिर्फ़ उस पार मौजूद हैं, इसलिए एडिटर के बाहर सब कुछ अंधा है: आपका टर्मिनल, आपका अलग git क्लाइंट, आपकी लोकल स्क्रिप्ट, आपके लैपटॉप पर चलने वाला कोडिंग एजेंट। साथ ही आपने एक वेंडर के रिमोट प्रोटोकॉल में निवेश कर दिया, अपने एक्सटेंशन उस पार दोबारा इंस्टॉल किए, और यह मान लिया कि रिमोट इतना भरोसेमंद है कि उन्हें वहाँ चलाया जा सके।
Mutagen। मॉडल इसी ने सही पकड़ा: दोनों तरफ़ असली कॉपी, लगातार सिंक होते डेल्टा, लोकल एडिट, रिमोट एग्ज़िक्यूशन। यह अच्छा सॉफ़्टवेयर है और इसने अपनी साख कमाई है। पर लोग जिस घर्षण की रिपोर्ट करते हैं वह एक जैसा है, और वह गुणवत्ता से नहीं, आकार से आता है: यह एक डेमन और सेशन मैनेजर है, कमांड नहीं।
उस आकार का वज़न है। आपकी मशीन पर एक स्थायी डेमन रहता है और हर एंडपॉइंट के लिए एक एजेंट — चाहे आप काम कर रहे हों या नहीं, वे ज़िंदा रहते हैं — और इशू ट्रैकर में ऐसी रिपोर्टों की लंबी क़तार है कि यह उन दौरों में CPU जलाता है जब फ़ाइलें बदली ही नहीं, बड़े ट्री पर, और WSL के नीचे। डिस्क पर भी वज़न है: जिस Docker वर्कफ़्लो के लिए यह बना है, वहाँ आपका प्रोजेक्ट दो बार मौजूद रहता है, और DDEV की अपनी डॉक्युमेंटेशन आपसे उस डुप्लिकेट वॉल्यूम पर नज़र रखने को कहती है — 5 GB से ऊपर चेतावनी है, 10 GB से ऊपर गंभीर।
फिर परिचालन सतह है। आप सेशन बनाते हैं, उन्हें लिस्ट करते हैं, mutagen sync terminate सीखते हैं — और यह भी सीखते हैं कि फ़ाइलें स्टेज करते वक़्त वह अटक सकता है, और इशू थ्रेड्स में घूमता लोक-नुस्ख़ा यह है कि डेमन रोको, लॉक फ़ाइल मिटाओ, और फिर से चालू करो। इसका डिफ़ॉल्ट रूप से सुरक्षित कॉन्फ़्लिक्ट मोड रुककर इंसान का इंतज़ार करता है, जो सही फ़ैसला है, पर इसका मतलब है कि सेशन चुपचाप कन्वर्ज होना बंद कर सकता है जबकि आप मान रहे हों कि सब ठीक है; सिर्फ़ यह भरोसे से जान पाने के लिए कि सेशन टूटा है या नहीं, एक पुराना अनुरोध लंबे समय से लटका है। बड़े ट्री पर शुरुआती स्कैन में मिनट लग जाते हैं इससे पहले कि कुछ भी लाइव हो। कितनी सतह जमा हो गई, इसका सबसे साफ़ संकेत यह है कि DDEV — जो Mutagen पर बहुत टिका है — ने आख़िरकार एक पूरा डायग्नोस्टिक सबकमांड निकाला, ddev utility mutagen-diagnose, ताकि बता सके कि आपकी सिंक नाख़ुश क्यों है।
फिर ज़मीन खिसकी। 2023 में Docker ने Mutagen को ख़रीद लिया। Mutagen Compose को सीधे-सीधे बंद घोषित कर दिया गया — डॉक्युमेंटेशन साफ़ कहती है: "Mutagen Compose is now deprecated with the release of v0.18.0." ख़ुद Mutagen ने फ़रवरी 2025 की v0.18.1 के बाद कोई टैग किया हुआ रिलीज़ नहीं निकाला। रिपॉज़िटरी में अब भी कभी-कभार डिपेंडेंसी कमिट आते हैं, तो यह मरा नहीं है। पर हिल भी नहीं रहा।
इसमें कुछ भी घोटाला नहीं है, और मैं निष्पक्ष रहना चाहता हूँ: वह वज़न बस उस क़ीमत का नाम है जो एक सर्व-उद्देश्यीय सिंक इंजन को तब चुकानी पड़ती है जब उसे चार ऑपरेटिंग सिस्टम पर, एक ऑर्केस्ट्रेटर के नीचे, लोकल, SSH और Docker एंडपॉइंट सँभालने हों। मुझे बस इसमें से कुछ नहीं चाहिए था। मुझे एक डायरेक्टरी और एक पाथ चाहिए था।
तो मैं ऐसा कुछ ढूँढने निकला जो सिर्फ़ वही हो — और मिला नहीं। Unison है, जिसका CLI आप हर बार छूने पर दोबारा सीखते हैं। Syncthing है, जो एक डेवलपर के सोर्स ट्री के बजाय डिवाइस और साझा फ़ोल्डरों के इर्द-गिर्द बना है। उसके आगे अधूरे GitHub प्रोजेक्टों की एक लंबी पूँछ है। ऐसा कुछ नहीं जो बस इतना हो: यह डायरेक्टरी, वह पाथ, SSH पर, अभी, मेरे .gitignore का लिहाज़ करते हुए — और फिर मेरे रास्ते से हट जाओ।
तो मैंने ख़ुद लिख लिया।
मुझे इससे क्या चाहिए था
ज़रूरतों की सूची छोटी थी, और उसकी हर पंक्ति किसी ऐसी चीज़ से आई थी जिसने मुझे खिजाया था:
- एक कमांड, सेशन नहीं। कोई डेमन नहीं जिसकी देखभाल करनी पड़े,
status/terminate/ लॉक-फ़ाइल का कोई नाच नहीं। यह तब तक चलता है जब तक आप चाहें, औरctrl+cका मतलब है रुक गया। - कुछ भी स्थायी रूप से चलता हुआ नहीं। जब मैं सिंक नहीं कर रहा, तो कोई प्रोसेस है ही नहीं। न कोई कैश गरम कर रहा है, न कोई ट्री स्कैन कर रहा है, रात दो बजे एक्टिविटी मॉनिटर में सोचने को कुछ नहीं।
- कोई कॉन्फ़िग फ़ाइल नहीं। दो पाथ ही आर्ग्युमेंट हैं। रेपो में कमिट करने को कुछ नहीं, याद रखने को कुछ नहीं, पुराना पड़ने को कुछ नहीं।
.gitignoreही क़ानून है। आपका रेपो पहले ही घोषित कर चुका है कि क्या सोर्स नहीं है।target/,node_modules/,.venv/— वे डायरेक्टरियाँ जो हर भोले सिंक टूल को रेंगने पर मजबूर कर देती हैं — मैनिफ़ेस्ट में घुसनी ही नहीं चाहिए, किसी भी दिशा में।- डेटा कभी न खोए। जो सिंक टूल वह चीज़ मिटा दे जिसे आप रखना चाहते थे, वह किसी टूल के न होने से बदतर है। बाक़ी सब पर बात हो सकती है; इस पर नहीं।
- पहले से सिंक हो चुके ट्री पर सस्ता। दूसरे रन की क़ीमत एक डायरेक्टरी वॉक होनी चाहिए, पूरा री-हैश नहीं।
- सिर्फ़ SSH। आपकी कुंजियाँ, आपका
~/.ssh/config, आपकाProxyJump। कोई नई ऑथ सतह नहीं, खोलने को कोई नया पोर्ट नहीं।
इस सूची को दोबारा पढ़िए और देखिए कि इसमें क्या नहीं है: कंटेनर, ऑर्केस्ट्रेशन, नेटवर्क फ़ॉरवर्डिंग, Windows, कोई GUI, कोई कॉन्फ़िग स्कीमा, कोई प्लगइन सिस्टम। इनमें से हर एक चाहना वाजिब है, और इनमें से हर एक ही वह वजह है जिससे कोई सिंक टूल डेमन उगा लेता है। इन्हें बाहर छोड़ना विनम्रता नहीं है — यही पूरा डिज़ाइन है। टूल छोटा इसलिए रहता है क्योंकि उसने जो समस्या स्वीकारी वह छोटी थी।
यही Synx है: एक अकेली ओपन-सोर्स Rust बाइनरी — आज वर्शन 0.1.2 — जो एक लोकल डायरेक्टरी को SSH पर एक रिमोट पाथ पर मिरर करती है और आपके काम करते रहने के दौरान दोनों को कदम-से-कदम मिलाकर रखती है। एक प्रोसेस, सिर्फ़ तभी चलता हुआ जब आप काम कर रहे हों, एक काम करता हुआ। यह शुरुआती है। और यह वह काम पहले से ही करता है जो मुझे हर दिन इससे चाहिए था।
Synx क्या है
आप इसे अपनी लोकल मशीन पर चलाते हैं और एक रिमोट टारगेट की ओर इशारा करते हैं:
synx ./src dev@beefy:/srv/app/src
यही पूरा इंटरफ़ेस है। एक ही बाइनरी दोनों छोरों पर चलती है — लोकल पर यह क्लाइंट है, रिमोट पर यह एक छिपे --agent मोड में चलती है जिसे क्लाइंट आपके लिए SSH पर लॉन्च करता है। कोई सेवा इंस्टॉल नहीं करनी, कुछ रजिस्टर नहीं करना, कोई YAML नहीं।
यह एक शुरुआती मिलान-सिंक करता है, फिर एक लाइव वॉच में चला जाता है जहाँ किसी भी तरफ़ का हर बदलाव दूसरी तरफ़ कुछ सौ मिलीसेकंड में पहुँच जाता है:
synx /Users/dk/proj ◀─▶ dev@beefy:/srv/proj
✓ connected
• manifests: local 1243 • remote 1180 (47 ignored)
• plan: push 78 files (4.2 MiB) 6 dirs 0 links • pull 14 entries
✓ initial sync: 4.2 MiB sent, 312 KiB received in 1.4s
• watching for changes — ctrl+c to stop
→ src/main.rs 3.1 KiB
← README.md 824 B
यह macOS और Linux पर चलता है। ट्रांसपोर्ट सादा SSH है — आपकी कीज़, आपका एजेंट, आपका ~/.ssh/config, आपका ProxyJump, सब कुछ। Synx कोई नई ऑथ स्कीम नहीं गढ़ता; यह वही उधार लेता है जिस पर आप पहले से भरोसा करते हैं।
सिंक मोड और कॉन्फ़्लिक्ट हैंडलिंग
दिशा एक अकेला फ़्लैग है। डिफ़ॉल्ट दोतरफ़ा है।
| मोड | दिशा | कॉन्फ़्लिक्ट नियम | शुरुआती सिंक |
|---|---|---|---|
push |
लोकल → रिमोट | हमेशा लोकल जीतता है | केवल-लोकल या भिन्न फ़ाइलें भेजता है |
pull |
रिमोट → लोकल | हमेशा रिमोट जीतता है | केवल-रिमोट या भिन्न फ़ाइलें लाता है |
both (डिफ़ॉल्ट) |
दोतरफ़ा | नया mtime जीतता है | मर्ज करता है, कोई डिलीशन नहीं |
# दोतरफ़ा (डिफ़ॉल्ट)
synx ./src dev@host:/srv/app/src
# एकतरफ़ा push
synx ./build host:/var/www --mode push
# एकतरफ़ा pull
synx ./nginx host:/etc/nginx --mode pull
कॉन्फ़्लिक्ट मॉडल के बारे में मैं ईमानदार रहूँगा क्योंकि यह मायने रखता है: both मोड में, जब वही फ़ाइल दोनों तरफ़ बदली हो, तो नया मॉडिफ़िकेशन समय जीतता है। बस इतना ही। कोई पूर्वज-सजग तीन-तरफ़ा मर्ज नहीं, कोई कॉन्फ़्लिक्ट मार्कर नहीं। यह तभी तक भरोसेमंद है जब तक दोनों मशीनों की घड़ियाँ ठीक हों — तो अगर आप फ़ाइलों को आगे-पीछे उछलते देखें, सबसे पहले घड़ी के अंतर (clock skew) की जाँच करें (NTP इसे ठीक करता है), और अगर घड़ियों पर भरोसा न हो, तो एक स्पष्ट push या pull दिशा चुनें।
दूसरा सोच-समझकर लिया गया फ़ैसला: शुरुआती सिंक कभी कुछ डिलीट नहीं करता। अगर आप Synx को किसी पुराने रिमोट पाथ की ओर इशारा करते हैं, तो यह डेटा मिटाएगा नहीं — यह मर्ज करता है। डिलीशन तभी फैलते हैं जब Synx लाइव होकर देख रहा हो, और तभी जब उसके पास एक baseline हो — एक रिकॉर्ड किया हुआ स्नैपशॉट कि दोनों तरफ़ ने पिछली बार किस पर सहमति बनाई थी। baseline आपकी कैश डायरेक्टरी में रहता है और Synx को इस फ़र्क़ को पहचानने देता है कि "यह फ़ाइल डिलीट की गई" और "यह फ़ाइल दूसरी तरफ़ कभी थी ही नहीं"। बिना baseline के एक नया रन सब कुछ रखता है; डिलीशन दूसरी सिंक से आगे फैलना शुरू होते हैं। यह असमानता जानबूझकर है। किसी अति-उत्साही सिंक टूल की वजह से डेटा खोना — यही इकलौता विफलता-मोड है जिसे मैं शिप करने से इनकार करता हूँ।
इग्नोर नियम ही सर्वोच्च हैं
Synx आपके सिंक रूट के नीचे के हर .gitignore को लोड करता है — किसी भी गहराई पर नेस्टेड वाले — साथ में एक वैकल्पिक .synxignore जिसका सिंटैक्स बिल्कुल वैसा ही है। जो भी मेल खाता है वह कभी सिंक नहीं होता, किसी भी दिशा में नहीं। यह स्रोत पर लगाया गया कोई "बेहतर-कोशिश" फ़िल्टर नहीं है; इसे तीन जगहों पर लागू किया जाता है:
- शुरुआती वॉक — इग्नोर की गई फ़ाइलें मैनिफ़ेस्ट में आती ही नहीं।
- रिमोट मैनिफ़ेस्ट — रिमोट जिन फ़ाइलों की रिपोर्ट करता है और जो आपके लोकल इग्नोर नियमों से मेल खाती हैं, उन्हें डिफ़ प्लान चलने से पहले फ़िल्टर कर दिया जाता है, ताकि रिमोट पर संयोगवश मौजूद कोई
target/याnode_modules/कभी नीचे न खिंच आए। - लाइव इवेंट्स — आने वाले अप्लाई और जाने वाली नोटिफ़िकेशन दोनों इग्नोर किए गए पाथ छोड़ देते हैं।
एक बात जो लोगों को चौंकाती है: डॉटफ़ाइलें ख़ास नहीं हैं। .env, .vscode/, .git/ — सब किसी और चीज़ की तरह ही सिंक होते हैं जब तक आप उन्हें बाहर न रखें। अगर आप अपनी .git/ डायरेक्टरी मिरर नहीं करना चाहते, तो ऐसा कह दीजिए:
echo '/.git' >> .synxignore
.git/ की बात करें तो — Synx इसके साथ सावधान रहता है। Git उस डायरेक्टरी को ट्रांज़ैक्शनल स्टेट मानता है, और किसी कमिट के बीच में आधे-लिखे ऑब्जेक्ट या लॉक फ़ाइलें पीयर पर एटॉमिकली रीनेम करना रेपो को भ्रष्ट कर देता है। इसलिए Synx git के चल रहे ऑपरेशन के मार्कर (index.lock और साथी) पर नज़र रखता है और जब कोई git ऑपरेशन चल रहा हो तब .git/ पाथ की सिंक रोक देता है, और git के पूरा होने पर टाले गए बदलाव फिर से चला देता है। आपका वर्किंग ट्री इस पूरे समय सिंक होता रहता है; सिर्फ़ .git/ इंतज़ार करता है। अगर आप किसी जीवंत रेपो को मिरर करने जा रहे हैं, तो यह आपको चाहिए।
उल्टी दिशा में भी एक सुरक्षा है। अगर आपका लोकल .git/ किसी बीच में रुकी हुई ऑपरेशन का बचा हुआ हिस्सा है और रिमोट पर कोई .git/ नहीं है, तो Synx उस हटाने को तभी मिरर करता है जब baseline यह साबित करे कि .git/ उस आख़िरी स्थिति का हिस्सा था जिस पर दोनों तरफ़ सहमत थीं। उस सबूत के बिना आपका .git/ बस ऐसा डेटा है जो कभी सिंक हुआ ही नहीं — Synx उसे रखता है और उल्टा रिमोट पर भेज देता है।
यह अंदर से कैसे काम करता है
┌─ local (client) ──────────────┐ ┌─ remote (agent) ─────────────┐
│ watcher (notify) │ ssh │ watcher (notify) │
│ parallel walker (blake3) │ ◀─────▶ │ parallel walker (blake3) │
│ persistent hash cache │ stdio │ persistent hash cache │
│ diff plan + executor │ postcard│ message dispatcher │
└───────────────────────────────┘ + zstd └──────────────────────────────┘
कुछ हिस्से समझाने लायक हैं, क्योंकि रफ़्तार वहीं से आती है।
परसिस्टेंट कैश के साथ समानांतर हैशिंग। दोनों तरफ़ अपने ट्री को समानांतर में चलते हैं (ignore क्रेट के समानांतर वॉकर के ज़रिये) और हर फ़ाइल को blake3 से हैश करते हैं। नेस्टेड इग्नोर फ़ाइलें अलग से किसी पूर्व-पास में नहीं, बल्कि इसी वॉक के भीतर खोजी जाती हैं, तो स्टार्टअप में ट्री के दो नहीं, एक ही ट्रैवर्सल का ख़र्च आता है — और वॉचर वॉक शुरू होने से पहले चालू हो जाता है, इसलिए स्कैन के दौरान आप जो सेव करते हैं वह खोता नहीं, क़तार में लग जाता है। हैश आपके प्लेटफ़ॉर्म की कैश डायरेक्टरी (~/.cache/synx/ Linux पर, ~/Library/Caches/synx/ macOS पर) में एक परसिस्टेंट कैश में जाते हैं, जिसका इंडेक्स (path, size, mtime) पर होता है। एक बिना-बदले रेपो पर Synx को फिर से चलाना री-हैशिंग को पूरी तरह छोड़ देता है — एक लाख फ़ाइलों के ट्री की दूसरी सिंक केवल डायरेक्टरी वॉक से बँधी होती है, जो क़रीब एक सेकंड है। कैश की रीड इम्यूटेबल हैं और वॉकर के नतीजे हर वर्कर के हिसाब से बैच होते हैं, तो हॉट लूप में कोई ग्लोबल लॉक नहीं है, और जो कैश बदला ही नहीं वह डिस्क पर दोबारा लिखा ही नहीं जाता।
बड़ी फ़ाइलों के लिए डेल्टा ट्रांसफ़र। छोटी या पूरी तरह बदली फ़ाइलें तार पर पूरी जाती हैं। पर 256 KiB से 256 MiB के बीच की उन फ़ाइलों के लिए जिनका कोई अलग संस्करण पीयर के पास पहले से है, Synx एक rsync-शैली का डेल्टा करता है। यह fast_rsync इस्तेमाल करता है, जो librsync का SIMD-त्वरित पोर्ट है: प्राप्त करने वाली तरफ़ अपनी पहले से मौजूद फ़ाइल का एक सिग्नेचर निकालती है, भेजने वाली तरफ़ उस सिग्नेचर के विरुद्ध diff करती है, और केवल बदले हुए ब्लॉक यात्रा करते हैं। चूँकि librsync की आंतरिक ब्लॉक हैशिंग आधुनिक क्रिप्टोग्राफ़िक हैश से पुरानी है, Synx हर लागू किए गए डेल्टा के नतीजे को कमिट करने से पहले एक ताज़ा blake3 हैश के विरुद्ध सत्यापित करता है। तार को कभी आपसे झूठ बोलने का मौक़ा नहीं मिलता।
कंप्रेशन और चंकिंग। संदेश लंबाई-उपसर्ग वाले postcard हैं (एक कॉम्पैक्ट, सक्रिय रूप से रखरखाव किया जाने वाला बाइनरी फ़ॉर्मैट) और तब zstd-कंप्रेस होते हैं जब कंप्रेशन वाक़ई जगह बचाए। जो फ़ॉर्मैट पहले से कंप्रेस्ड हैं — आर्काइव, मीडिया, पैकेज — वे एक्सटेंशन के आधार पर zstd को छोड़ देते हैं, ताकि यह साबित करने में CPU न जले कि वे और सिकुड़ नहीं सकते। 16 MiB से बड़ी फ़ाइलें 4 MiB चंक में एक temp फ़ाइल में स्ट्रीम होती हैं, फिर अपने मूल मोड और mtime को बनाए रखते हुए जगह पर एटॉमिकली रीनेम होती हैं — ताकि ट्रांसफ़र के बीच कोई क्रैश असली पाथ पर कभी आधी-लिखी फ़ाइल न छोड़े।
स्टेट-आधारित इको दमन। किसी भी दोतरफ़ा सिंक का यह नाज़ुक हिस्सा है। जब Synx कोई आने वाला बदलाव लागू करता है, तो आपका लोकल वॉचर उसी पाथ के लिए चलने ही वाला होता है — और अगर आप उसे भोलेपन से वापस भेज दें, तो आपके पास एक अनंत इको है। आलसी हल एक समय-खिड़की है ("अगले N मिलीसेकंड के इवेंट नज़रअंदाज़ करो"), जो उस खिड़की के दौरान उपयोगकर्ता के किए वैध एडिट भी गिरा देती है। Synx इसके बजाय डिस्क पर नतीजे की स्थिति रिकॉर्ड करता है (mtime, या "deleted") और, जब वॉचर चलता है, मौजूदा फ़ाइल की तुलना उससे करता है जो उसने रिकॉर्ड किया था। केवल मेल को इको मानकर गिराया जाता है। अगर आपने इस बीच फ़ाइल एडिट की हो, तो इवेंट सामान्य रूप से बहता है। कोई अंधी खिड़की नहीं जिसमें आपके कीस्ट्रोक निगल लिए जाएँ।
कनेक्शन पुनः-उपयोग। SSH ControlMaster auto और एक छोटी पर्सिस्ट अवधि के साथ चलता है, ताकि एक ही होस्ट के विरुद्ध कई Synx इन्वोकेशन फिर से नेगोशिएट करने के बजाय एक ही TCP कनेक्शन साझा करें।
पाँच मिनट में आज़माएँ
आपको Synx दोनों छोरों पर चाहिए — आपकी मशीन और रिमोट। एक-लाइन वाला इंस्टॉलर हर एक पर सबसे तेज़ रास्ता है:
# Linux और macOS, x86_64 + ARM64
curl -fsSL https://raw.githubusercontent.com/Muvon/synx/master/install.sh | sh
# या crates.io से
cargo install synx
अगर आपके पास पहले से एक लोकल release बिल्ड है, तो बस उसे कॉपी कर दें:
scp target/release/synx user@host:~/.local/bin/synx
ssh user@host 'chmod +x ~/.local/bin/synx'
फिर एक सेशन शुरू करें:
# दोतरफ़ा सिंक, लाइव
synx ./project dev@host:/srv/project
# प्लान देखें, कुछ न बदलें
synx ./project dev@host:/srv/project --dry-run
# केवल शुरुआती सिंक, फिर बाहर
synx ./project dev@host:/srv/project --once
कुछ फ़्लैग जिन तक आप पहुँचेंगे:
# ग़ैर-मानक SSH पोर्ट (या कोई भी अतिरिक्त ssh आर्ग)
synx ./code host:/work --ssh-opts "-p 2222 -i ~/.ssh/devkey"
# रिमोट पर synx PATH में नहीं है
synx ./code host:/work --remote-synx ~/.local/bin/synx
# कंप्रेशन छोड़ें (तेज़ LAN पर असम्पीड़नीय blobs के साथ तेज़)
synx ./code host:/work --no-compress
# ज़्यादा लॉग
synx ./code host:/work -v # debug
synx ./code host:/work -vv # trace
अगर रिमोट बाइनरी नहीं ढूँढ पाता, तो आपको लॉगिन शेल से synx: command not found मिलेगा — यह --remote-synx वाला मामला है, बग नहीं। और दोनों छोरों को एक ही प्रोटोकॉल वर्शन चलाना चाहिए; 0.1.x प्रोटोकॉल v1 बोलता है और आगे आने वाली किसी भी चीज़ के साथ वायर-स्तर पर असंगत है, तो दोनों तरफ़ साथ में अपग्रेड करें।
यह अभी क्या नहीं करता
Synx 0.1.2 है। मैं आपको उसके किनारे बता देना पसंद करूँगा बजाय इसके कि आप उन्हें ख़ुद ठोकर खाकर पाएँ।
- कोई डेमन मोड नहीं। यह फ़ोरग्राउंड में चलता है।
&से बैकग्राउंड में डालें, याtmux/screenमें रहें। एक ढंग काsynx status/synx stopसूची में है। - कंटेंट का कोई तीन-तरफ़ा मर्ज नहीं। डिलीशन baseline के सहारे चलते हैं, पर फ़ाइल के कंटेंट के कॉन्फ़्लिक्ट नए mtime पर तय होते हैं, पूर्वज-सजग नहीं। ठीक घड़ियाँ ज़रूरी।
- हैश कैश की कुंजी
(size, mtime)है। जो फ़ाइल उसी जगह उसी आकार और उसी टाइमस्टैम्प के साथ दोबारा लिखी जाए, वह री-हैश नहीं होगी। यह वही ह्यूरिस्टिक है जो git इस्तेमाल करता है, और व्यवहार में सही है। - नए इग्नोर नियमों के लिए रीस्टार्ट। सेशन के बीच में
.gitignoreबदलें और Synx तब तक नहीं समझेगा जब तक आप उसे रीस्टार्ट न करें। - केवल macOS और Linux। Windows समर्थित नहीं है।
इनमें से कोई भी मुख्य उपयोग के लिए कठिन बाधा नहीं है — यहाँ एडिट करो, वहाँ चलाओ — जिसके लिए ही मैंने इसे बनाया था और जो यह आज अच्छी तरह करता है।
ओपन सोर्स
Synx GitHub पर Apache-2.0 के तहत है। यह Rust है क्योंकि फ़ाइल सिंक को कंपाइल-टाइम शुद्धता, पूर्वानुमेय लेटेंसी, और किसी ट्रांसफ़र के बीच में कोई GC ठहराव नहीं चाहिए — और क्योंकि एक अकेली स्टैटिक बाइनरी जिसे आप किसी सर्वर पर scp कर सकें, ऐसे टूल के लिए सही आकार है।
इसे वही टीम बनाती है जो हमारे बाक़ी डेवलपर टूल के पीछे है; अगर आप उत्सुक हैं कि हम एक बड़े प्लैटफ़ॉर्म के बजाय छोटी, धारदार, ओपन-सोर्स उपयोगिताएँ क्यों शिप करते रहते हैं, तो वह एक लंबी बातचीत है। Synx उनमें सबसे नई है, और वह जिसे मैं ख़ुद सबसे ज़्यादा इस्तेमाल करता हूँ।
अगर यह आपके काम की है, तो कोड वहीं है। अगर यह टूटे, तो इशू ट्रैकर भी वहीं है — असली रिमोट-डेव सेटअप से आने वाली बग रिपोर्ट 0.2 को 0.1 से बेहतर बनाने का सबसे तेज़ तरीक़ा हैं।
— Vladimir
Synx Apache-2.0 के तहत ओपन सोर्स है। इसे लें, सोर्स पढ़ें, या github.com/Muvon/synx पर एक इशू दर्ज करें।



