
عميل TypeScript رفيع + عميل بايثون بدون تبعيات ووصفات لحظر الإجراءات عالية المخاطر خلف موافقة مفتاح مرور مرتبط بالحمولة.
عميل TypeScript رفيع، خالٍ من التبعيات، لـ Cosignet — موافقة بإشراف بشري لإجراءات وكلاء الذكاء الاصطناعي عالية المخاطر، مع توقيعات مفتاح المرور المرتبطة بالحمولة.
ضع إنساناً في الحلقة قبل تنفيذ إجراء خطير: أوقفه، احصل على موافقة صريحة بمفتاح مرور من شخص (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: 'تحويل بنكي للمورد',
payload: { to: 'acct_8821', amount_usd: 4200, memo: 'INV-2025-118' },
notify: 'telegram_or_email',
},
{ onCreated: (c) => console.log('وافق هنا:', c.url) },
);
if (decision.status === 'approved') {
// تابع — decision.rawAssertion هو الدليل الموقّع
} else {
// 'rejected' | 'expired' | 'pending' (مهلة زمنية)
}
requestApproval يقوم بالاستقصاء الطويل عبر اتصالك الصادر (~25 ثانية لكل قفزة)، لذا يعمل من أدوات CLI وشبكات VPC المقيدة خلف NAT/جدران الحماية — لا حاجة إلى webhook داخلي، أو منفذ مفتوح، أو IP عام.
إذا لم يتمكن نظامك من الحصول على موافقة موقعة، يجب ألا يتم تشغيل الإجراء المقيد. عدم التوفر يجب ألا يتحول أبدًا إلى موافقة تلقائية. تعامل مع كل مهلة زمنية، خطأ شبكة، و5xx كـ "غير موافق عليه".
ملاحظات التنفيذ:
انظر عندما يكون Cosignet غير متاح للحصول على إرشادات كاملة حول التوفر والـ break-glass.
const created = await cosignet.createConfirmation({ username, action, payload });
const status = await cosignet.getConfirmation(created.id, { wait: 25 }); // استقصاء طويل
مرّر idempotencyKey (يُرسل كرأس Idempotency-Key) بحيث تعيد محاولة الإنشاء —
على سبيل المثال بعد انقطاع الشبكة الذي يقطع الاستقصاء الطويل — باستخدام نفس المفتاح ونفس الإجراء/الحمولة إرجاع التأكيد الأصلي (idempotent: true) بدلاً من إنشاء نسخة مكررة أو إعادة إشعار الموافق. إعادة استخدام المفتاح مع معاملات مختلفة يُرفض بـ 422.
await cosignet.createConfirmation({
username: 'alex',
action: 'تحويل بنكي للمورد',
payload: { to: 'acct_8821', amount_usd: 4200 },
idempotencyKey: 'wire-INV-2025-118', // ثابت لكل عملية منطقية
});
مرّر returnUrl لإظهار زر "العودة إلى <host>" في صفحة الموافقة بمجرد حل الطلب (موافق عليه / مرفوض / منتهي الصلاحية) — مفيد ليتمكن الإنسان من العودة إلى تطبيقك لإعادة تشغيل إجراء انتهت مهلة. يجب أن يكون عنوان URL https ويُعرض فقط كارتباط نقرة (لا يتم جلبه من الخادم أبداً).
await cosignet.requestApproval({
username: 'alex',
action: 'تحويل بنكي للمورد',
payload: { to: 'acct_8821', amount_usd: 4200 },
returnUrl: 'https://app.example.com/approvals',
});
notify ('none' | 'telegram' | 'email' | 'telegram_or_email') يتحكم في
التنبيه الشخصي للموقّع المحدد:
telegram — رسالة مباشرة إلى Telegram المرتبط بالموافق (telegram_or_email يفضل
هذا عندما يكون الموافق قد ربط محادثة).email — بريد إلكتروني إلى عنوان الموافق الخاص. الأولوية: البريد الإلكتروني المباشر
الذي حددته للموافق في لوحة التحكم (للتسليم فقط)، ثم بريده الإلكتروني كعضو موثّق،
ثم عنوان الاتصال للحساب كملاذ أخير.telegram_or_email — Telegram إذا كان مرتبطاً، وإلا البريد الإلكتروني أعلاه.none — لا تنبيه شخصي.بشكل منفصل، التنبيه الجماعي هو بث Slack إلى قناة مشتركة، يتم تكوينه لكل حساب
في لوحة التحكم. وهو إضافي و يُطلق دائماً عند تكوينه، بغض النظر عن
notify (لذا notify:'none' لا يزال ينشر إلى Slack). يحصل الموافقون على
Telegram والبريد الإلكتروني الخاص بهم من قسم الموافقين في لوحة التحكم.
ملاحظة حول الإفصاح
public: true: البريد الإلكتروني الملتزم في سجل الشفافية هو البريد الإلكتروني الموثّق المحدد للحساب (المختار في إعدادات لوحة التحكم، كالطرف المسؤول — وليس الموقّع الفردي). البريد الإلكتروني المباشر للإشعار هو للتسليم فقط ولا يُستخدم أبداً لتجزئة الإفصاح.
كل قناة هي رابط فقط: الإشعار يحمل رابط الموافقة ولا يحمل أبداً action
أو payload. تتطلب الموافقة دائماً مفتاح مرور الموافق المرتبط بالحمولة على صفحة التأكيد
— لا توجد موافقة داخل القناة.
verifyWebhookSignature يعيد حساب HMAC-SHA256 السداسي العشري لجسم الطلب الخام
ويقارنه بزمن ثابت مع الرأس Cosignet-Signature. مرّر البايتات الدقيقة التي تلقيتها —
إعادة تسلسل JSON يغير التوقيع.
import { verifyWebhookSignature } from '@cosignet/sdk';
const ok = await verifyWebhookSignature({
body: rawBody, // سلسلة خام، وليس JSON معاد تحليله
signature: req.headers['cosignet-signature'],
secret: process.env.COSIGNET_WEBHOOK_SECRET!,
timestamp: req.headers['cosignet-timestamp'], // حماية اختيارية ضد إعادة التشغيل
toleranceSeconds: 300, // اختياري
});
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>بشكل افتراضي، يحجب سجل الشفافية الحمولة ولا يكشف عن هوية أي موافق. قم بتعيين public: true في createConfirmation/requestApproval لتفعيل الإفصاح العام لموافقة واحدة: تصبح action + payload الخام قابلة للقراءة في حزمة التحقق، ويتم التزام تجزئة PBKDF2 لـ البريد الإلكتروني الموثّق المحدد للحساب (المختار في إعدادات لوحة التحكم — الطرف المسؤول، وليس الموقّع الفردي) في ورقة Merkle حتى يتمكن أي شخص من التحقق من عنوان مرشح مقابله.
await cosignet.requestApproval({
username: 'alice',
action: 'نشر قرار مجلس الإدارة للربع الثالث',
payload: { docId: 'res-2026-Q3' },
public: true, // دائم ولا رجعة فيه — انظر المحاذير أدناه
});
examples/ — أمثلة قابلة للنسخ واللصق لـ cURL، Node، Python، GitLab CI، MCP/tool-wrapper، وWorker.security/ — ربط الحمولة، نموذج إثبات WebAuthn، نموذج التهديد، سجل الشفافية، والإفصاح المسؤول.احجز الإجراءات عالية المخاطر خلف موافقة مفتاح المرور:
recipes/ai-agent-gate — أوقف وكيل ترميز AI (Claude Code) قبل تشغيل أمر شل خطير حتى يوقع إنسان مشارك.recipes/cli-approval — لف أي أمر (مثل terraform destroy).recipes/ci-cd — احجز خط أنابيب (GitHub Actions / GitLab CI) عند الموافقة.recipes/backend-service — أوقف عملية غير قابلة للتراجع في خدمة.[email protected]؛ لا تفتح مشكلات عامة للثغرات.[email protected].MIT © Cosignet
| الإشارة | المعنى | إجراءك |
|---|
status: approved (يتحقق التوقيع) | وافق الإنسان على هذه الحمولة بالضبط | تابع |
status: rejected | رفض الإنسان | لا تنفذ؛ أظهر لمقدم الطلب |
status: expired | لم يقرر أحد في الوقت المناسب | لا تنفذ؛ أعد الطلب إذا لا يزال ضرورياً |
| مهلة زمنية، خطأ شبكة، أو 5xx | حالة غير معروفة | لا تنفذ؛ أعد المحاولة مع تأخير متزايد؛ أرسل تنبيه بعد N من الإخفاقات |