
पतला TypeScript + शून्य-निर्भरता Python क्लाइंट और पेलोड-बाउंड पासकी अनुमोदन के पीछे उच्च-जोखिम क्रियाओं को गेट करने के लिए रेसिपी
पतला, शून्य-निर्भरता वाला TypeScript क्लाइंट Cosignet के लिए — मानव-इन-द-लूप अनुमोदन उच्च-जोखिम वाली AI-एजेंट क्रियाओं के लिए, पेलोड-बाउंड पासकी हस्ताक्षरों के साथ।
एक खतरनाक कार्रवाई चलने से पहले एक मानव को लूप में डालें: इसे रोकें, एक व्यक्ति से स्पष्ट पासकी अनुमोदन प्राप्त करें (Face ID / Touch ID / Windows Hello / सुरक्षा कुंजी), और केवल सटीक पेलोड से बंधे हस्ताक्षरित निर्णय पर आगे बढ़ें — बाद में कार्रवाई बदलें और हस्ताक्षर अब मेल नहीं खाएगा। Cosignet एक अनुमोदन और साक्ष्य परत है, न कि निष्पादक या नीति इंजन। वैश्विक fetch + Web Crypto के साथ कहीं भी चलता है: Node 18+, Cloudflare Workers, Deno, और ब्राउज़र।
स्थिति: प्रारंभिक पहुंच। npm पर
@cosignet/sdkके रूप में प्रकाशित।
npm install @cosignet/sdk
import { Cosignet } from '@cosignet/sdk';
const cosignet = new Cosignet({ apiKey: process.env.COSIGNET_API_KEY! });
const decision = await cosignet.requestApproval(
{
username: 'alex',
action: 'Wire transfer to vendor',
payload: { to: 'acct_8821', amount_usd: 4200, memo: 'INV-2025-118' },
notify: 'telegram_or_email',
},
{ onCreated: (c) => console.log('Approve here:', c.url) },
);
if (decision.status === 'approved') {
// proceed — decision.rawAssertion is the signed proof
} else {
// 'rejected' | 'expired' | 'pending' (timed out)
}
requestApproval आपके अपने आउटबाउंड कनेक्शन पर लॉन्ग-पोल करता है (~25s हॉप्स), इसलिए यह CLI टूल्स और लॉक-डाउन VPCs NAT/फ़ायरवॉल के पीछे से काम करता है — कोई इनबाउंड वेबहुक, खुला पोर्ट, या सार्वजनिक IP आवश्यक नहीं है।
यदि आपका सिस्टम एक हस्ताक्षरित अनुमोदन प्राप्त नहीं कर सकता है, तो गेटेड क्रिया नहीं चलनी चाहिए। अनुपलब्धता कभी भी ऑटो-अनुमोदन में नहीं बदलनी चाहिए। प्रत्येक टाइमआउट, नेटवर्क त्रुटि और 5xx को "अनुमोदित नहीं" मानें।
| संकेत | अर्थ | आपकी कार्रवाई |
|---|---|---|
status: approved (हस्ताक्षर सत्यापित होता है) | मानव ने इस सटीक पेलोड को अनुमोदित किया | आगे बढ़ें |
status: rejected | मानव ने अस्वीकार किया | न चलाएं; अनुरोधकर्ता को सूचित करें |
status: expired | किसी ने समय पर निर्णय नहीं लिया | न चलाएं; यदि अभी भी आवश्यक हो तो पुनः अनुरोध करें |
| टाइमआउट, नेटवर्क त्रुटि, या 5xx | अज्ञात स्थिति | न चलाएं; बैकऑफ़ के साथ पुनः प्रयास करें; N विफलताओं के बाद अलर्ट करें |
कार्यान्वयन नोट:
जब Cosignet अनुपलब्ध हो तो पूर्ण उपलब्धता और ब्रेक-ग्लास मार्गदर्शन के लिए जब Cosignet अनुपलब्ध हो देखें।
const created = await cosignet.createConfirmation({ username, action, payload });
const status = await cosignet.getConfirmation(created.id, { wait: 25 }); // long-poll
idempotencyKey पास करें (जो Idempotency-Key हेडर के रूप में भेजा जाता है) ताकि एक पुनर्प्रयास किया गया क्रिएट — उदाहरण के लिए नेटवर्क ब्लिप के बाद लॉन्ग-पोल बाधित होने पर — उसी कुंजी और उसी क्रिया/पेलोड के साथ मूल पुष्टि लौटाता है (idempotent: true) बजाय डुप्लिकेट बनाने या अनुमोदक को पुनः सूचित करने के। अलग-अलग पैरामीटर के साथ कुंजी का पुन: उपयोग 422 के साथ अस्वीकार कर दिया जाता है।
await cosignet.createConfirmation({
username: 'alex',
action: 'Wire transfer to vendor',
payload: { to: 'acct_8821', amount_usd: 4200 },
idempotencyKey: 'wire-INV-2025-118', // प्रति तार्किक संचालन स्थिर
});
returnUrl पास करें ताकि अनुरोध हल होने पर अनुमोदन पृष्ठ पर एक "<होस्ट> पर वापस जाएं" बटन दिखाया जा सके (अनुमोदित / अस्वीकृत / समाप्त) — यह सुविधाजनक है ताकि कोई मानव समय-समाप्त हुई कार्रवाई को फिर से ट्रिगर करने के लिए आपके ऐप पर वापस आ सके। यह एक https URL होना चाहिए और केवल एक क्लिक-थ्रू लिंक के रूप में प्रस्तुत किया जाता है (कभी सर्वर-साइड नहीं लाया जाता)।
await cosignet.requestApproval({
username: 'alex',
action: 'Wire transfer to vendor',
payload: { to: 'acct_8821', amount_usd: 4200 },
returnUrl: 'https://app.example.com/approvals',
});
notify ('none' | 'telegram' | 'email' | 'telegram_or_email') विशिष्ट हस्ताक्षरकर्ता को व्यक्तिगत-पहुंच पिंग को नियंत्रित करता है:
telegram — अनुमोदक के लिंक किए गए Telegram पर एक DM (telegram_or_email इसे पसंद करता है जब अनुमोदक ने एक चैट लिंक किया है)।email — अनुमोदक के स्वयं के पते पर एक ईमेल। प्राथमिकता: डैशबोर्ड में आपने अनुमोदक पर सेट किया गया प्रत्यक्ष ईमेल (केवल-डिलीवरी), फिर उनका सत्यापित सदस्य ईमेल, फिर अंतिम उपाय के रूप में खाता संपर्क पता।telegram_or_email — यदि लिंक किया गया है तो Telegram, अन्यथा उपरोक्त ईमेल।none — कोई व्यक्तिगत पिंग नहीं।अलग से, टीम पहुंच एक साझा चैनल पर एक Slack प्रसारण है, जो डैशबोर्ड में प्रति-खाता कॉन्फ़िगर किया गया है। यह योगात्मक है और कॉन्फ़िगर होने पर हमेशा फायर होता है, notify से स्वतंत्र (इसलिए notify:'none' अभी भी Slack पर पोस्ट करता है)। अनुमोदक अपने Telegram और ईमेल डैशबोर्ड के अनुमोदक अनुभाग से सेट करते हैं।
public: trueरिवील पर नोट: पारदर्शिता लॉग में प्रतिबद्ध ईमेल खाते का निर्दिष्ट सत्यापित ईमेल है (डैशबोर्ड सेटिंग्स में चुना गया, उत्तरदायी पार्टी के रूप में — व्यक्तिगत हस्ताक्षरकर्ता नहीं)। प्रत्यक्ष सूचना ईमेल केवल-डिलीवरी है और कभी भी रिवील हैश के लिए उपयोग नहीं किया जाता है।
हर चैनल केवल-लिंक है: सूचना अनुमोदन URL ले जाती है और कभी action या payload नहीं। अनुमोदन के लिए हमेशा पुष्टि पृष्ठ पर अनुमोदक के पेलोड-बाउंड पासकी की आवश्यकता होती है — कोई इन-चैनल अनुमोदन नहीं है।
verifyWebhookSignature कच्चे अनुरोध निकाय के hex HMAC-SHA256 की पुनर्गणना करता है और इसे Cosignet-Signature हेडर से स्थिर-समय तुलना करता है। आपके द्वारा प्राप्त सटीक बाइट्स पास करें — JSON को पुनः क्रमबद्ध करने से हस्ताक्षर बदल जाता है।
import { verifyWebhookSignature } from '@cosignet/sdk';
const ok = await verifyWebhookSignature({
body: rawBody, // raw string, not re-parsed JSON
signature: req.headers['cosignet-signature'],
secret: process.env.COSIGNET_WEBHOOK_SECRET!,
timestamp: req.headers['cosignet-timestamp'], // optional replay protection
toleranceSeconds: 300, // optional
});
if (!ok) return res.status(401).end();
कॉलर-साइड चेकलिस्ट के लिए docs/verification.md देखें: केवल approved पर जारी रखें, निर्णय की तुलना उस ऑपरेशन से करें जिसे आप चलाने वाले हैं, और अस्वीकृत/समाप्त/टाइमआउट को हार्ड स्टॉप मानें।
new Cosignet({ apiKey, baseUrl?, fetch? })createConfirmation(input) → CreatedConfirmationgetConfirmation(id, { wait? }) → ConfirmationrequestApproval(input, { timeoutMs?, onCreated? }) → ConfirmationverifyWebhookSignature({ body, signature, secret, timestamp?, toleranceSeconds? }) → Promise<boolean>