CI में अपनी build को obfuscate करें

आपकी pipeline में AfterPack कहाँ बैठता है: केवल production, एक बार build करें और हर जगह promote करें, एक CI recipe के साथ, और seeds, artifacts, source maps और बंद cloud के नियम।

AfterPack एक post-build step है: आपका bundler JavaScript बनाता है, फिर AfterPack उसे obfuscate करता है। हमारी सलाह है कि production build में एक बार obfuscate करें, और उस artifact को बिना बदलाव हर stage से promote करें। नीचे की एक बात सलाह नहीं है: deploy किया गया source map आपका मूल source वापस दे देता है, इसलिए maps को deploy से बाहर रखें।

Dev obfuscate नहीं होता

Obfuscation built output पर चलती है, इसलिए dev server के सामने उसके करने को कुछ नहीं है। Vite dev, next dev और HMR बिना build या आंशिक रूप से build किया हुआ code serve करते हैं।

  • Development. आपका bundler हमेशा की तरह चलता है। AfterPack निष्क्रिय रहता है। Stack traces आपके असली source की ओर इशारा करते हैं, HMR तुरंत होता है, debugging सामान्य है।
  • Production build. Bundle बंद होने के बाद AfterPack चलता है। एक framework plugin इसे अपने-आप चलाता है। केवल bundler वाले stack पर आप इसे एक बार invoke करते हैं।

Obfuscation के बिना production build करने के लिए, उदाहरण के लिए एक बार की debug build, अपने config में { build: { autorun: false } } या environment में AFTERPACK_build_autorun=false सेट करें।

एक बार build करें, artifact को promote करें

Obfuscated bundle ही वह artifact है जिसे आप ship करते हैं, इसलिए सबसे साफ़ pipeline उसे एक बार build करती है और वही bytes हर stage से आगे ले जाती है।

Build once, promote the identical obfuscated bundle across every gateProd buildbundler outputAfterPackrandom seedOne frozen artifactfrozen the moment it's builtsame bytesDevsandboxQAintegrationPreprodstagingProdlivepromote the identical obfuscated bundle at every gate

हर environment के लिए AfterPack दोबारा चलाना विकल्प है, और इसकी कीमत आपका test signal है: हर hop एक अलग आकार का bundle बनाता है, इसलिए QA ने जो परखा वह वह नहीं है जो production serve करता है। एक ही artifact promote करने से QA में चलाए गए integration tests ठीक उन्हीं bytes पर चलते रहते हैं जो production तक पहुँचते हैं। आपका dev loop दोनों ही तरह un-obfuscated रहता है।

Command surface

$ npx afterpack@latest dist/ --preset=hard

Emitted JavaScript का path, साथ में हर config key एक flag के रूप में। CI में build directory का नाम स्पष्ट रूप से देना फ़ायदेमंद है, जहाँ auto-detect की गई directory चुपचाप गलत tree को obfuscate करने का रास्ता है। npx afterpack@latest verify [dir] किसी build को उसकी लिखी protection receipt के विरुद्ध दोबारा जाँचता है, और deploy से पहले यही step चलाना है। पूरी surface CLI reference में है।

डिफ़ॉल्ट रूप से random seed, reproducibility के लिए pin करें

Seed हर release को पिछली से संरचना में अलग बनाता है, इसलिए पिछले हफ़्ते के bundle के लिए बना tool या patch इस पर टूट जाता है। हर release के लिए नया random seed default है। इसे पाने के लिए आपको कुछ set नहीं करना पड़ता।

Seed कोई secret नहीं है। इसे किसी भी दूसरे build parameter की तरह, एक CI variable में store करें।

इसे केवल तब pin करें जब आपको reproducible output चाहिए: build-cache hits, golden-snapshot tests, या auditable byte-for-byte rebuilds।

$ npx afterpack@latest dist/ --seed=git

--seed=git, git rev-parse HEAD पर resolve होता है। Repository के बाहर यह build को fail करने के बजाय एक नए random seed पर लौट आता है और एक notice print करता है। कोई भी दूसरी string engine द्वारा hash की जाती है; एक integer जैसा है वैसा इस्तेमाल होता है। Pinned seed हर बार एक जैसा output बनाता है।

ऐसी value पर pin करें जो हर release में फिर भी बदले

जो seed releases के बीच स्थिर रहता है, वह attacker के विश्लेषण को एक release से अगली तक ले जाता है, और यही वह गुण है जिसके लिए आपने AfterPack install किया। जब pinned seed सचमुच ज़रूरी हो, तो उसे ऐसी value पर pin करें जो हर release में बदलती है, जैसे commit SHA।

File names और caching

Framework plugin हर obfuscated फ़ाइल को एक content-hashed नाम देता है जो उसके obfuscated bytes के साथ चलता है, इसलिए एक नाम कभी दो अलग फ़ाइलें serve नहीं करता और browsers, CDNs और service workers को दोबारा इस्तेमाल हुए नाम के तहत कभी पुरानी फ़ाइल नहीं मिलती। नाम फ़ाइल के unobfuscated content के साथ seed, engine version, आपकी settings, आपके directives और build mode से निकाला जाता है।

इसलिए caching seed का फ़ैसला है। जो release नया seed लेता है, default रूप से या --seed=git के साथ, वह हर obfuscated फ़ाइल का नाम बदल देता है, इसलिए browsers और service workers हर फ़ाइल फिर से download करते हैं। स्थिर seed, जब तक engine version और settings वही रहें, किसी बिना बदली फ़ाइल के bytes और नाम को releases के बीच वैसा ही रखता है, इसलिए long-lived caches बने रहते हैं, उस क़ीमत पर जो ऊपर की चेतावनी बताती है।

key के साथ, cloud engine का version नाम का हिस्सा होता है। जब build API से वह version नहीं पढ़ पाता, तो वह चेतावनी देता है और उस build को random नाम देता है, जो सुरक्षित हैं पर pinned seed से दोबारा नहीं बनते।

CLI Next.js, Angular, Vite और webpack builds पर फ़ाइलों के नाम इसी तरह रखता है। बाकी किसी भी output पर वह वही नाम रखता है जो आपके bundler ने लिखे और एक notice print करता है; अगर वहाँ आप long-lived caching पर निर्भर हैं, तो अपने framework का plugin इस्तेमाल करें, या एक स्थिर seed pin करें।

Schedule पर rotate करें

हर release के लिए random seeding पहले से ही हर ship पर artifact को बदल देती है। जो teams कम ship करती हैं, वे अक्सर इसके ऊपर एक scheduled rebuild जोड़ती हैं, उसी source से एक nightly या weekly run, ताकि deployed bundle हर cycle में आकार बदले, भले ही code न बदले।

जिस attacker ने build N को reverse करना शुरू किया, वह build N+1 पर शून्य पर लौट आता है: दोनों builds के बीच कोई साझा संरचना नहीं है जिससे काम आगे ले जाया जा सके। यह anti-bot, anti-cheat और CAPTCHA-resistant logic के लिए सबसे ज़्यादा मायने रखता है, जहाँ adversary के पास समय होता है। Rotation अपने-आप cheating या bot traffic को नहीं रोकता; client-side logic के पीछे फिर भी server-authoritative validation चाहिए।

CI में

Pipeline है: build, फिर obfuscate, फिर artifacts को deploy से बाहर रखना। Framework plugin के साथ obfuscate step आपकी सामान्य build का हिस्सा है और आप AfterPack का अलग call हटा सकते हैं। केवल bundler वाले stack पर इसे ख़ुद चलाएँ।

# .github/workflows/release.yml (excerpt)
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # 1. Build. With @afterpack/<fw> installed, obfuscation runs here automatically.
      - run: npm ci
      - run: npm run build

      # 2. Pure-bundler stacks only (no framework plugin): obfuscate explicitly.
      #    Pin --seed for Turborepo/Nx remote-cache hits; omit it for a fresh
      #    random seed per release. --diagnostics.level=all prints every diagnostic
      #    instead of rolling the info ones up to one line.
      - run: npx afterpack@latest dist/ --preset=hard --seed=git --diagnostics.level=all --protectionMap.enabled

      # 3. Keep the Protection Map as a reviewable CI artifact. It is off under
      #    CI=true unless asked for: --protectionMap.enabled above, or
      #    protectionMap: { enabled: true } in your plugin options. It is written to
      #    the gitignored .afterpack/ at the PROJECT ROOT, never into the deploy
      #    tree. It embeds your ORIGINAL SOURCE — treat the upload as
      #    source-confidential.
      - uses: actions/upload-artifact@v4
        with:
          name: protection-map
          path: .afterpack/

      # 4. Deploy only the bundle. Never the .afterpack/ directory, never a .map.
      - run: ./deploy.sh dist/

Turborepo और Nx अपने remote cache की key output hashes पर रखते हैं। Random seed हर run में अलग bundle बनाता है, इसलिए वे caches हमेशा miss होते हैं। Cache hits के लिए --seed=git दें: SHA फिर भी हर release में बदलता है, इसलिए builds एक commit के भीतर reproducible रहती हैं और releases के बीच बदलती हैं।

Protection Map को PR से जोड़ें ताकि reviewer पुष्टि कर सके कि किन regions का protection level बदला। यह उस refactor को पकड़ने का सबसे तेज़ तरीका है जिसने गलती से किसी secret को चिह्नित region से बाहर कर दिया। इसे एक reviewer artifact मानें।

`afterpack audit ./dist` जैसा कुछ नहीं है

Security scanner को चलाने वाला audit एक URL पर चलता है और text print करता है; यह local directory नहीं लेता। Deploy step के बाद इसे अपने deployed staging URL पर लगाएँ। देखें Audit।

Artifacts, और उन्हें deploy से बाहर रखना

AfterPack तीन तरह के local artifacts लिखता है। हर एक उस bundle से ज़्यादा संवेदनशील है जिसका वह वर्णन करता है, और तीनों डिफ़ॉल्ट रूप से सुरक्षित पक्ष पर रहते हैं।

Artifactडिफ़ॉल्टकहाँ जाता हैFlag
Protection Map (protectionMap.html)चालू जब bundler ने खोजा जा सकने वाला source map emit किया हो और build CI में न चल रही हो, वरना opt-inFree build पर, हमेशा project root पर gitignored .afterpack/ directory, served build के बगल में कभी नहीं। Pro cloud build कोई local file नहीं लिखती: उसका map dashboard में store होता है, और build पूरी होने पर उसका link print करती है--protectionMap.enabled=false
आपकी मूल फ़ाइलों का backupCLI के लिए चालू, framework plugin के लिए बंदCLI के लिए project root पर .afterpack/backups/<build dir>/; plugin से माँगे जाने पर output के बगल में एक .backup.<hash> siblingOpt out करने के लिए --build.backup=false
.map source-map siblingProduction में बंद, dev में केवल तब emit होता है जब कोई input map मिला होEmitted JS के बगल में--sourceMap.enabled=false

हर integration पर दो चीज़ें अपने-आप होती हैं:

  • .afterpack/ खुद को ignore करता है। AfterPack .afterpack/ के अंदर एक .gitignore लिखता है और आपके .gitignore में कभी बदलाव नहीं करता। आपकी build के बगल में लिखी गई .map या .backup.<hash> फ़ाइल ignore नहीं होती: उसे git और deploy दोनों से खुद बाहर रखें।
  • Served path पर artifact चेतावनी देता है। अगर कोई artifact served directory में पहुँचता है तो AfterPack चेतावनी देता है। Served source map पूरा deobfuscation है।

Backup आपका मूल source ज्यों-का-त्यों है। CLI उसे project root पर, gitignored .afterpack/backups/ के नीचे रखता है, एक manifest के साथ जो दर्ज करता है कि हर फ़ाइल किसमें obfuscate हुई; npx afterpack@latest restore किसी run के ऊपर मूल फ़ाइलें वापस रख देता है। build.backup माँगा गया plugin अपनी copy इसके बजाय output के बगल में लिखता है, जहाँ deploy उसे उठा लेगा।

Leak हुआ source map obfuscation को बेकार कर देता है

Source maps obfuscated output को सीधे आपके मूल source से जोड़ते हैं, और sourcesContent में source ख़ुद होता है। इसे कभी deploy न करें।

Production build आपके bundler के लिखे source maps भी हटा देता है, क्योंकि उनमें आपका मूल source होता है: .map files, hidden और inline maps, //# sourceMappingURL= comments और CSS maps। CLI और हर plugin यह करते हैं। sourceMap.enabled चालू होने पर AfterPack का map bundler के JavaScript map की जगह ले लेता है और CSS maps बने रहते हैं। जो map हटाया न जा सके, वह build को fail कर देता है। node_modules और build directory के अंदर का कोई भी folder जिसका अपना package.json हो, अछूते रहते हैं।

जिस फ़ाइल को engine obfuscate नहीं कर सकता, वह build को fail कर देती है

Build रुक जाती है और फ़ाइल का नाम बताती है। जिस फ़ाइल को बिना छुए ship होना है, उसे अलग करने के लिए paths.exclude इस्तेमाल करें; codes और हर एक का मतलब Diagnostics में है।

CI gate

CLI का कोई भी nonzero exit मतलब है कि build ने वह ship नहीं किया जो आपने configure किया था, इसलिए gate if ! npx afterpack@latest dist/ ...; then fail; fi है, किसी विशेष code पर branch नहीं।

इसमें usage error (64), size cap (3), update माँगता engine (6), और 1 के रूप में खाली build directory, engine error, या एक Pro build शामिल है जिसकी key AfterPack cloud तक नहीं पहुँच सकी। वह आख़िरी मामला कमज़ोर output पर लौटने के बजाय build को fail कर देता है, इसलिए green run का मतलब है कि जो protection आपने माँगी वही ship हुई। Free builds पूरी तरह local हैं और कभी cloud को call नहीं करतीं। पूरा contract और हर code जो एक build emit कर सकती है, Diagnostics में है।

Ship होने के बाद builds पर नज़र रखना

हर Pro Cloud build dashboard में दर्ज होती है: वह कब चली, किस commit से आई, उसने कितनी फ़ाइलें process कीं, MB और build time में उसकी क्या लागत रही, और उसका coverage व inflation। इससे अपनी rotation की लय पर नज़र रखें और समय के साथ builds की तुलना करें। देखें Dashboard में builds।

Free local builds कभी AfterPack तक नहीं पहुँचतीं, इसलिए वे वहाँ कभी नहीं दिखतीं।

आगे

  • Best practices: क्या चिह्नित करें, क्या अछूता छोड़ें, और CI को ईमानदार कैसे रखें।
  • Dashboard में builds: इस पेज से बनी हर चीज़ का per-build record।
  • Threat model: हर release में आकार बदलने वाली build प्राथमिक बचाव क्यों है।
  • CLI reference: ऊपर के synopsis के पीछे की पूरी flag table।
  • Diagnostics: exit contract और fail-closed नियम, पूरे।
  • Protection Map: उस artifact को पढ़ना जिसे आपके CI job ने upload किया।