
Seal v0.2.3
دردشة مشفرة من نظير إلى نظير ومن الطرف إلى الطرف. لا صندوق وارد. لا حساب لاستعادته. لا أحد يستمع — ولا حتى نحن.
Seal
دردشة من نظير إلى نظير، مشفّرة من الطرف إلى الطرف.
لا صندوق وارد. لا حساب لاستعادته. لا أحد يستمع — ولا حتى نحن.
تنتقل الرسائل مباشرة بين النظائر عبر libp2p وتُشفَّر باستخدام بروتوكولي Olm/Megolm بنمط Signal (عبر vodozemac) قبل أن تغادر جهازك أصلًا. الخادم الوحيد المعني هو دليل صغير يساعد النظائر على العثور على عنوان كلٍّ منها الحالي. لا يطّلع أبدًا على محتوى الرسائل، ويمكن مسحه بأمر واحد.
راجع docs/THREAT_MODEL.md و
docs/SECURITY.md لمعرفة ما هو محمي فعليًا وضد ماذا وكيف.
المحتويات
- لقطات الشاشة
- الميزات
- كيف يعمل
- بنية المشروع
- 1. المتطلبات الأساسية
- 2. البناء
- 3. التشغيل في وضع التطوير
- 4. الاختبار
- 5. إعداد الخلفية (خادم الدليل)
- 6. استخدام التطبيق
لقطات الشاشة
أول تشغيل — اختر اسمًا؛ لا حاجة لإعداد أي شيء آخر.
المحادثات — شريط المجموعات، وقائمة جهات الاتصال، ولوحة دردشة مشفّرة من الطرف إلى الطرف.
الإعدادات — حساسية الميكروفون، والضغط للتحدث، والتشغيل عند تسجيل الدخول، وقابلية الوصول عبر الشبكة.
الميزات
- مشفّرة من الطرف إلى الطرف، دائمًا — كل رسالة تُختَم بـ Olm (1:1) أو Megolm (للمجموعات) قبل أن تغادر جهازك أصلًا، باستخدام مخطط vodozemac بنمط Double-Ratchet: كل رسالة تحصل على مفتاحها الخاص.
- لا صندوق وارد، أبدًا — تنتقل الرسائل عبر اتصال مباشر بين النظائر (libp2p: QUIC/TCP + Noise، مع الترحيل وثقب النفق (hole-punching) للتعامل مع NAT). إذا كان المستلم غير متصل، تنتظر الرسالة محليًا وتُعاد المحاولة — ولا تُدرَج أبدًا في قائمة انتظار على بنية أي طرف آخر.
- دليل، لا قاعدة بيانات — الخادم الوحيد المعني
(
crates/directory-server) يربط معرّف المستخدم بعنوان شبكة حالي ولا شيء غير ذلك. وهو غير قادر بنيويًا على قراءة محتوى الرسائل: حتى ملفCargo.tomlالخاص به لا يعتمد على الكريتات التي تعرف كيفية القيام بذلك. - حسابات متعددة على جهاز واحد — هويات منفصلة تمامًا (مفاتيح، جهات اتصال، رسائل) يمكنك التنقل بينها دون إعادة تشغيل.
- مجموعات مع تغييرات حقيقية في العضوية — قنوات نصية وصوتية لكل مجموعة؛ إزالة شخص ما تُدير مفتاح المجموعة بحيث لا يمكنه قراءة أي شيء يُرسَل بعد ذلك.
- صوت مدمج — الضغط للتحدث عبر اختصار على مستوى النظام (يعمل من أي تطبيق، وليس Seal فقط)، وحساسية ميكروفون قابلة للضبط، ومبدّل صوت اختياري.
- مرفقات بلا بيانات وصفية — تُزال بيانات EXIF (موقع GPS، ومعلومات الكاميرا/الجهاز) من الصور قبل إرسالها، وهي مفعّلة افتراضيًا.
- زر طوارئ حقيقي — الإعدادات ← البيانات والخصوصية تحذف فورًا وبشكل لا رجعة فيه كل مفتاح وجهة اتصال ورسالة على هذا الجهاز، دون أي تأثير على أي شخص تواصلت معه.
- التشغيل عند تسجيل الدخول، إن أردت — مفعّل افتراضيًا، ويمكن إيقافه بمفتاح من الإعدادات.
- قاعدة كود واحدة، ثلاثة أنظمة — نوافذ أصلية على macOS وWindows و Linux عبر Tauri.
كيف يعمل
هناك نوعان من الهوية في هذا التطبيق، وهما منفصلان عمدًا:
- هوية الدردشة الخاصة بك هي زوج مفاتيح Ed25519/Curve25519 يُنشأ محليًا
عبر vodozemac في أول مرة
تفتح فيها التطبيق (
identity::Identity). "معرّف المستخدم" العام لديك هو مجرد بصمة ذلك المفتاح (wire_proto::user_id_from_ed25519). لا يمكن لأي خادم إصداره أو إبطاله، لأنه لا يوجد خادم مشارك في إنشائه. - هوية الشبكة الخاصة بك هي زوج مفاتيح libp2p منفصل (
PeerId)، يُستخدم فقط لطبقة النقل. يمكن أن يتغير عبر عمليات إعادة التشغيل دون أن يؤثر على هوية الدردشة إطلاقًا؛ ويرتبط الاثنان فقط بسجل حضور توقّعه بنفسك.
العثور على شخص ما والتحدث معه فعليًا خطوتان مختلفتان:``` ┌────────────────────────┐ │ directory server │ │ (axum + one SQLite │ │ file: users, │ │ presence, group │ │ rosters. Never │ │ message content.) │ └─────────┬───────────────┘ 1. "where is bob │ 2. "here's my current right now?" │ address" (signed, │ expires in minutes) ┌─────────┴───────────────┐ ▼ ▼ ┌───────┐ 3. direct libp2p ┌───────┐ │ alice │◄──── connection ────►│ bob │ └───────┘ (Noise + Olm/ └───────┘ Megolm encrypted)
1. تبحث أليس عن بوب في الدليل باستخدام معرف المستخدم الخاص به. ويعيد هذا
مفاتيحه العامة وعنوان شبكته الذي أُعلن عنه آخر مرة. وهذا هو كل ما
يحتويه الدليل: المفاتيح العامة، وأسماء العرض، وقوائم
العضوية في المجموعات، وإعلانات العناوين قصيرة الأجل
(`crates/directory-server`).
2. تتصل أليس ببوب مباشرة عبر libp2p (QUIC أو TCP+Noise، مع relay +
hole-punching للأقران خلف أجهزة NAT؛ انظر `crates/net`).
لم يعد للدليل أي دور من هنا فصاعدًا.
3. يتم تشفير الرسالة الفعلية باستخدام **Olm** للدردشة 1:1، أو
**Megolm** للمجموعة (`crates/crypto-session`)، وهو مخطط بأسلوب Double-Ratchet
حيث تحصل كل رسالة على مفتاحها الخاص، قبل وضعها على اتصال
libp2p هذا. لا يوجد صندوق وارد من جانب الخادم: إذا كان بوب غير متصل بالإنترنت،
تنتظر الرسالة محليًا ويُعاد المحاولة عليها، ولا تُخزَّن على
البنية التحتية لأي شخص آخر.
كل ما سبق يُنسَّق بواسطة `AppService` الخاص بـ `crates/core`، وهو ما
تستدعيه الواجهة الخلفية لتطبيق Tauri المكتوبة بلغة Rust (`apps/desktop/src-tauri`)؛
لا تتواصل واجهة المستخدم مع الشبكة مباشرة.
## تخطيط المشروع```
crates/
wire-proto shared signed-request types for the directory API
identity vodozemac identity, OS-keychain key management
storage local encrypted store (contacts, messages, groups)
net libp2p transport + directory HTTP client
crypto-session Olm (1:1) / Megolm (group) session management
core orchestrates the above into `AppService` / `ChatNode`
directory-server the one server component (axum + SQLite)
apps/desktop the Tauri + React app
scripts/ build + backend-deployment scripts (§2, §5)
1. المتطلبات الأساسية
تحتاج إلى Rust و Node.js على كل منصة، بالإضافة إلى سلسلة أدوات خاصة بالمنصة
يحتاجها Tauri لبناء نافذة أصلية. كما يقوم storage وdirectory-server
بتجميع SQLite من المصدر، الأمر الذي يتطلب مترجم C بسيطًا (لا
حاجة إلى OpenSSL أو أي مكتبة تشفير أصلية أخرى في أي مكان في هذا المشروع).
مشترك بين جميع المنصات:
macOS
```sh xcode-select --install ``` هذا كل شيء. توفر Xcode Command Line Tools كلاً من مترجم C و الأطر التي تحتاجها الواجهة الخلفية لنظام macOS في Tauri (القائمة على WKWebView).Linux
ثبّت مترجم C وpkg-config وحزم التطوير WebKitGTK/AppIndicator التي
ترتبط بها الواجهة الخلفية لنظام Linux في Tauri.
Debian/Ubuntu:```sh
sudo apt update
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file
libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev pkg-config
Fedora:```sh
sudo dnf install webkit2gtk4.1-devel openssl-devel curl wget file \
libappindicator-gtk3-devel librsvg2-devel pkgconf-pkg-config
sudo dnf group install "C Development Tools and Libraries"
Arch:```sh
sudo pacman -S --needed webkit2gtk-4.1 base-devel curl wget file openssl
appmenu-gtk-module libappindicator-gtk3 librsvg pkgconf
(تتغير أسماء الحزم بين إصدارات Tauri: إذا فشل البناء بحثًا عن ملف `.pc` مفقود، فراجع
[المتطلبات الأساسية الحالية لـ Tauri على لينكس](https://v2.tauri.app/start/prerequisites/)
لتوزيعتك.)
</details>
<details>
<summary><strong>Windows</strong></summary>
1. ثبّت **Microsoft C++ Build Tools** (Visual Studio Installer ← حمل عمل "تطوير سطح المكتب باستخدام C++")، وهي ضرورية لكلٍّ من غلاف Tauri الأصلي ولتجميع SQLite المضمّنة.
2. ثبّت سلسلة أدوات Rust الخاصة بـ **MSVC**: `rustup default stable-msvc`.
3. **WebView2**: موجود مسبقًا على ويندوز 11 ومعظم تثبيتات ويندوز 10 المحدّثة؛ إذا لم يكن موجودًا، فسيطالبك بناء Tauri بتثبيت وقت تشغيل Evergreen.
</details>
---
## 2. البناء
من جذر المستودع:```sh
# Rust workspace (backend crates + the directory server)
cargo build --workspace --release
# Frontend + the actual desktop app bundle (installer/.app/.exe)
cd apps/desktop
npm install
npm run tauri build
npm run tauri build يُنتج مثبّتًا أصليًا للمنصة في
target/release/bundle/ في جذر المستودع (هذه مساحة عمل Cargo؛ لذلك تشترك
جميع الكريتات، بما فيها تطبيق Tauri، في دليل target/ أعلى مستوى واحد).
الترجمة المتقاطعة (مثل بناء مثبّت Windows من macOS) غير مُعدّة:
قم بالبناء على كل منصة مستهدفة، أو استخدم سير عمل GitHub Actions الخاص بـ Tauri
إذا كنت تريد إصدارات مبنية عبر CI.
أو استخدم السكربتات
يحتوي scripts/ على سكربت بناء واحد لكل منصة/ناتج، كل منها قابل للتشغيل بشكل مستقل،
وتم التحقق من أنه يُنتج فعليًا ناتجًا يعمل:
| السكربت | المنتج |
|---|---|
scripts/build-mac-dmg.sh | مثبّت .dmg لنظام macOS |
scripts/build-mac-app.sh | حزمة .app macOS الخام، بدون مثبّت |
scripts/build-linux.sh | .AppImage + .deb لنظام Linux |
scripts/build-windows.ps1 | .msi + .exe لنظام Windows (NSIS) |
كل واحد منها يغلّف فقط npm run tauri build --bundles <...> مع العلامات الصحيحة
وفحص المنصة؛ شغّل الأمر الخام بنفسك إذا أردت مجموعة حزم مختلفة
(npx tauri build --help من apps/desktop).
scripts/release.sh vX.Y.Z يحدّث الإصدار في كل مكان يلزم
ويضع وسمًا على الالتزام — انظر docs/RELEASING.md. يعمل على
macOS وLinux؛ لا ينفّذ commit أو push.
دمج شبكة "Seal" الخاصة بك
شاشة اختيار الخادم (§3) تعرض دائمًا ثلاثة خيارات: Seal (شبكتك الرسمية الخاصة)، خادم مخصص، ورابط صغير خادم اختبار محلي في الأسفل. يكون خيار "Seal" معطّلًا (رماديًا، مع رسالة "غير مُعدّ في هذا الإصدار بعد") حتى تقوم بتضمين عنوان URL في وقت البناء:```sh SEAL_DEFAULT_DIRECTORY_URL=https://directory.example.com npm run tauri build
بمجرد أن تشغّل خادمك الخاص (§5) وتشير إلى نطاق حقيقي نحوه، اضبط هذا وأعد البناء: كل نسخة توزّعها من بعد ذلك ستُظهر "Seal" كخيار حقيقي قابل للتحديد باستخدام عنوان URL هذا، دون لمس أي كود آخر. اتركه غير مضبوط للإصدارات العادية/التطويرية: لا يوجد خادم رسمي مستضاف من هذا المستودع، لذا يبقى "Seal" معطَّلاً ويلجأ المستخدمون إلى خادم مخصص أو الخادم المحلي، بدلاً من أن يشير التطبيق بصمت إلى نطاق مؤقت لا يشغّل أي شيء فعليًا.
---
## 3. تشغيله في وضع التطوير```sh
cd apps/desktop
npm install
npm run tauri dev
يبدأ هذا خادم تطوير Vite، ويجمّع الواجهة الخلفية المكتوبة بـ Rust في وضع التصحيح، ويفتح نافذة أصلية مع إعادة تحميل سريعة للواجهة الأمامية. أول بناء يجمّع شجرة الاعتماديات كاملة ويستغرق بضع دقائق؛ أما التشغيلات اللاحقة فتكون سريعة.
اختيار الخادم (أول تشغيل)
في أول مرة تشغيل، يسأل Seal عن خادم الدليل الذي سيُستخدم، بالترتيب التالي:
- Seal: الشبكة الرسمية، إذا كان هذا البناء يتضمّنها مدمجة (انظر §2). معطّل حتى يتم ذلك؛ هذا المستودع لا يُشحن موجّهاً إلى نطاق مؤقت.
- خادم مخصص: خادم أي شخص، بما في ذلك خادمك الخاص (§5).
- خادم اختبار محلي: رابط صغير ومُخفَض الأهمية عمداً في الأسفل.
يشغّل الخادم المدمج الخاص بالتطبيق (يرتبط بـ
127.0.0.1:47100/47101، والبيانات تحت دليل بيانات التطبيق في نظام تشغيلك)، وهو مناسب لتجربة Seal أو اختبار الحالات على جهاز واحد، وليس نشراً حقيقياً. إذا وجدت نسخة ثانية أن تلك المنافذ مشغولة بالفعل، فإنها تعيد استخدام خادم النسخة الأولى بدلاً من تشغيل خادم آخر، وهذا ما يسمح لنسختين على جهاز واحد بأن تجد كلٌّ منهما الأخرى. وهذا ما يتم اختياره تلقائياً إذا لم يكن "Seal" مُعرّفاً ولم تختر أي شيء آخر.
يتم حفظ الاختيار (server.json بجوار البيانات المحلية الأخرى للتطبيق)
وإعادة استخدامه بصمت في كل تشغيل لاحق؛ غيّره من الإعدادات ← خادم الدليل،
ويسري التغيير في المرة التالية التي تشغّل فيها التطبيق بدلاً من
محاولة تبديل اتصال قيد التشغيل بشكل سريع. للاستخدام النصي/التطويري،
يتجاوز متغير بيئة الطلب بالكامل:```sh
P2P_CHAT_DIRECTORY_URL=https://directory.example.com npm run tauri dev
### تشغيل مثيلين محليًا (لاختبار المراسلة فعليًا)
يحتاج كل مثيل إلى هويته الخاصة. يدعم Seal حسابات متعددة
بشكل أصلي (الإعدادات → الحسابات على هذا الجهاز)، ولكن بالنسبة إلى *عمليتين
منفصلتين* على جهاز واحد، يُعد `P2P_CHAT_PROFILE` المسار الأسرع: فهو
ينشئ تلقائيًا (في المرة الأولى) أو يستأنف تلقائيًا (في كل مرة لاحقة) حسابًا
بهذا الاسم، دون تفاعل، متجاوزًا منتقي الحسابات تمامًا:```sh
# terminal 1
P2P_CHAT_PROFILE=alice npm run tauri dev
# terminal 2
P2P_CHAT_PROFILE=bob npm run tauri dev
اختيار الخادم (server.json) وقائمة الحسابات (accounts.json)
مشتركان بين العمليات على نفس الجهاز، وليس لكل ملف تعريف. أول
مثيل تشغّله يختار الخادم، وكل ملف تعريف بعد
ذلك (بما في ذلك bob هنا) يعيد استخدامه بصمت. تنتهي النافذتان على
نفس خادم الدليل المدمج، لذا يمكنك إضافة بعضكما كجهات اتصال بالمعرّف
والتواصل بينهما.
يحتاج خادم التطوير الخاص بـ Vite إلى منفذ حقيقي وثابت يشير إليه webview الخاص بـ Tauri،
مما يعني عادةً أنه يمكن تشغيل npm run tauri dev واحد فقط في كل مرة —
الثاني سيجد المنفذ 1420 محجوزًا بالفعل وسيفشل فورًا.
npm run tauri هو في الواقع غلاف صغير (apps/desktop/scripts/tauri.mjs)
يختار المنفذ الحر التالي (1421، 1422، …) لكل مثيل بعد
الأول ويوصّله تلقائيًا، لذا فإن تشغيل الأمرين أعلاه
في طرفيتين يعمل ببساطة؛ لا تحتاج إلى فعل أي شيء مختلف. إنه
يغيّر السلوك فقط لـ dev — npm run tauri build وكل شيء آخر
يمر مباشرةً عبر CLI الحقيقي.
الاختبار على بناء حقيقي (وليس وضع التطوير)```sh
./scripts/run-two-mac-instances.sh # profiles: alice, bob ./scripts/run-two-mac-instances.sh carol dave
نفس الفكرة أعلاه، لكنه يشغّل التطبيق المبني الفعلي (`build-mac-app.sh` /
مخرجات `build-mac-dmg.sh`، أو نسخة مثبتة في `/Applications`) مرتين
مع قيم `P2P_CHAT_PROFILE` مختلفة بدلاً من `npm run tauri dev`، أقرب
إلى ما يشغّله مستخدم حقيقي. يطبع معرفات العمليات (PIDs) وكيفية إيقاف كليهما.
### التصحيح
- **سجلات Rust**: اضبط `RUST_LOG` قبل الإطلاق، على سبيل المثال:
`RUST_LOG=debug npm run tauri dev` (أو `RUST_LOG=p2p_core=debug,net=debug`
لتضييق نطاقه). الحقول المُسجَّلة محدودة بالبيانات الوصفية (النظير/المجموعة/المستخدم
المعرفات، أنواع الأخطاء)؛ راجع [`docs/SECURITY.md`](https://github.com/emn4tor/seal/blob/HEAD/docs/SECURITY.md) لمعرفة السبب
في كون تركها مطوّلة آمن.
- **الواجهة الأمامية**: نافذة التطوير هي عرض ويب حقيقي؛ انقر بالزر الأيمن → فحص
العنصر (أو افتح أدوات المطوّر) يعمل مثل متصفح عادي.
- **crates الواجهة الخلفية بشكل معزول**: كل crate لديه مجموعة اختبارات خاصة يمكنك
تشغيلها والتكرار عليها دون لمس الواجهة إطلاقًا؛ راجع §4.
- **خادم دليل مستقل**، بدلاً من الخادم المدمج: راجع §5.
---
## 4. الاختبار```sh
# everything
cargo test --workspace
# one crate, e.g. the full backend-to-backend flow a Tauri command would trigger
cargo test -p p2p-core --test app_service
# lint + format check (what CI runs)
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
# dependency vulnerability scan
cargo install cargo-audit --locked # once
cargo audit
# frontend type-check + build
cd apps/desktop && npm run build
5. إعداد الواجهة الخلفية (خادم الدليل)
ملخص لما هو هذا فعليًا، لأنه من السهل المبالغة في التخيّل: عملية axum واحدة، ملف SQLite واحد، ثلاثة أنواع من السجلات (المفاتيح العامة، إعلانات الحضور قصيرة العمر، قوائم المجموعات)، وجميع عمليات الكتابة موقّعة بمفتاح هوية المتصل نفسه. لا يكون هذا الخادم أبدًا في مسار الرسالة. انظر docs/THREAT_MODEL.md لمعرفة سبب صحة ذلك بنيويًا، وليس فقط كسياسة: إنّ Cargo.toml الخاص بـ directory-server لا يعتمد حتى على الحِزم (crates) التي تعرف كيفية قراءة محتوى الرسالة.
أسرع مسار: سكربت الإعداد```sh
sudo ./scripts/setup-backend.sh
تفاعلي، ويعمل على Linux و systemd فقط (انظر ترويسة السكريبت لمعرفة السبب). يطلب منك تحديد عائلة التوزيعة التي تستخدمها (Debian/Ubuntu أو Fedora/RHEL/Rocky/Alma أو Arch/Manjaro أو openSUSE، مع تعبئة مسبقة بتخمين من `/etc/os-release`، لذا عادةً ما يكون تأكيدًا بضغطة مفتاح واحدة) ويقوم بتثبيت متطلبات البناء الخاصة بتلك التوزيعة عبر دالة مخصصة لكل عائلة، ويعرض تثبيت Rust عبر `rustup` إذا كانت مفقودة، ويبني الملف التنفيذي للإصدار، وينشئ مستخدم نظام مخصصًا، ويولّد رمز مدير، ويسألك ما إذا كنت تريد أن يهيئ نطاقًا مع HTTPS تلقائي عبر [Caddy](https://caddyserver.com) (بتثبيت Caddy نفسه حسب التوزيعة، مع اللجوء إلى الملف الثنائي الرسمي الثابت لـ Caddy إذا لم تتوفر حزمة التوزيعة)، أو أن يكتفي بربط loopback/HTTP عادي إذا كنت تفضّل وضعه أمام خادم بنفسك، ثم يكتب ويفعّل خدمة systemd. من الآمن إعادة تشغيله.
كل ما يلي هو ما يفعله فعليًا، في حال كنت تفضّل القيام بذلك يدويًا أو فهمه قبل تشغيله.
### macOS: خادم اختبار سريع على الشبكة المحلية```sh
./scripts/run-mac-test-server.sh
ليس للاستضافة الفعلية: لاختبار التطبيق عبر جهازين على نفس الشبكة (مثل جهاز Mac الخاص بك + جهاز آخر، أو شخصين على نفس شبكة Wi-Fi) دون إعداد نطاق أو TLS أو systemd (الذي لا يوجد على macOS على أي حال). يبني الملف التنفيذي الثنائي للإصدار، ويُنشئ رمز مسؤول (يُعاد استخدامه في التشغيلات اللاحقة)، ويربط واجهة برمجة التطبيقات العامة بجميع الواجهات، ويطبع عنوان URL الذي يجب استخدامه: عنوان IP الفعلي لشبكة LAN المحلية لجهاز Mac (عبر ipconfig getifaddr)، وليس فقط 127.0.0.1، حتى تتمكن الأجهزة الأخرى من الوصول إليه أيضًا. تظل منفذ الإدارة مقتصرًا على الاسترجاع المحلي فقط. يعمل في المقدمة؛ Ctrl-C يوقفه. البيانات موجودة تحت ~/.seal-test-server.
تشغيل محلي سريع```sh
DIRECTORY_DB_PATH=/var/lib/seal-directory/directory.sqlite3
DIRECTORY_PUBLIC_ADDR=0.0.0.0:8080
DIRECTORY_ADMIN_ADDR=127.0.0.1:8090
DIRECTORY_ADMIN_TOKEN=$(openssl rand -hex 32)
cargo run --release -p directory-server --bin directory-server
| Variable | Required | Meaning |
|---|---|---|
| `DIRECTORY_DB_PATH` | لا (الافتراضي `directory.sqlite3`, cwd) | المكان الذي يوجد فيه ملف SQLite الوحيد. يجب أن يكون الدليل الأصلي موجودًا. |
| `DIRECTORY_PUBLIC_ADDR` | لا (الافتراضي `0.0.0.0:8080`) | واجهة برمجة تطبيقات الالتقاء التي تتواصل معها التطبيقات. لا بأس من تعريضها للعامة. |
| `DIRECTORY_ADMIN_ADDR` | لا (الافتراضي `127.0.0.1:8090`) | نقطة نهاية التطهير. أبقِها بعيدًا عن الإنترنت العام؛ انظر أدناه. |
| `DIRECTORY_ADMIN_TOKEN` | **نعم** | رمز Bearer لواجهة برمجة تطبيقات الإدارة. ترفض العملية البدء بدونه. أنشئه باستخدام `openssl rand -hex 32` أو ما شابه؛ لا تعِد استخدامه في أي مكان آخر. |
تسجّل العملية العناوين التي ارتبطت بها عند بدء التشغيل وتُحذّر بشدة إذا لم يكن
`DIRECTORY_ADMIN_ADDR` على loopback.
### توجيه التطبيق إليه
ثلاث طرق، بالترتيب الذي تلجأ إليه عادةً:
1. **شاشة التشغيل الأولى**: اختر "خادم مخصص" وأدخل عنوان URL. انظر §3.
2. **الإعدادات ← خادم الدليل**: يمكن تغييره لاحقًا؛ يصبح ساريًا عند
إعادة التشغيل التالية.
3. **`P2P_CHAT_DIRECTORY_URL`**، عند ضبطه قبل الإطلاق: يتخطى السؤال
تمامًا ويتجاوز أي قيمة محفوظة، وهو مفيد لتشغيلات التطوير أو التشغيل عبر السكربتات: ```sh
P2P_CHAT_DIRECTORY_URL=https://directory.example.com npm run tauri dev
كل من يريد أن يجد الآخرين يحتاج إلى الإشارة إلى نفس مثيل الدليل؛ فهذه هي الطريقة التي يجد بها كلٌّ منهم الآخر في المقام الأول.
تشغيله كخدمة حقيقية (systemd)
إظهار وحدة systemd + ملاحظات
```ini # /etc/systemd/system/seal-directory.service [Unit] Description=Seal directory server After=network.target[Service] Type=simple User=seal-directory Group=seal-directory Environment=DIRECTORY_DB_PATH=/var/lib/seal-directory/directory.sqlite3 Environment=DIRECTORY_PUBLIC_ADDR=127.0.0.1:8080 Environment=DIRECTORY_ADMIN_ADDR=127.0.0.1:8090 EnvironmentFile=/etc/seal-directory/admin-token.env ; DIRECTORY_ADMIN_TOKEN=... ExecStart=/usr/local/bin/directory-server Restart=on-failure
Sandboxing: this process needs almost nothing
ProtectSystem=strict ProtectHome=true PrivateTmp=true NoNewPrivileges=true ReadWritePaths=/var/lib/seal-directory
[Install] WantedBy=multi-user.target
ملاحظات:
- `DIRECTORY_PUBLIC_ADDR` مرتبط بـ **loopback** هنا عن قصد؛ ضع وكيلًا عكسيًا أمامه لتوفير TLS (بالأسفل) بدلًا من تعريض axum مباشرةً للإنترنت.
- أنشئ مستخدم/مجموعة النظام `seal-directory` و `/var/lib/seal-directory` أولاً (`useradd --system --no-create-home seal-directory && install -d -o seal-directory -g seal-directory /var/lib/seal-directory`)، ثم انسخ الثنائي `directory-server` المبني (من `target/release/`) إلى `/usr/local/bin/`.
- ضع رمز الإدارة في `EnvironmentFile` قابل للقراءة من الجذر فقط، وليس مباشرةً في ملف الوحدة (ملفات الوحدة غالبًا ما تكون قابلة للقراءة للجميع).
</details>
### TLS عبر وكيل عكسي
<details>
<summary>إظهار إعدادات Caddy / nginx</summary>
يمنحك [Caddy](https://caddyserver.com) HTTPS تلقائيًا بأقل إعداد:```
# /etc/caddy/Caddyfile
directory.example.com {
reverse_proxy 127.0.0.1:8080
}
caddy run (أو systemctl enable --now caddy) يتولى إصدار/تجديد الشهادات من تلقاء نفسه. إذا كنت تفضّل استخدام nginx، فأنهِ TLS هناك واستخدم proxy_pass http://127.0.0.1:8080;، لأن التطبيق يحتاج فقط إلى HTTP عادي من منظور البروكسي.
من ناحية جدار الحماية: يجب أن يكون المنفذ العام فقط قابلاً للوصول من الخارج (8080 في الأمثلة أعلاه، ويُقدَّم عبر المنفذ 443 بواسطة البروكسي). يجب ألا يكون منفذ الإدارة قابلاً للوصول من الخارج أبدًا؛ يمكنك الوصول إليه عبر إعادة توجيه منافذ SSH (ssh -L 8090:127.0.0.1:8090 your-server) عندما تحتاج إلى تنفيذ purge عن بُعد.
تنظيفه```sh
cargo run --release -p directory-server --bin directory-admin --
--admin-url http://127.0.0.1:8090 --token "$DIRECTORY_ADMIN_TOKEN" purge
هذا يحذف ملف SQLite ويعيد إنشاء مخطط فارغ: لا
عبارات `DELETE`، ولا حالة جزئية. من الآمن تشغيله دون تحذير أي شخص أولاً:
كل سجل فيه هو مخزن مؤقت لبيانات يمتلكها كل عميل محلياً بالفعل
(تسجيلهم الخاص، حضورهم، وأي قوائم مجموعات هم عضو
فيها)، لذا يقوم العملاء فقط بإعادة ملئها في غضون لحظات من إجراءهم التالي.
لا توجد سياسة نسخ احتياطي لقاعدة البيانات هذه عن قصد؛ انظر
[`docs/SECURITY.md`](https://github.com/emn4tor/seal/blob/HEAD/docs/SECURITY.md) لمعرفة سبب أن الاحتفاظ بواحدة من شأنه أن يقوض
الهدف برمته.
---
## 6. استخدام التطبيق
1. **الإطلاق الأول، السؤال الأول**: أي خادم دليل تستخدمه (§3).
الافتراضي هو ما هو مدمج في البناء الذي تشغّله (خادم اختبار محلي،
ما لم يكن من بناه قد هيّأ خادمًا رسميًا)؛ اختر
"خادم مخصص" للإشارة إلى خادم تستضيفه أنت أو شخص تثق به.
2. **اختر اسم عرض.** يولّد هذا زوج مفاتيح خاص على جهازك
(لا شيء لتتذكره، ولا شيء يمكن استعادته إذا فُقد: هذا مقصود)
ويأخذك في شرح قصير داخل التطبيق حول كيفية
عمل التشفير فعليًا. يمكنك إعادة عرضه في أي وقت من الإعدادات. كل تشغيل لاحق
يعيدك مباشرة دون أي مطالبة؛ يحدث هذا مرة واحدة فقط لكل
حساب. أضف المزيد من الحسابات (هويات منفصلة تمامًا) من الإعدادات →
الحسابات على هذا الجهاز، وبدّل بينها دون إعادة التشغيل.
3. **أضف شخصًا**: انقر **+** بجانب "الرسائل المباشرة" وأدخل
معرفهم (الموجود في *إعداداتهم* → هويتي). لا يوجد دليل لتصفحه
حسب التصميم؛ أنت تتواصل بنفس الطريقة التي تشارك بها رقم هاتف.
4. **راسلهم**: اختر اسمهم من القائمة واكتب. أول
رسالة إلى شخص ما تنشئ جلسة مشفرة تلقائيًا.
5. **ابدأ مجموعة**: انقر **+** على شريط الأيقونات، وسمِّها، ثم ادعُ
الأشخاص بالمعرف بنفس الطريقة. إزالة شخص ما يؤدي إلى تدوير مفتاح المجموعة بحيث
لا يمكنهم قراءة أي شيء يُرسل لاحقًا.
6. **احذف كل شيء**: الإعدادات → البيانات والخصوصية. هذا فوري،
محلي فقط، ولا رجعة فيه: إنه يدمر مفاتيحك وجهات اتصالك،
وسجلّك على *هذا الجهاز* وليس له أي تأثير على أي شخص تحدثت معه.