تشغيل خادم بلا واجهة
شغّل cc_server على جهاز يبقى يعمل، ثم اتصل به من الحاسوب أو المتصفح أو
الهاتف. الخادم ثنائي Dart خالص — بلا محرّك Flutter — فيعمل بلا واجهة على
macOS أو Linux أو Windows. يملك قاعدة البيانات، ويشغّل الوكلاء، ويجري كل
استدعاء لواجهة برمجة خارجية، والعملاء يقتصرون على الرسم.
التشغيل في Docker (صور مبنية مسبقًا)
قسم بعنوان “التشغيل في Docker (صور مبنية مسبقًا)”ينشر كل إصدار مكدس استضافة ذاتية من أربع صور على GHCR — الخلفية، والعميلان
الخفيفان الثابتان، ومرحّل الاقتران الاختياري — وصورة cc-server-demo منفصلة
لمضيف العرض العام. التثبيت بالاستضافة الذاتية لا يحتاج استنساخ المصدر. ثبّت
وسم إصدار (ghcr.io/samuelalev/<image>:v0.0.1-rc.1) أو ابقَ على latest.
| الصورة | ما هي | ما ينشره |
|---|---|---|
ghcr.io/samuelalev/cc-server |
الخلفية: قاعدة البيانات، والوكلاء، وMCP، وخطافات الويب | -p 9030:9030 |
ghcr.io/samuelalev/cc-webapp |
عميل الويب (nginx ثابت) | -p 8080:8080 |
ghcr.io/samuelalev/cc-remote |
تطبيق الويب التقدّمي المرافق للهاتف (nginx ثابت) | -p 8081:8080 |
ghcr.io/samuelalev/cc-signaling-server |
مرحّل اقتران WebRTC (اختياري) | -p 8788:8788 |
المكدس كله في ملف compose:
services: server: image: ghcr.io/samuelalev/cc-server:latest ports: ["9030:9030"] volumes: ["cc_data:/data"] restart: unless-stopped web: image: ghcr.io/samuelalev/cc-webapp:latest ports: ["8080:8080"] restart: unless-stopped remote: image: ghcr.io/samuelalev/cc-remote:latest ports: ["8081:8080"] restart: unless-stopped signaling: image: ghcr.io/samuelalev/cc-signaling-server:latest ports: ["8788:8788"] restart: unless-stopped
volumes: cc_data:ما يستحق معرفته قبل أن تكشف أيًا من هذا خارج loopback:
-
صورة
cc-serverتُشحن وفيهاCC_SERVER_INSECURE=1(لا بد أن تربط0.0.0.0لتستقبل حركة المنافذ المنشورة)، وهذا لا يكون آمنًا إلا خلف بروكسي عكسي ينهي TLS — Caddy أو Traefik أو nginx أو Cloudflare Tunnel — على شبكة موثوقة. لإنهاء TLS داخل العملية، اربط شهادة كوحدة تخزين واضبطCC_SERVER_TLS_CERT/CC_SERVER_TLS_KEY(ثم أزلCC_SERVER_INSECURE). كل ذلك قابل للتجاوز بـdocker run -e …أو بكتلةenvironment:في compose. -
/dataهي الحالة الوحيدة. قواعد SQLite، وأسرار الأجهزة المقترنة، والنماذج المنزَّلة، وصوت الاجتماعات، كلها في وحدة التخزين. انسخها احتياطيًا فتصير الحاوية قابلة للاستبدال. -
اقرن عميلًا من داخل الصورة قبل أول اتصال، وهو في الحاوية ما يقابل الخطوة 3:
نافذة طرفيّة docker run --rm -it -v cc_data:/data \--entrypoint /app/bin/cc_server ghcr.io/samuelalev/cc-server:latest pair -
صورتا العميلين مضيفتان nginx ثابتتان — لا شيء يُضبط وقت التشغيل؛ يأخذ نموذج الاتصال لدى كل عميل عنوان URL لخادمك ومعرّف الجهاز ومفتاح الاقتران. مرحّل الإشارات كذلك لا يحتاج إلا منفذه (
SIGNALING_HOST/SIGNALING_PORTقابلان للتجاوز)؛ وهو بلا حالة، ولا يحمل أسرارًا، وهو احتياط — يستخدم الهاتف loopback أو LAN أو tailnet أو مسارwss://مباشرًا حين يمكن بلوغ أحدها، ولا يعود إلى المرحّل إلا حين لا يبلغ أيًا منها. تخطَّه إن كان عملاؤك يبلغون الخادم مباشرة دائمًا.
بقية الدليل تبني الخادم من المصدر. ما يغطيه من أعلام وسلوك هو نفسه ما تغذّيه متغيرات بيئة الصورة.
المتطلبات
قسم بعنوان “المتطلبات”- Dart SDK. يقيّد هذا المستودع إصدار SDK عبر fvm، فأسبق كل أمر بـ
fvm. - نسخة محلية من هذا المستودع.
- سلسلة أدوات البناء الأصيلة التي يحتاجها سكربت التجهيز — Rust/
cargo، معmesonوninjaوpkg-configوgitومترجم C++. يسمّي كل سكربت بناء ما لم يجده وكيف يُثبَّت. - مضيف يمكن بلوغه: loopback للاستخدام المحلي، أو عنوان على LAN أو عنوان عام مع TLS للعملاء البعيدين.
الخطوة 1: تجهيز المكتبات الأصيلة
قسم بعنوان “الخطوة 1: تجهيز المكتبات الأصيلة”المكتبات الأصيلة مطلوبة — لا وضع منقوص. ابنِها من جذر المستودع قبل أي شيء آخر:
scripts/natives/build_natives.shيبني هذا rift وfff وtree-sitter مع قواعده النحوية، وaec وlame
وccpty وcc_watcher وcc_inference ومكتبة SAML الأصيلة، داخل
build/natives/. خطاف بناء cc_server يعيد تضمين ما جُهّز هناك في الحزمة،
فتجاوز هذه الخطوة إما يُفشل البناء مباشرة، أو — مع المخرج أدناه — ينتج ثنائيًا
يرفض فحص ما قبل الإقلاع تشغيله ويسمّي المكتبة الناقصة.
الخطوة 2: بناء الثنائي
قسم بعنوان “الخطوة 2: بناء الثنائي”cd apps/cc_serverfvm dart build cliتُشحن الحزمة ومعها libsqlite3 والمكتبات الأصيلة المجهّزة إلى جانب الثنائي،
فلا حاجة إلى SQLite من النظام ولا إلى محرّك Flutter. يستقر الملف التنفيذي في
./build/cli/<os_arch>/bundle/bin/cc_server.
الخطوة 3: اقتران عميل قبل أول تشغيل
قسم بعنوان “الخطوة 3: اقتران عميل قبل أول تشغيل”دليل البيانات الجديد بلا جهاز مقترن وبلا مفتاح مشترك مسبقًا، فلا يمكن بعد
تلبية مطالبة مفتاح الاقتران في العميل. صُكّ واحدًا بالأمر الفرعي pair. يكتب
global.db وsecrets.json مباشرة، والخادم الذي يقدّم دليل البيانات هذا
أصلًا يلتقط الجهاز الجديد من غير إعادة تشغيل:
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030يطبع ثلاث قيم تلصقها في نموذج اتصال العميل:
Connect a thin client (paste into its connect form): Server ws://localhost:9030/rpc Device id web-client Pairing key <a freshly generated key>أعلام مفيدة: --device (الافتراضي web-client؛ والمعرّف يختار أيضًا فئة
الجهاز — desktop وios وandroid، وأي شيء آخر عميل ويب)، و--label
(افتراضه اسم المنصة مع اسم مضيف هذا الجهاز)، و--host (المضيف الذي يُطبع في
العنوان، لأن loopback لا يُبلَغ من جهاز آخر)، و--client-url. إعادة تشغيل
pair تدور مفتاح ذلك الجهاز وتُسقط جلساته الحيّة. الاقتران لا ينشئ مساحة عمل
أبدًا: الإعداد الأولي للعميل المتصل هو الذي يسمّي الأولى وينشئها.
مع --client-url يطبع أيضًا رمز QR يُمسح من الطرفية، ويفتح عميل الويب وقد
مُلئت حقوله:
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030 \ --bind any --host 192.168.1.42 --client-url https://app.usectrl.devبيانات الاعتماد التي يصكّها cc_server pair تبقى بلا انتهاء صلاحية، والتي
تُصك في التطبيق تنتهي دائمًا بعد 30 يومًا. أبطل الجهاز الذي صُكّ من سطر الأوامر
من الإعدادات ← أنت ← أجهزتك حين تنتهي منه.
الخطوة 4: تشغيل الخادم
قسم بعنوان “الخطوة 4: تشغيل الخادم”على loopback — الافتراضي، وكل ما يلزم لعميل على الجهاز نفسه:
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030لبلوغه من جهاز آخر، اربط كل الواجهات. ربط غير loopback من غير TLS يُرفض عند
الإقلاع (Refusing to bind non-loopback address … without TLS)، فقدّم شهادة:
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \ --bind any --tls-cert /path/fullchain.pem --tls-key /path/privkey.pem \ --public-url wss://server.example:9030/rpcأو أنهِ TLS في بروكسي عكسي على شبكة خاصة موثوقة، واختر الربط بنص واضح خلفه:
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \ --bind any --insecure --public-url wss://server.example/rpcيُتجاهل --insecure حين يُضبط --tls-cert و--tls-key معًا — TLS يغلب
دائمًا. يعلن الخادم حالة النص الواضح على أنها insecure: true في
GET /healthz، ويضع العملاء على هذا الاتصال شارة غير آمن.
يبقى الخادم يعمل حتى SIGINT/SIGTERM، ثم يتوقف بإيقاف نظيف.
الخطوة 5: اتصال عميل
قسم بعنوان “الخطوة 5: اتصال عميل”وجّه عميلًا إلى عنوان URL للخادم ومفتاح الاقتران من الخطوة 3:
- تطبيق الحاسوب: في شاشة أول تشغيل (كيف ينبغي أن يعمل Control Center؟) املأ حقول الاتصال بدل التشغيل داخل هذا التطبيق، أو أضف الخادم لاحقًا من الإعدادات ← الخادم ← الاتصال والحالة. انظر الاتصال بخادم بعيد.
- عميل الويب: افتح app.usectrl.dev (أو نسختك من حزمة الويب) وأدخل عنوان URL للخادم ومعرّف الجهاز ومفتاح الاقتران.
- الهاتف: اقرن تطبيق Remote من عميل يعمل — انظر اقتران جهاز.
الخطوة 6: ربط Google Calendar (اختياري)
قسم بعنوان “الخطوة 6: ربط Google Calendar (اختياري)”لمزامنة تقويم مساحة عمل بلا واجهة رسومية، استخدم تدفق رمز الجهاز:
GOOGLE_OAUTH_CLIENT_ID=<id> GOOGLE_OAUTH_CLIENT_SECRET=<secret> \ ./build/cli/<os_arch>/bundle/bin/cc_server calendar connect \ --data-dir ./data --workspace <workspace-id>يطبع رمزًا وعنوان URL للموافقة على جهاز آخر، ويخزّن رمز التحديث على الخادم ثم
يخرج. بناء المصدر يُشحن بلا عميل Google، فمتغيرا البيئة للمعرّف والسر مطلوبان
— ولا يوجد علم --google-client-id. انظر
ربط Google Calendar.
الضبط
قسم بعنوان “الضبط”الأعلام المستخدمة أعلاه هي ما يحتاجه الإعداد الأول. السطح الكامل — 23 زوجًا،
كل منها علم ومتغير بيئة، والأوامر الفرعية الأربعة، وتخطيط دليل البيانات — في
مرجع واجهة سطر أوامر cc_server.
الأربعة التي ستحتاجها في الغالب:
| العلم | الافتراضي | المعنى |
|---|---|---|
--data-dir |
دليل بيانات التطبيق لكل مستخدم في نظام التشغيل | قواعد البيانات، والأسرار، والنماذج، والوسائط المخزَّنة مؤقتًا |
--port |
9030 |
منفذ TCP (0 = مؤقت) |
--bind |
loopback |
loopback، أو any لكل واجهة (يحتاج TLS أو --insecure) |
--public-url |
يُشتق من الربط | عنوان RPC المُعلَن للعملاء المقترنين. اضبطه صراحة خلف بروكسي أو NAT أو نفق |
دليل البيانات الافتراضي هو ~/Library/Application Support/control-center على
macOS، و%APPDATA%\control-center على Windows، و$XDG_DATA_HOME/control-center
(وإلا ~/.local/share/control-center) في سواهما. لا يُستخدم .cc_server
النسبي إلى دليل العمل الحالي إلا إذا لم يُعثر على دليل المنزل ولا على دليل
بيانات التطبيق.
ما يقدّمه الخادم
قسم بعنوان “ما يقدّمه الخادم”cc_server هو سطح المنتج كله، لا شريحة منه: مسار بيانات repo-RPC والاشتراكات،
ونقطة نهاية أدوات MCP عند POST /mcp وGET /sse على المنفذ نفسه، وخطافات
الويب الواردة، وبروكسيات الوسائط والخطوط، واستدعاءات تسجيل الدخول الموحّد
الراجعة، وبروتوكول إيجار الأسطول، وجلب RSS من جهة الخادم بجدول كل 30 دقيقة،
والبحث المتجهي (تُسجَّل إضافة sqlite_vector إضافةً تلقائية على نطاق العملية،
فيتراجع إلى البحث بالنص الكامل وحده حيث لا تتوفر الإضافة).
الموزّع بلا حالة: لا ربط لمساحة عمل على مستوى الجلسة. كل استدعاء في نطاق
مساحة عمل يحمل workspace_id الخاص به، ويُحسم الوصول بحسب عضوية المستدعي في
مساحة العمل ودوره. حيازة مفتاح الاقتران ليست حد الوصول.
أمران قبل أن تكشف المنفذ خارج loopback:
- MCP يفشل مغلقًا خارج المضيف. سطح MCP بلا رمز لا يجيب إلا على loopback. ربط
anyلا يوسّعه — فيُرفض الطلب القادم من خارج المضيف بالرسالة «MCP requires a bearer token to answer non-loopback clients» إلى أن تضبط رمزًا (mcp.setToken، أو الإعدادات ← الخادم ← خوادم MCP). GET /healthzبلا مصادقة عن قصد، مع CORS عام، وينشر معرّف الخادم واسمه وبصمة هويته وإصدار البناء. يحتاج العملاء البصمة ليثبّتوا الخادم، فالإفصاح مقصود، ويعني أن أيcc_serverيمكن بلوغه يكشف هذا القدر لمن يسأل.