# AI agent package publishing में release-token sprawl से बचें

AI coding agent को dependencies इंस्टॉल करने में सक्षम होना चाहिए, लेकिन उसके पास पैकेज publish, unpublish, delete या retag करने का अधिकार नहीं होना चाहिए। कमांड लाइन में ये कार्रवाइयाँ पास-पास दिखती हैं, पर इनके परिणाम बिल्कुल अलग होते हैं। इन्हें एक ही permission मानने से रोज़मर्रा का development work release authority में बदल जाता है।

मैंने देखा है कि टीम किसी एजेंट से एक मामूली install command चलवाना चाहती थी और इसके लिए release credentials बाँट दिए। फिर token shell environment, configuration file या command output में पड़ा रहा, जहाँ उस run का कोई भी tool उसे दोबारा इस्तेमाल कर सकता था। बाद की समस्या आमतौर पर किसी बड़े compromise से शुरू नहीं होती। अक्सर एजेंट गलत registry पर `npm publish` चला देता है, किसी के tarball देखने से पहले `latest` बदल देता है या test cleanup के अनुरोध को बहुत शाब्दिक ढंग से समझकर unpublish करने की कोशिश करता है।

## Registry read से release authority नहीं मिलती

Package installation और package publication एक ही तरह के registry endpoint family का इस्तेमाल कर सकते हैं, लेकिन दोनों अलग trust questions का उत्तर देते हैं। Download पूछता है, «क्या यह process कोई artifact ला सकता है?» Publish पूछता है, «क्या यह process इस नाम के तहत कोई public या organization-visible release बना सकता है?» Delete या unpublish पूछता है, «क्या यह process किसी ऐसे release का इतिहास और उपलब्धता बदल सकता है जिसे दूसरे builds पहले से इस्तेमाल कर सकते हैं?»

Registry client इन कामों को समान दिखाए, इसलिए इन सवालों को एक ही credential में न समेटें। Package manager `npm ci`, `npm publish`, `npm dist-tag add` और `npm unpublish` के लिए उसी `.npmrc` file से configuration पढ़ सकता है। File layout client की सुविधा है, permission design नहीं।

एजेंट के लिए registry actions को कम से कम इन समूहों में बाँटें:

- Read: metadata lookup, tarball download, integrity verification और dependency installation।
- Publish: approved package name के तहत नया immutable version बनाना।
- Route: `latest`, prerelease tags या registry access settings जैसे mutable channels में बदलाव।
- Destructive: unpublish, जहाँ registry अनुमति दे वहाँ package deletion और release tag हटाना।

Publish और route का अंतर कई release scripts की तुलना में अधिक महत्वपूर्ण है। Versioned package ठीक हो सकता है, लेकिन उसे `latest` देने से नए consumers उसी तक पहुँचेंगे। दूसरी ओर, dedicated tag के तहत प्रकाशित prerelease कम जोखिम वाला हो सकता है, यदि उपयोगकर्ताओं को उस tag को स्वयं चुनना पड़े। Package bytes और consumers के वहाँ तक पहुँचने का रास्ता अलग controls हैं।

Deletion को अलग category दें, भले ही registry उसे सीमित करती हो। Registry rules अलग-अलग होते हैं और कुछ जगह «delete» कहे जाने वाले operations केवल artifact को छिपाते या unavailable mark करते हैं। इससे कार्रवाई harmless नहीं हो जाती। इससे reproducible installs, incident investigations और हटाए गए version को pin करने वाली teams टूट सकती हैं। एजेंट को यह कभी नहीं मानना चाहिए कि cleanup सुरक्षित है सिर्फ इसलिए कि version कुछ मिनट पहले ही publish हुआ था।

## Broad publish tokens सामान्य परिस्थितियों में भी विफल होते हैं

Agent environment में registry token सुविधाजनक file format में दिया गया authorization grant है। यह कोई सीमित instruction नहीं है। अगर एजेंट environment या config file पढ़ सकता है, तो dependency, issue template, build log या copied terminal text में मौजूद prompt injection के पास credential तक पहुँचने का रास्ता है।

आम सलाह है कि registry जितना संकीर्ण scope दे, उतने संकीर्ण scope वाला token इस्तेमाल करें। यह सही है, लेकिन अधूरा है। केवल publishing तक सीमित token भी unwanted version publish कर सकता है, ऐसे package में publish कर सकता है जहाँ उसके account को access है, या altered configuration से चुने गए registry endpoint पर काम कर सकता है। Scope blast radius घटाता है। यह हर release के लिए intent तय नहीं करता।

जिस तरह की समस्या मुझे एक से अधिक बार ठीक करनी पड़ी है, वह कुछ ऐसी होती है:

1. टीम एजेंट को registry token देती है ताकि वह release checks चला सके।
2. Project config production registry की ओर इशारा करता है, क्योंकि developer machines भी वही इस्तेमाल करती हैं।
3. एजेंट को package contents जाँचने होते हैं और वह `npm pack` चलाता है, जो harmless है।
4. अगला instruction «test publishing» कहता है और एजेंट isolated destination पर publish करने के बजाय `npm publish` चला देता है।
5. Command सफल हो जाता है क्योंकि token और package name valid हैं। टीम को इसका पता तब चलता है जब automation या users नया version देख लेते हैं।

इस chain में किसी hostile actor की ज़रूरत नहीं है। Design ने planning system को credential दिया और उससे ऐसी boundary बचाए रखने की उम्मीद की जिसे operating environment ने लागू ही नहीं किया था।

Credential को agent process से बाहर रखें। एक अलग action gateway उसे अपने पास रखे और requested destination जाँचने तथा ज़रूरी human approval लेने के बाद कोई specific HTTP request या command execute करे। Sallyport इसी pattern का पालन करता है: एजेंट को authorized action का result मिलता है, registry token स्वयं नहीं।

फिर भी action definitions अच्छी होनी चाहिए। जो gateway «registry.example के लिए कोई भी request» approve करता है, उसने broad token problem को केवल एक button के पीछे पहुँचा दिया है। Request में इतना visible detail होना चाहिए कि approver tarball fetch और version publish, तथा package publish और unpublish के बीच अंतर कर सके।

## चलाने से पहले release request को जाँचने योग्य बनाएँ

कोई इंसान «package release कर दो» जैसे अस्पष्ट instruction को approve नहीं कर सकता। Approval surface पर package identifier, exact version, registry host, operation और होने वाला tag change दिखना चाहिए। अगर एजेंट ये fields नहीं दे सकता, तो उसने अभी release request तैयार नहीं की है।

npm-compatible registry के लिए काम को artifact inspection phase और registry mutation phase में अलग करें। Inspection phase publication authority के बिना चल सकता है:

```sh
npm ci
npm test
npm pack --json
```

`npm pack --json` उस tarball की structured जानकारी देता है जिसे यह बनाएगा। उपयोगी हिस्से कुछ ऐसे दिखते हैं:

```json
[
  {
    "id": "@acme/widget@1.4.0",
    "name": "@acme/widget",
    "version": "1.4.0",
    "filename": "acme-widget-1.4.0.tgz",
    "files": [
      {"path": "README.md", "size": 2400},
      {"path": "dist/index.js", "size": 18420},
      {"path": "package.json", "size": 910}
    ]
  }
]
```

केवल command exit code नहीं, file list भी जाँचें। मैं उन source directories को देखता हूँ जिन्हें private रहना चाहिए था, credentials वाले test fixtures को, गायब compiled output directory को और गलत entry point वाले package metadata को। `npm pack` पर इन गलतियों को ठीक करना सस्ता होता है।

इसके बाद registry state अलग से लें। npm CLI documentation में `npm view` को registry से package metadata देखने का तरीका बताया गया है। व्यापक data dump स्वीकार करने के बजाय इसका इस्तेमाल ठोस सवाल पूछने के लिए करें:

```sh
npm view @acme/widget version dist-tags --json
npm view @acme/widget@1.4.0 dist --json
```

पहला command बताता है कि कौन सा version मौजूद है और tags कहाँ point कर रहे हैं। Publication के बाद दूसरा command उपयोगी है, क्योंकि `dist` में registry द्वारा दर्ज tarball address और integrity data होता है। Release workflow को यह output secrets हटाकर अपने release record में रखना चाहिए। इससे पता चलता है कि registry ने वास्तव में क्या स्वीकार किया, न कि local directory क्या भेजना चाहती थी।

इन facts से approval request बनाएँ। अच्छी request कहती है: `@acme/widget@1.4.0` को `registry.example` पर publish करें, tag न बदलें। खराब request कहती है: `npm publish` चलाएँ। पहली request से reviewer namespace error पकड़ सकता है। दूसरी में reviewer को ऐसे command से intent समझना पड़ेगा जिस पर शायद उसे भरोसा न हो।

## Registry path साबित करने के लिए अस्थायी पैकेज इस्तेमाल करें

अस्थायी package उस path को test करने का सबसे सुरक्षित तरीका है जो तैयार tarball को retrievable registry release में बदलता है। इससे यह साबित नहीं होता कि आपका production package तैयार है। इससे यह साबित होता है कि authentication, registry selection, publishing mechanics और clean installation उन controls के तहत साथ काम करते हैं जो वास्तविक release जैसे हैं।

अपने नियंत्रण वाले namespace के तहत package name चुनें और उसे साफ़ तौर पर temporary रखें। किसी popular package name की नकल न करें और ऐसा नाम न लें जो बाद में गलती से real product बन सके। इसमें एक छोटा, inert module रखें। इसका काम publish और install होना है, application behavior दिखाना नहीं।

यह minimal `package.json` test को स्पष्ट रखता है:

```json
{
  "name": "@acme-release-test/relay-check-2025-04",
  "version": "0.0.1",
  "description": "Temporary registry release-path check",
  "main": "index.js",
  "files": ["index.js", "README.md"],
  "publishConfig": {
    "access": "restricted"
  }
}
```

Access settings को अपने वास्तविक package type के अनुसार चुनें। अगर आपका वास्तविक package public है, तो `restricted` को बिना सोचे copy न करें और केवल अधिक वास्तविक लगने के लिए temporary test को public न बनाएं। उद्देश्य वही authorization और intended audience boundary आज़माना है। यदि public publishing ही test करना है, तो स्पष्ट रूप से temporary public name लें और शुरू करने से पहले registry के retention तथा unpublish rules जाँचें।

Cached metadata और मौजूदा workspace के कारण परिणाम बेहतर न दिखे, इसलिए test को fresh directory में चलाएँ:

```sh
mkdir release-path-check
cd release-path-check
npm init -y
npm install @acme-release-test/relay-check-2025-04@0.0.1
node -e "console.log(require('@acme-release-test/relay-check-2025-04'))"
```

सफल `npm install`, सफल publish response से बेहतर सवाल का जवाब देता है: क्या कोई clean consumer नामित version को resolve और retrieve कर सकता है? अगर आपका package exports, type declarations, command-line binary या postinstall script इस्तेमाल करता है, तो इस fresh directory में relevant public entry point भी जाँचें। Registry ने package store कर लिया, इसका अर्थ यह नहीं कि consumer उसका उपयोग कर सकता है।

सिर्फ account साफ़ रखने के लिए test package delete न करें। Registry के सामान्य rules के अनुसार audit trail रखें या जहाँ उचित हो वहाँ deprecation इस्तेमाल करें। Release test को एजेंट और टीम को यह दिखाना चाहिए कि वास्तविक post-publish record कैसा होता है। उसे मिटाने से दोनों यह सीखते हैं कि publication history disposable है।

## Package contents और registry acceptance अलग tests हैं

Teams अक्सर `npm pack` को release test कहती हैं। यह package-content test है। फिर सफल `npm publish` को release test कहा जाता है। यह registry-acceptance test है। दोनों एक-दूसरे की जगह नहीं ले सकते और किसी एक को complete मानने से predictable gaps पैदा होते हैं।

Package-content testing पूछता है कि tarball में इच्छित files और metadata हैं या नहीं। इससे `.npmignore` की गलती, बहुत broad `files` array, missing built artifact और source तथा manifest के बीच version mismatch पकड़े जाते हैं। इस काम का बड़ा हिस्सा network access के बिना हो सकता है।

Registry-acceptance testing पूछता है कि registry credential और namespace को पहचानती है या नहीं, version स्वीकार करती है या नहीं, tarball store करती है या नहीं, integrity information दर्ज करती है या नहीं और उसे consumer के लिए उपलब्ध कराती है या नहीं। इससे गलत registry host, missing organization entitlement, publish configuration error और local development से अलग authorization path सामने आते हैं।

Consumer testing तीसरा सवाल पूछता है: क्या कोई clean project exact release को install करके उसी तरह चला सकता है जैसे users चलाएँगे? यहीं missing peer dependencies, गलत `exports` entries और workspace files पर अनजाने भरोसे जैसी समस्याएँ सामने आती हैं।

इन tests को इसी क्रम में रखें। «हम इसे हमेशा unpublish कर सकते हैं» सोचकर पहले publish करना खराब discipline है। Unpublish rollback button नहीं है। कुछ users, mirrors, caches और build records package को बनाए रख सकते हैं, जबकि दूसरे consumers उसे resolve नहीं कर पाएँगे। गलत version को operationally recover किया जा सकता है, लेकिन इससे वह काम और भ्रम पैदा होता है जिसे local tarball inspection पहले ही रोक सकती थी।

## Approval को call के परिणाम के अनुसार रखें

कई expected reads करने वाले run के लिए session-level approval उपयोगी है। हर metadata lookup पर किसी से मंज़ूरी माँगना लोगों को बिना पढ़े click करना सिखाता है। यह approval fatigue है और इससे meaningful prompt पर ध्यान मिलने की संभावना घटती है।

लेकिन publication और deletion को इस flow को रोकना चाहिए। दोनों external state को ऐसे बदलते हैं जैसे routine dependency download नहीं बदलता। नए package version के लिए अलग confirmation, dist-tag change के लिए दूसरी और हर destructive action के लिए अलग confirmation लें। अगर एजेंट दो package publish का प्रस्ताव देता है, तो दो requests दिखाएँ। Batch approval उस exact version को छिपा देता है जिसे reviewer को देखना चाहिए।

Per-call prompt में action को सही कारण से reject करने के लिए पर्याप्त जानकारी होनी चाहिए:

- Registry host और package scope या owner।
- Operation: publish, tag move, deprecate, unpublish या delete।
- Exact version और requested tag transition।
- वह agent process जिसने request किया, ताकि reviewer अनपेक्षित caller को reject कर सके।
- Release plan से लिया गया छोटा reason, free-form tool output नहीं।

किसी इंसान से उसी क्षण authorization header parse करने या opaque hashes मिलाने की अपेक्षा न करें। ये audit details हैं। Live decision में action का परिणाम सरल भाषा में दिखना चाहिए, जबकि system को बाद की समीक्षा के लिए underlying request सुरक्षित रखनी चाहिए।

Sallyport का per-call key control publish boundary के लिए उपयोगी है: registry credential के हर इस्तेमाल पर approval ज़रूरी हो सकती है, जबकि एजेंट अलग session decision के तहत अपना सामान्य काम करता रहे। इससे package contents की समीक्षा की ज़रूरत खत्म नहीं होती। यह release credential को एक पुराने click के बाद background authority बनने से रोकता है।

## Tags को consumer-routing change मानें

Dist-tag आपके users के लिए सही version को भी unsafe बना सकता है। npm-compatible registries में बिना explicit version वाला install आमतौर पर `latest` tag का पालन करता है। इस tag को बदलने से नए installs को मिलने वाला package बदल जाता है, भले ही पहले publish किया गया tarball वही रहे।

Publishing और tag movement को अलग requests रखें। पहले candidate version publish करें, फिर clean test project में exact version से उसे retrieve करें। इस check के बाद ही तय करें कि `latest` बदलना है या नहीं। इससे एक उपयोगी विराम मिलता है: bytes immutable version के तहत दिखाई दे रहे हैं और routing decision अभी नहीं हुआ है।

Commands यह अंतर साफ़ करते हैं:

```sh
npm publish --tag candidate
npm view @acme/widget dist-tags --json
npm dist-tag add @acme/widget@1.4.0 latest
```

पहला command version बनाकर candidate route देता है। आखिरी command वह route बदलता है जिसे बहुत से users follow करते हैं। अगर release script दोनों को एक helper function में छिपा दे, तो human judgment का सबसे उपयोगी बिंदु हट जाता है।

Agent को अनुमान लगाकर tag mistake सुधारने न दें। अगर `latest` गलत version की ओर point कर रहा है, तो एजेंट को current tag map, intended version और proposed correction बतानी चाहिए। Reviewer को इसकी पुष्टि करनी चाहिए। यहाँ extra click की लागत बहुत कम है, जबकि हर नए install को गलत package देने की लागत बहुत बड़ी हो सकती है।

## Incident के बाद भी audit records उपयोगी रखें

केवल command history यह नहीं बताती कि registry mutation को किसने authorize किया, किस agent run ने उसे जारी किया या बाद में किसी ने log बदला। Release operations को ऐसा record चाहिए जो request, approval, execution result और लौटाए गए registry metadata को जोड़ सके।

Package name, version, registry host, operation type, final status और action से पहले बने artifact inspection का reference दर्ज करें। Tokens, authorization headers या ऐसे raw configuration files दर्ज न करें जिनमें credentials हो सकते हैं। अच्छे audit record से maintainer महीनों बाद भी एक व्यावहारिक सवाल का जवाब दे सकता है: क्या हमने यह version publish किया, tag बदला या केवल कोशिश की थी?

Tamper evidence महत्वपूर्ण है, क्योंकि release logs अक्सर कुछ गलत होने के बाद ही evidence बनते हैं। Sallyport session events और individual calls को encrypted hash-chained audit log से बनाए गए अलग journals में रखता है, और `sp audit verify` vault key के बिना offline इस chain की जाँच कर सकता है। यह repository की mutable text file पर भरोसा करने से अधिक मजबूत है।

Logs किसी dangerous action को safe नहीं बनाते। वे यह पता लगाना संभव बनाते हैं कि approval prompt गलत समझा गया, registry destination गलत था या release process ने ऐसा कुछ किया जिसकी किसी को अपेक्षा नहीं थी। Log के साथ running agent session को तुरंत revoke करने का तरीका भी रखें। जब release अजीब व्यवहार करने लगे, तो अगली call रोकना बाद में perfect postmortem लिखने से अधिक उपयोगी है।

## Disposable test को release contract का हिस्सा बनाएँ

Disposable publish test planned release-path check होना चाहिए, production release विफल होने के बाद किया गया improvisation नहीं। तय करें कि यह कब चलेगा: registry integration बदलने पर, credential handling बदलने पर, नया package manager configuration अपनाने पर या किसी नए agent workflow को publish access देने से पहले।

जहाँ संभव हो, इसकी authority production authority से संकीर्ण रखें, लेकिन इसे इतना artificial न बनाएँ कि वास्तविक failure mode छूट जाए। वही registry class, वही request broker, वही credential storage pattern और वही clean-install verification test करें। अगर production publishing के लिए human approval ज़रूरी है, तो test में भी वही होना चाहिए। वरना आपने अलग system test किया है।

पहला उपयोगी कदम agent-visible environment variables से registry write credentials हटाना है। इसके बाद एक disposable package को उसी controlled path से गुजारें जिस पर आप भरोसा करना चाहते हैं। Approval से पहले package file list पढ़ें। Publication के बाद exact version जाँचें। Deletion को अलग से approved action रखें, क्योंकि साफ़ test account release boundary में accidental hole के लायक नहीं है।
