روابط مختصرة وأسرار، من الكود مباشرة
مفتاح bearer واحد و JSON بسيط، وقواعد الخطة نفسها المطبقة في لوحة التحكم. صُممت للإضافات وأدوات CLI وخوادم MCP والأدوات الداخلية.
وصول مبكر. نفعّل نقاط النهاية في الإصدار v1 مساحة عمل تلو الأخرى. إذا أعاد الطلب 503 مع الرمز disabled، فالواجهة لم تُفعَّل لك بعد. يمكنك إنشاء المفاتيح اليوم، وستعمل فور تفعيلها.
مكتبات العملاء
تُشفَّر الأسرار قبل إرسالها، لذا على كل عميل أن يتولى هذا الجزء بنفسه. تتولاه هذه المكتبات مرة واحدة وبالشكل الصحيح، وتُختبر على متجهات الاختبار المعتمدة نفسها التي يستخدمها تطبيق الويب. المكتبات الثلاث مرخصة بترخيص MIT، وتُنشر من CI مع إثبات المصدر في npm، ولا تعتمد إلا على المكتبة القياسية.
TypeScript SDK
npmعميل بأنواع محددة لكل مسار. بلا أي اعتماديات، ويستخدم WebCrypto فقط، فيعمل دون تعديل على Node 20+ و Deno و Bun والمتصفحات وبيئات التشغيل على خوادم الحافة.
npm i @uselinkpilot/sdkسطر الأوامر
npmمرّر السر عبر الأنبوب (pipe) بدل تمريره وسيطًا: الوسيط يُحفظ في سجل أوامر الصدفة ويظهر في قائمة العمليات.
npm i -g linkpilot
echo 'db password: hunter2' | linkpilot secret --ttl 1hخادم MCP
npmيتيح لمساعد ذكاء اصطناعي أن يسلّم أحدًا بيانات الدخول دون أن يضعها في سجل المحادثة. أضف ما يلي إلى Claude Desktop أو Claude Code أو Cursor أو أي عميل MCP:
{
"mcpServers": {
"linkpilot": {
"command": "npx",
"args": ["-y", "@uselinkpilot/mcp"],
"env": { "LINKPILOT_API_KEY": "lp_live_..." }
}
}
}اعرف هذا قبل التثبيت: النص الأصلي يمر عبر سياق المساعد، لأن النموذج يجب أن يرى السر حتى ينشئه. تحميه هذه الطريقة أثناء النقل وأثناء التخزين، وتجعل الرابط يُفتح مرة واحدة فقط، لكنها لا تستطيع إخفاءه عن النموذج نفسه.
الكود المصدري: linkpilot-cli (SDK و CLI) و linkpilot-mcp.
1. المصادقة
أنشئ مفتاحًا من /app/api-keys (لمالكي مساحة العمل ومسؤوليها). يظهر المفتاح الكامل مرة واحدة، ولا تحتفظ خدمة LinkPilot إلا بقيمة تجزئة SHA-256 للمفتاح. أرسله مع كل طلب:
Authorization: Bearer lp_live_...يرتبط كل مفتاح بمساحة عمل، فلا يوجد معامل tenant في أي مكان. المفاتيح المُبطلة والمنتهية الصلاحية وغير المعروفة والمشوهة تعيد كلها الاستجابة نفسها 401 unauthorized. ولا تُكتب المفاتيح في السجلات أبدًا.
عنوان URL الأساسي: https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1
2. المسارات
| الطريقة | المسار | ما يفعله |
|---|---|---|
| GET | /me | مساحة العمل والخطة والحدود والاستهلاك وحصة الطلبات لمفتاحك |
| POST | /links | إنشاء رابط مختصر (url، و slug اختياري، و domain و title و tags) |
| GET | /links | عرض الروابط، الأحدث أولًا، مع ترقيم صفحات بالمؤشر (cursor) |
| GET | /links/{id} | جلب رابط واحد |
| DELETE | /links/{id} | حذف رابط |
| POST | /secrets | إنشاء رابط سري (ciphertext و enc_version و ttl_seconds و burn_after_read، و passphrase_hash في خطة Pro) |
| GET | /secrets | عرض البيانات الوصفية للأسرار (دون المحتوى أبدًا) |
| DELETE | /secrets/{id} | إبطال سر وإتلاف محتواه |
| POST | /public/links | إنشاء رابط مجهول المصدر باستخدام client_id، بلا مفتاح |
| POST | /public/secrets | إنشاء سر مجهول المصدر يُحذف بعد القراءة، بلا مفتاح |
تعيد القوائم { data, next_cursor }. لمتابعة القائمة أعد إرسال next_cursor في cursor. قيمة limit من 1 إلى 100.
3. أمثلة
إنشاء رابط باستخدام curl
curl -X POST "https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1/links" \
-H "Authorization: Bearer $LINKPILOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/launch","slug":"launch","tags":["campaign"]}'إنشاء سر من JavaScript
const b64u = (b) => btoa(String.fromCharCode(...new Uint8Array(b)))
.replace(/+/g, "-").replace(///g, "_").replace(/=+$/, "");
// 1. Encrypt locally. This key is generated here and never sent anywhere.
const key = crypto.getRandomValues(new Uint8Array(32));
const iv = crypto.getRandomValues(new Uint8Array(12));
const k = await crypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt"]);
const ct = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv, tagLength: 128 },
k,
new TextEncoder().encode("staging db password"),
);
const ciphertext = `v1.${b64u(iv)}.${b64u(ct)}`;
// 2. Store the blob. LinkPilot cannot read it.
const res = await fetch("https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1/secrets", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LINKPILOT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ ciphertext, enc_version: 1, ttl_seconds: 3600 }),
});
if (!res.ok) throw new Error((await res.json()).error.code);
const { secret_url } = await res.json();
// 3. Attach the key. Without this the link can never be opened, by anyone.
const shareUrl = `${secret_url}#k=${b64u(key)}`;عرض الروابط من Python
import os, requests
r = requests.get(
"https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1/links",
params={"limit": 20},
headers={"Authorization": f"Bearer {os.environ['LINKPILOT_API_KEY']}"},
)
r.raise_for_status()
for link in r.json()["data"]:
print(link["short_url"], "->", link["url"])
print("remaining this hour:", r.headers["X-RateLimit-Remaining"])إنشاء رابط مجهول المصدر بلا مفتاح
curl -X POST "https://khiydxamyihofmtmgvjz.supabase.co/functions/v1/api-v1/public/links" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","client_id":"my-cli-0123456789abcdef"}'ننشر العقد الرسمي هنا، لا في مستودع كود: مستند OpenAPI 3.1 و مجموعة Postman و المرجع المكتوب. وجّه مولّد الكود لديك مباشرة إلى عنوان OpenAPI.
4. كيف تُخزَّن الأسرار
نريد أن نكون دقيقين هنا، لأن على عميل API عملًا حقيقيًا: الواجهة لا تقبل payload بنص غير مشفر إطلاقًا.
- أنت تشفّر، ونحن نخزن كتلة بيانات. أرسل
ciphertext(AES-GCM-256،v1.<iv>.<ct>) معenc_version: 1. إرسالpayloadيُرفض، ولا يُقبل بصمت. - عليك أن تضيف المفتاح بنفسك. لا يحتوي
secret_urlالذي نعيده على أي مفتاح، ولا يمكن أن يحتويه، لأننا لم نرَ المفتاح قط. أضف#k=<base64url key>. لا ترسل المتصفحات أبدًا الجزء الذي يلي علامة#، فيفك المستلم التشفير على جهازه. - لا يوجد استرجاع. إذا فقدت المفتاح ضاع السر، عليك وعلينا. هكذا يعمل الضمان.
- يُتلف عند الحذف بعد القراءة أو انتهاء الصلاحية أو الإبطال. نكتب فوق النص المشفر المخزن على الخادم.
- لا تعيده الواجهة أبدًا. لا يوجد مسار يعيد المحتوى أو النص المشفر. القارئ الوحيد هو صفحة الإظهار لدى المستلم، وهي تسجل المشاهدة.
- عبارات المرور (في خطة Pro وما فوقها) عامل ثانٍ حقيقي: تُقوّى باستخدام PBKDF2-SHA256 وتُدمج في المفتاح باستخدام HKDF، فلا الرابط وحده ولا عبارة المرور وحدها تكفي لفك التشفير. أرسل
passphrase_hashفقط، وهو قيمة تجزئة SHA-256 بالصيغة الست عشرية لعبارة المرور بعد توحيدها بصيغة NFKC. لا ترسل عبارة المرور الأصلية أبدًا، فهي نصف المفتاح. اشتق المفتاح من صيغة NFKC نفسها، حتى تفتح عبارة المرور السر إذا كُتبت على لوحة مفاتيح أخرى. - ما لا يغطيه. من يملك الرابط كاملًا يملك المفتاح، لذلك يبقى قِصر مدة الصلاحية والحذف بعد القراءة مهمين. تُشفَّر المرفقات بالطريقة نفسها، بمفتاح مشتق من الرابط نفسه.
صيغة البيانات المتبادلة موصوفة في مرجع API، وتنفذ @uselinkpilot/sdk العملية كلها في استدعاء واحد إن لم ترغب في كتابتها بنفسك. والنموذج نفسه موصوف في صفحة بنية الأمان.
5. حدود الخطط وحدود معدل الطلبات
| الحد | Free | Pro | Agency |
|---|---|---|---|
| الروابط | 50 | غير محدود | غير محدود |
| الأسرار النشطة | 5 | 100 | 100 |
| أسرار بعبارة مرور | لا | نعم | نعم |
| نطاقات مخصصة للروابط | النطاقات الموثقة الحالية | حتى 5 | غير محدود |
| الطلبات لكل مفتاح في الساعة | 60 | 1,000 | 5,000 |
تطبق الواجهة قواعد الخطة نفسها المطبقة في لوحة التحكم وتقرؤها من المصدر نفسه، فيسري هنا أيضًا تجميع حدود الوكالة. يعيد GET /me الأرقام الحالية لمساحة عملك. وتحمل كل استجابة مصادَق عليها الترويسات X-RateLimit-Limit و X-RateLimit-Remaining و X-RateLimit-Reset.
6. مسارات عامة بلا مفتاح
يوجد POST /public/links و POST /public/secrets للعملاء الذين لا يستطيعون عرض اختبار تحقق في المتصفح: أدوات CLI وخوادم MCP والمشغّل. يأخذ كلاهما client_id مُبهمًا (من 16 إلى 64 حرفًا، يُولَّد مرة واحدة لكل تثبيت)، وينشئ سجلات في المجموعة المجهولة المصدر نفسها التي تستخدمها أدوات الويب المجانية.
- 20 عملية إنشاء لكل عميل في اليوم و60 لكل شبكة في الساعة، إضافة إلى حدود هذه المجموعة نفسها لكل شبكة.
- الفحص نفسه بمصادر التهديدات، والاستعلام نفسه عن التهديدات في عناوين URL لدى Google، وإعادة الفحص المجدولة نفسها كما في كل طرق الإنشاء الأخرى.
- لا نطاق مخصص ولا نهاية مخصصة ولا عبارة مرور. الأسرار العامة تُحذف نهائيًا دائمًا بعد قراءة واحدة، وتنتهي صلاحيتها خلال 24 ساعة.
- تتضمن الاستجابة
claim_tokenحتى يتمكن مستخدم سجّل الدخول لاحقًا من ضمّ السجل إلى مساحة عمل. تعامل معه كما تتعامل مع بيانات الدخول.
7. الأخطاء
كل خطأ يأتي بالشكل { "error": { "code", "message", "upgrade_url"? } }. تذكر الرسائل اسم الحقل، ولا تذكر بياناتك أبدًا. الموارد الموجودة في مساحات عمل أخرى تعيد 404، لا 403.
| الرمز | HTTP | متى |
|---|---|---|
| unauthorized | 401 | مفتاح مفقود أو مشوه أو غير معروف أو مُبطل أو منتهي الصلاحية |
| disabled | 503 | الواجهة غير مفعّلة لهذا النشر بعد |
| invalid_request | 400 | فشل التحقق من البيانات، أو وجهة محظورة، أو نهاية رابط مستخدمة، أو نطاق غير معروف |
| not_found | 404 | المسار غير موجود، أو المورد ليس في مساحة عملك |
| plan_limit | 402 | بلوغ حصة الروابط أو الأسرار النشطة (مع upgrade_url) |
| pro_required | 402 | عبارة مرور في خطة لا تدعمها (مع upgrade_url) |
| rate_limited | 429 | نفاد حصة المفتاح في الساعة أو الحصة العامة (مع Retry-After) |
أسئلة يطرحها المطورون
- أي الخطط تشمل الواجهة؟
- تُنشأ مفاتيح API من لوحة التحكم في /app/api-keys. ويستخدم كل مفتاح حدود خطة مساحة العمل التي ينتمي إليها: خطة Free تتيح 50 رابطًا و5 أسرار نشطة و60 طلبًا في الساعة، وخطة Pro تتيح روابط غير محدودة و100 سر نشط وأسرارًا محمية بعبارة مرور و1,000 طلب في الساعة، وتحصل خطط Agency على 5,000 طلب في الساعة.
- هل محتوى الأسرار مشفر من طرف إلى طرف؟
- نعم، والواجهة لا تقبل النص غير المشفر أصلًا. تشفّر أنت باستخدام AES-GCM-256 وترسل النص المشفر، ويخزن LinkPilot كتلة بيانات لا يملك مفتاحها ولا يستطيع فك تشفيرها. ولأننا لا نرى مفتاحك أبدًا، لا يحتوي secret_url الذي نعيده على أي مفتاح: تضيفه أنت بنفسك في صورة جزء #k= في آخر الرابط، ولا ترسل المتصفحات هذا الجزء أبدًا. إذا فقدت المفتاح فلن يستطيع أحد استرجاع السر، ولا نحن. تُشفَّر المرفقات بالطريقة نفسها، بمفتاح مشتق من الرابط نفسه.
- هل أستطيع إنشاء روابط بلا مفتاح API؟
- نعم. يأخذ POST /public/links و POST /public/secrets معرّف client_id مُبهمًا بدل المفتاح، وينشئان سجلات في المجموعة المجهولة المصدر نفسها التي تستخدمها أدوات الويب المجانية: 20 عملية إنشاء لكل عميل في اليوم، و60 لكل شبكة في الساعة، بلا نطاق مخصص ولا عبارة مرور، وتنتهي صلاحية الأسرار خلال 24 ساعة.
- ماذا يحدث عندما أبلغ حدًا؟
- تعيد حدود الخطة HTTP 402 مع الرمز plan_limit أو pro_required ومعه upgrade_url. وتعيد حدود معدل الطلبات 429 مع الترويسة Retry-After. وتحمل كل استجابة مصادَق عليها X-RateLimit-Limit و X-RateLimit-Remaining و X-RateLimit-Reset، حتى يبطئ العميل طلباته قبل أن يحدث ذلك.
- هل الواجهة مستقرة؟
- لن تتغير مسارات v1 بطريقة تكسر التوافق، وأي تغيير يكسر التوافق يصدر في إصدار جديد. خلال فترة الوصول المبكر نفعّل نقاط النهاية مساحة عمل تلو الأخرى، والاستجابة 503 مع الرمز disabled تعني أن الواجهة لم تُفعَّل لمساحة عملك بعد.
هل أنت جاهز للبدء؟
أنشئ مفتاحًا في مساحة عملك، أو ابدأ بخطة Free.