# Secret-free agent tools: मजबूत action contracts

एजेंट को किसी व्यक्ति या service account का रूप धारण करने के साधन नहीं मिलने चाहिए। उसे केवल कार्रवाई मांगनी चाहिए। यह बात साफ लगती है, लेकिन सामान्य agent tool देखने पर तस्वीर बदल जाती है। `http_request` function URL, headers, method और body स्वीकार करता है, जबकि एजेंट को environment variable से bearer token मिलता है। Tool call व्यवस्थित दिखती है, लेकिन authority prompt text, process memory, logs, shell history और अगले subprocess में बिखर जाती है।

Secret-free design intent और execution के बीच मजबूत सीमा बनाता है। एजेंट कहता है, «इस service को इस environment में deploy करें।» Credential रखने वाली layer तय करती है कि कार्रवाई चल सकती है या नहीं, सही identity चुनती है, authenticated call करती है और result लौटाती है। इससे audit, approval और revocation का तरीका बदल जाता है। साथ ही आपको ऐसे interfaces बनाने पड़ते हैं जिन्हें autonomy देना उचित हो।

## Contract को transport नहीं, intent बताना चाहिए

Action contract उस operation का नाम लेता है जिसे user पहचान सके और inputs को केवल उस operation के लिए जरूरी तथ्यों तक सीमित रखता है। Transport details सीमा के पीछे रहने चाहिए। यह अंतर आसानी से छूट जाता है, क्योंकि HTTP हर action को method, URL, headers और JSON body जैसा दिखाता है।

Change request खोलने वाले दो tool interfaces देखें। पहला autonomous process के लिए आम, लेकिन असुरक्षित है:

```json
{
  "name": "http_request",
  "input": {
    "method": "POST",
    "url": "https://code.example/api/projects/alpha/changes",
    "headers": {
      "Authorization": "Bearer ${TOKEN}",
      "Content-Type": "application/json"
    },
    "body": {
      "title": "Fix timeout",
      "branch": "agent/fix-timeout"
    }
  }
}
```

यह interface एजेंट को destination, authentication method और request shape पर authority देता है। Literal token हटाने से समस्या हल नहीं होती, अगर एजेंट header alias, credential identifier, proxy URL या ऐसा shell command चुन सकता है जो token कहीं और से पढ़े। आपने secret को केवल दूसरी जगह रखा है, authority कम नहीं की।

Contract-oriented interface कुछ ऐसी होगी:

```json
{
  "name": "create_change_request",
  "input": {
    "project": "alpha",
    "source_branch": "agent/fix-timeout",
    "title": "Fix timeout in retry path",
    "description": "Adds a bounded retry and a regression test."
  }
}
```

Execution layer `project` को known endpoint और approved account से map करती है। Authentication header वही खुद जोड़ती है। वह branch name अस्वीकार कर सकती है, target repository जांच सकती है, approval मांग सकती है या remote service की error लौटा सकती है। एजेंट के पास «सबसे अधिक access देने वाला credential इस्तेमाल करो» जैसा कोई parameter नहीं होता।

यह अंतर अक्सर धुंधला कर दिया जाता है: **secret-free का अर्थ token-redacted नहीं है।** Redaction उस authority को मिलने के बाद एजेंट की visibility नियंत्रित करने की कोशिश करता है। Action contract एजेंट को वह authority रखने ही नहीं देता। Prompt leak हो जाए, tool transcript copy हो जाए या subprocess environment पढ़ ले, तो पहले design में credential पहले ही खो चुका है। दूसरे design में operational data सामने आ सकता है, जिसके लिए अलग controls चाहिए, लेकिन signing material agent को नहीं मिलता।

Contract को यह भी नहीं मानना चाहिए कि हर endpoint के लिए custom tool बनाया जाए। Custom actions वहां उपयोगी हैं जहां व्यक्ति इच्छित outcome एक वाक्य में बता सके। «इस staging workload को restart करो» outcome है। «किसी भी URL पर PATCH भेजो» transport primitive है। Maintenance job के लिए primitive जरूरी हो तो उसे अलग, कड़ी सीमा वाली integration को दें, general-purpose coding agent को नहीं।

## Credential owner को request खुद execute करनी चाहिए

अगर agent अभी भी अपने process में mounted secret के साथ final network call करता है, तो contract किसी चीज की रक्षा नहीं करता। Credential रखने वाले component को HTTP request या SSH connection खुद बनाना चाहिए।

Execution boundary की पांच जिम्मेदारियां हैं:

- Action name को fixed destination और protocol behavior से जोड़ना।
- Approved identities के छोटे set में से stored identity चुनना।
- Credential को केवल outbound request या SSH authentication exchange में जोड़ना।
- Secret material को record में लिखे बिना request, decision और result दर्ज करना।
- Internal state का dump लौटाने के बजाय action के लिए उपयुक्त response देना।

Model process को token या private key, यहां तक कि temporary key भी नहीं मिलनी चाहिए। `TOKEN=$(vault read ...)` जैसी shell conventions, working directory में credential files, generated curl commands में `Authorization` values और agent-run shell के साथ साझा SSH agents से बचें। ये सुविधाजनक लगते हैं क्योंकि मौजूदा scripts वैसे ही चलती रहती हैं, लेकिन हर तरीका agent process को credential holder बना देता है।

IETF की OAuth 2.0 Security Best Current Practice अलग संदर्भ में यही व्यावहारिक बात कहती है: bearer tokens को storage और transit में सुरक्षित रखना जरूरी है, क्योंकि जिसके पास token है वह उसका इस्तेमाल कर सकता है। Model को token print न करने के लिए कहना उसे सुरक्षित नहीं बनाता। Possession ही authorization check है। Agent tool के लिए बेहतर design है कि process को possession ही न दी जाए।

SSH में boundary को private key से अधिक चीजों का मालिक होना चाहिए। Raw `ssh host command` interface agent को व्यापक पहुंच देता है, भले key helper से बाहर न आए। Helper को stored host definition और identity चुननी चाहिए, फिर उस host के लिए उपयुक्त command form लागू करनी चाहिए। Deployment host पर `status`, `restart-service` और service name के साथ `tail-release-log` की अनुमति हो सकती है। किसी shortcut के लिए चुपचाप `bash -c` स्वीकार नहीं करना चाहिए।

इसे man-in-the-middle proxy न समझें। Proxy arbitrary client traffic relay करता है और transit में credentials भी देख सकता है। Credential-owning action layer named operation का request लेती है, outbound call खुद बनाती है और credential अपने vault में रखती है। यही अंतर तय करता है कि agent approved action को किसी दूसरी action में बदल सकता है या नहीं।

## Parameter design तय करता है कि कितनी authority बाहर आएगी

Contract का हर field caller के लिए एक degree of freedom बनाता है। अच्छे fields काम की object पहचानते हैं या वह content देते हैं जिसकी action को सचमुच जरूरत है। खराब fields authority की दिशा, इस्तेमाल होने वाली identity या चलने वाले lower-level operation को बदलते हैं।

हर proposed input पर यह जांच करें: अगर agent यह value बदल दे, तो क्या privileged request किसी दूसरे system की ओर भेजी जा सकती है, प्रभावित resources का दायरा बढ़ सकता है या authentication बदल सकती है? अगर हां, तो field हटाएं, उसे executor द्वारा mapped enum बनाएं या operation को अलग contracts में बांटें।

Deployment interface इसका अच्छा उदाहरण है:

```json
{
  "name": "deploy_release",
  "input_schema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["service", "environment", "version", "reason"],
    "properties": {
      "service": {"type": "string", "enum": ["api", "worker"]},
      "environment": {"type": "string", "enum": ["test", "production"]},
      "version": {"type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
      "reason": {"type": "string", "maxLength": 500}
    }
  }
}
```

Schema `url`, `headers`, `credential_name` या `command` जैसे अनपेक्षित fields को रोकती है। Executor `service` और `environment` को known deployment target से map कर सकता है। `additionalProperties: false` जितना दिखता है उससे अधिक महत्वपूर्ण है। इसके बिना permissive validator unrecognized field को बचाकर रख सकता है और बाद में कोई व्यक्ति flexibility के नाम पर उसे HTTP client से जोड़ सकता है। इसी तरह harmless extension point credential escape hatch बनता है।

Enums हमेशा सही उत्तर नहीं होते। Repository name, branch, ticket number या file path बदल सकते हैं। इन values को उनके domain के अनुसार validate करें और resolution के बाद boundary check लगाएं। उदाहरण के लिए repository identifier को local allowlist से resolve करें और mapped remote location इस्तेमाल करें। Repository URL स्वीकार करके यह तय करने की कोशिश न करें कि वह friendly दिखती है या नहीं।

Free text पर अलग निर्णय चाहिए। Agent को issue description, pull request summary या support reply लिखना पड़ सकता है। यह text content है, authority नहीं, लेकिन mentions, markup, templates या downstream service द्वारा पढ़े जाने वाले embedded commands के जरिए नुकसान कर सकता है। इसकी लंबाई सीमित करें, rendering behavior स्पष्ट रखें और इसे shell command में interpolate न करें। Command चलानी हो तो argument vector सीधे बनाएं और untrusted text को data argument में रखें, command string में नहीं।

## Generic request tools छिपे हुए policy engines बनाते हैं

Generic HTTP tool लोकप्रिय है क्योंकि इससे टीम एक दोपहर में agent को किसी service से जोड़ सकती है। अधिकतर privileged agent work के लिए यह गलत है, क्योंकि हर prompt, tool description और code branch unofficial authorization policy बन जाता है।

टीम अक्सर ऐसे wrapper से शुरू करती है:

```text
request(method, url, headers, body)
```

फिर guardrails जुड़ते जाते हैं। कुछ domains block करें। `Authorization` हटाएं। कुछ methods की अनुमति दें। URL prefix parse करें। `localhost` अस्वीकार करें। Risky calls पर approval dialog दिखाएं। कुछ महीनों बाद नया endpoint custom header मांगता है, कोई exception जोड़ देता है और wrapper ऐसी policy language बन जाता है जिसका न test है, न स्पष्ट owner।

समस्या यह नहीं कि generic tools हमेशा खराब होते हैं। वे human-operated debugging console में ठीक हैं, जहां operator के पास पहले से authority है और वह हर byte देख सकता है। वे ऐसी integration service के लिए भी ठीक हैं जिसे नियंत्रित code से calls मिलती हों और जिसकी network identity संकरी हो। Autonomous agent अलग है। वह कई calls कर सकता है, अनपेक्षित paths खोज सकता है और untrusted text पर काम कर सकता है। उसे कम degrees of freedom चाहिए।

Stable work units के आसपास named actions बनाएं। Source control service में universal REST client के बजाय `read_merge_request`, `comment_on_merge_request` और `create_branch` बेहतर हैं। Operations में `get_service_status`, `fetch_release_logs` और `request_deployment` रखें। Contracts अधिक बनेंगे, लेकिन हर contract का owner, test set, स्पष्ट approval label और review किया जा सकने वाला blast radius होगा।

Named action के भीतर generic request छिपाएं भी नहीं। `update_ticket` नाम का tool अगर arbitrary `path`, `method` और `body` स्वीकार करता है, तो केवल label बदला है। Contract को इन details पर सीमा लगानी होगी। Downstream API को controlled patch object चाहिए तो उसे expose किया जा सकता है, लेकिन endpoint, HTTP method, content type और account executor तय करे।

Model Context Protocol specification tool discovery में मदद करती है, क्योंकि server client को tool names, descriptions और JSON input schemas प्रकाशित कर सकता है। Schema उपयोगी है, लेकिन अत्यधिक व्यापक action को सुरक्षित नहीं बनाती। JSON Schema बता सकती है कि URL string है। वह यह नहीं बता सकती कि यह URL production credential के लिए एकमात्र billing endpoint है। Authorization execution layer की जिम्मेदारी रहती है।

## Approval में वही action दिखनी चाहिए जिसे व्यक्ति समझ सके

Human approval तब काम करती है जब व्यक्ति recognizable request देख सके और जल्दी reject कर सके। जब prompt transport details का opaque packet approve करने को कहता है, तब यह विफल होती है, खासकर तब जब agent ने पहले ही महत्वपूर्ण choices कर ली हों।

इन approval cards की तुलना करें:

```text
Allow POST https://api.example/v1/resources/882?
Headers: Authorization, X-Region, X-Client
```

```text
Deploy version 2.14.3 of api to production
Reason: Fixes failed payment retries
Requested by: signed agent process build-worker
```

दूसरा card operator को intent समझने देता है। Audit record के लिए भी यह उपयोगी वाक्य देता है। पहला operator से URL और header list का अर्थ निकालने को कहता है, जिससे approval fatigue बढ़ती है। जब सामान्य agent run में कई prompts आते हैं, लोग unreadable prompts को बिना पढ़े approve कर देते हैं।

Approval उस बिंदु पर लें जहां decision authority बदलता है। Execution layer session के लिए नए agent process को एक बार authorize कर सकती है, फिर चुने हुए sensitive credentials या destructive actions के लिए fresh decision मांग सकती है। इससे routine work चलता रहता है और सभी credentials को समान नहीं माना जाता। Read-only project token और production deployment identity को केवल इसलिए एक approval rule साझा नहीं करना चाहिए कि दोनों HTTP headers में भेजे जाते हैं।

Approval language में यह भी होना चाहिए कि operation का अनुरोध किसने किया। Process identity उपयोगी है, क्योंकि terminal agent, background helper और unknown executable को समान trust नहीं मिलना चाहिए। macOS पर code-signing authority approval देने वाले व्यक्ति को origin का ठोस संकेत दे सकती है। इससे यह साबित नहीं होता कि हर prompt instruction सुरक्षित है, लेकिन पहला सवाल साफ हो जाता है: इस account के साथ काम करने का अनुरोध कौन-सा process कर रहा है?

Approval को कभी अकेला control न बनाएं। व्यक्ति prompt गलत पढ़ सकता है, समय के दबाव में approve कर सकता है या session खुला छोड़ सकता है। Contract में constrained inputs और fixed credential route फिर भी जरूरी हैं। दूसरी ओर, जब स्पष्ट action contract और approval choice पर्याप्त हों, तब अलग policy language न जोड़ें। Arbitrary fields, time windows, regular expressions और user claims की तुलना करने वाले rules जल्दी ही एक और program बन जाते हैं जिसे incident के दौरान कोई भरोसे से review नहीं कर पाता।

## Failed deployment दिखाता है कि ढीले contracts कहां टूटते हैं

एक परिचित failure तब शुरू होता है जब agent shell tool के जरिए test पर deploy कर सकता है। Team agent के environment में cloud token रखती है, क्योंकि deployment CLI उसे मांगती है। Tool schema `environment` और `extra_args` स्वीकार करती है। जब केवल test मौजूद था, ये harmless लगे।

Ticket agent से कहता है, «test में urgent fix verify करो और result साझा करो।» Agent अपेक्षित command चलाता है। फिर repository में पुराना deployment message देखकर पुराने script से copy किया गया extra argument आजमाता है। वह production चुन सकता है, target account बदल सकता है या shell expansion डाल सकता है। Token का scope production तक था, क्योंकि अलग credentials रखना अतिरिक्त मेहनत लगा। उस समय prompt की wording आपकी रक्षा नहीं करती। Process के पास पहले ही व्यापक authority है और interface उसे target चुनने देता है।

Contract boundary sequence बदल देती है:

1. Agent enumerated service, environment, version और reason के साथ `deploy_release` call करता है।
2. Executor environment को fixed target से resolve करता है और उस target को दी गई identity चुनता है।
3. अगर identity को approval चाहिए, तो executor decision मांगता है और result को अनुरोध करने वाले agent run के साथ दर्ज करता है।
4. Executor deployment identifier और status लौटाता है, या structured denial देता है जिसमें बताया जाता है कि आगे क्यों नहीं बढ़ा जा सकता।

Agent `--account` नहीं जोड़ सकता, cloud endpoint सेट नहीं कर सकता और token पढ़ नहीं सकता। गलत `production` value मांगना फिर भी संभव है, क्योंकि इंसान और model दोनों गलत चीज मांग सकते हैं। लेकिन अब approval text production को साफ भाषा में बताता है, selected identity के पास केवल intended production deployment authority हो सकती है और action record decision को process तथा request से जोड़ता है।

Diagnosis के समय यह अंतर महत्वपूर्ण है। Loose design में investigators को shell transcript, cloud audit events, CI log और शायद ऐसा token मिलता है जिसे अब rotate करना पड़े। Contract design में requested action, resolved target, identity label, approval result, response status और initiating session देखे जा सकते हैं। Audit trail error मिटाता नहीं, लेकिन यह अनुमान लगाने में लगने वाला समय कम करता है कि कौन-सा path चला था।

## Error responses recovery में मदद करें, internals उजागर न करें

Secure action layer को ऐसे errors लौटाने चाहिए जिन पर agent secret, request signing data या internal vault structure पाए बिना काम कर सके। बहुत vague failure agent को retries और workarounds की ओर धकेलता है। बहुत detailed failure error logs को information channel बना देता है।

Stable error codes और छोटा public shape इस्तेमाल करें:

```json
{
  "ok": false,
  "error": {
    "code": "APPROVAL_REQUIRED",
    "message": "Deployment to production needs user approval.",
    "retryable": true,
    "request_id": "act_01H..."
  }
}
```

Locked vault को `VAULT_LOCKED`, denied user decision को `APPROVAL_DENIED`, contract violation को `INVALID_ARGUMENT` और downstream 429 को `REMOTE_RATE_LIMITED` लौटाना चाहिए। Agent state बता सकता है, रुक सकता है, condition बदलने पर retry कर सकता है या non-destructive alternative ले सकता है। उसे raw authorization header, token subject dump, private host configuration या full signed request वाला error नहीं मिलना चाहिए।

Authorization failure और remote failure अलग रखें। «Permission denied» का अर्थ local executor द्वारा action रोकना, selected remote account के पास permission न होना या downstream service द्वारा malformed authentication अस्वीकार करना हो सकता है। इनकी मरम्मत अलग होगी। Public message छोटा रह सकता है, जबकि protected audit record precise reason code और remote status रखे।

Retries में contract semantics होनी चाहिए। Read operations अक्सर retry सह लेती हैं, लेकिन ticket बनाना, message भेजना या deployment शुरू करना ऐसा नहीं कर सकता। जहां remote API support करे, idempotency identifier जोड़ें। इसे executor generate करे या constrained request identifier के रूप में मिले। Request भेजने से पहले association record करें और retry पर वही identifier इस्तेमाल करें। Timeout दिखने पर agent को हर बार नया identifier बनाने न दें, वरना मदद करने की कोशिश में duplicate work हो सकता है।

जिन actions में remote idempotency support नहीं है, वहां prepare-and-confirm arrangement अपनाएं। Prepare action target और diff बताने वाला short-lived plan लौटाए। Confirm action उस plan को refer करे और current approval मांगे। एक extra round trip लगेगा, लेकिन ambiguous network failure के बाद payment, deletion या production change दोहराने से यह सस्ता है।

## Audit records को दो views और एक source of truth चाहिए

उपयोगी audit system दो अलग सवालों का जवाब देता है: इस agent run ने क्या करने की कोशिश की, और हर privileged call ने क्या किया? दोनों को एक ही undifferentiated event stream में मिला देने पर session reconstruct करना या कोई specific request ढूंढना कठिन हो जाता है।

Run के लिए session journal रखें। इसमें process identity, start और end, authorization decision, revocation state और उस run में मांगी गई actions दिखनी चाहिए। Calls के लिए activity journal रखें। इसमें action name, normalized parameters, secret के बिना selected credential label, approval result, timing, destination class और outcome दिखना चाहिए।

दोनों views एक ही append-only record से बनें। वरना एक writer crash होने या events को अलग तरह से filter करने पर session screen और call log अलग बातें कह सकते हैं। Write-blind encrypted log का व्यावहारिक लाभ भी है: event जोड़ने वाले component को नया event लिखने के लिए पुराने records decrypt नहीं करने पड़ते।

Tamper evidence के लिए offline check चाहिए। Hash chain verifier को deletion, substitution या reordered records पहचानने देती है, जब उसके पास log sequence हो। Check ciphertext पर चलना चाहिए, ताकि auditor vault key पाए बिना continuity जांच सके। इससे यह साबित नहीं होता कि compromised machine ने कभी कोई event log करना छोड़ा नहीं। यह केवल साबित करता है कि retained chain को बाद में चुपचाप edit नहीं किया गया। इन दोनों दावों को अलग रखें।

Command-line verifier failures को स्पष्ट बनाए:

```text
$ sp audit verify audit.log
verified: 184 records
first sequence: 9012
last sequence: 9195
chain: valid
```

अगर record 9137 बदला गया हो, तो command को पहला टूटा sequence बताकर nonzero exit करना चाहिए। केवल «verification failed» न दिखाएं। Incident responders को वह स्थान चाहिए जहां evidence पर भरोसा खत्म होता है।

Sallyport इसी encrypted hash-chained audit log से agent sessions और individual activity के दो views बनाता है। `sp audit verify` vault key के बिना इसकी जांच कर सकता है। Local agent gateway के लिए यही सही shape है, क्योंकि running session revoke करना और individual call की जांच करना अलग काम हैं।

## Contracts को escape attempts वाले tests चाहिए

Happy-path tests बताते हैं कि action काम करती है। Security tests बताते हैं कि घोषित inputs ही caller के पास मौजूद controls हैं। Convenience parameter जोड़ने से पहले ये tests लिखें, क्योंकि authority अक्सर ऐसे ही parameters से वापस अंदर आती है।

हर action में कम से कम ये cases test करें:

- `headers`, `url`, `command` और credential references सहित unexpected field अस्वीकार हो।
- ऐसे values अस्वीकार हों जो action के allowed resource set से बाहर resolve होती हैं।
- Outbound HTTP request को credential तभी मिले जब executor destination बना चुका हो।
- Audit entries में token values, private key material और signed authorization fields न हों।
- Denied या revoked session पिछली approval reuse न कर सके।

Tests में fake outbound server इस्तेमाल करें और received request जांचें। Actual URL, method, executor द्वारा बनाए गए headers और caller-controlled authentication की अनुपस्थिति assert करें। केवल executor के internal client को mock करना पर्याप्त नहीं, क्योंकि असली सवाल यह है: agent hostile parameters दे तो machine से बाहर क्या जाएगा?

उन awkward inputs को भी test करें जिन्हें model आखिर में जरूर बनाएगा: scheme prefix वाला repository identifier, shell punctuation वाला branch name, environment label में Unicode lookalike, repeated JSON fields, बहुत बड़ा description और remote service द्वारा action स्वीकार करने के बाद आया timeout। Contract validation fail closed होनी चाहिए। Executor requested target को भरोसे से resolve न कर सके तो call रोककर उपयोगी error लौटाए।

Contracts को authority वाले code की तरह review करें। पूछें कि नया field caller को दूसरे host, व्यापक account, अलग command या अलग object class तक पहुंच तो नहीं देता। अगर देता है, तो authority को action name और approval behavior में स्पष्ट करें। Free-form absolute path वाला `delete_file` action समझना कठिन है। Known project के भीतर asset identifier resolve करने वाला `remove_preview_asset` अधिक स्पष्ट है।

आमतौर पर सबसे पहले उस contract को ठीक करें जो arbitrary URL या shell string स्वीकार करता है। उसे लोगों की वास्तविक जरूरत पूरी करने वाली सबसे छोटी named action से बदलें। Interface कम clever होगा और agent उन तरीकों से कम शक्तिशाली होगा जिन्हें बाद में समझाना मुश्किल पड़ता है। यही प्रगति है।
