From c067fa9e7ee939c2a47e00077ed6c891ca4b89c0 Mon Sep 17 00:00:00 2001 From: sunny Date: Wed, 4 Nov 2026 12:30:23 +0800 Subject: [PATCH 1/2] docs(readme): synchronize multilingual documentation with English README --- docs/README.ar.md | 120 ++++++++++++++++++++++-- docs/README.es.md | 122 ++++++++++++++++++++++-- docs/README.fr.md | 216 +++++++++++++++++++++++++++++++------------ docs/README.ja.md | 194 +++++++++++++++++++++++++++++--------- docs/README.ru.md | 192 ++++++++++++++++++++++++++++---------- docs/README.zh-CN.md | 55 +++++++++-- docs/README.zh-TW.md | 124 +++++++++++++++++++++++-- 7 files changed, 835 insertions(+), 188 deletions(-) diff --git a/docs/README.ar.md b/docs/README.ar.md index 28035f53..0c4811d6 100644 --- a/docs/README.ar.md +++ b/docs/README.ar.md @@ -24,7 +24,7 @@ [English](../README.md) | **العربية** | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [Français](README.fr.md) | [Русский](README.ru.md) | [Español](README.es.md) | [日本語](README.ja.md) -Vocat هي لوحة تحكم ويب مفتوحة المصدر ومجموعة أدوات هندسية لمودمات Quectel الخلوية من فئة EC20/EC25. تجمع في خدمة واحدة مكتفية ذاتيًا بين اكتشاف المودم، وحالة الراديو المباشرة، وطرفيات AT وUSSD، والرسائل القصيرة SMS، وWiFi Calling، وإدارة eSIM، واختيار الشبكة، والتوجيه عبر البروكسي، والإشعارات، وسجلات التدقيق، وأتمتة الإصدارات. +Vocat هي لوحة تحكم ويب مفتوحة المصدر ومجموعة أدوات هندسية لمودمات Quectel الخلوية من فئة EC20/EC25 وسلسلة China Mobile ML307. تجمع في خدمة واحدة مكتفية ذاتيًا بين اكتشاف المودم، وحالة الراديو المباشرة، وطرفيات AT وUSSD، والرسائل القصيرة SMS، وWiFi Calling، وإدارة eSIM، واختيار الشبكة، والتوجيه عبر البروكسي، والإشعارات، وسجلات التدقيق، وأتمتة الإصدارات. الواجهة الخلفية مكتوبة بلغة Go، والواجهة مبنية باستخدام React وTypeScript، وتُضمَّن واجهة الإنتاج الأمامية داخل الملف الثنائي لـ Go. يحتوي ملف تنفيذي واحد على تطبيق الويب ويستخدم SQLite للحالة الدائمة. @@ -38,7 +38,7 @@ Vocat هي لوحة تحكم ويب مفتوحة المصدر ومجموعة أ | المجال | ما يوفره Vocat | | --- | --- | | إدارة الأجهزة | اكتشاف تلقائي عبر المنفذ التسلسلي/USB، دعم عدة مودمات، أسماء أجهزة مألوفة، تحديثات مباشرة للنظرة العامة، إعادة تشغيل الوحدة، وضع الطيران، وضوابط وضع شبكة USB. | -| الراديو والشبكة | حالة التسجيل، المشغّل، مقاييس الإشارة، RSRP/RSRQ/SINR، وضع الشبكة، النطاق، القناة، فحص المشغّلين، والاختيار التلقائي أو اليدوي للشبكة. | +| الراديو والشبكة | حالة التسجيل، المشغّل، مقاييس الإشارة، RSRP/RSRQ/SINR، وضع الشبكة، النطاق، القناة، فحص المشغّلين، الاختيار التلقائي أو اليدوي للشبكة، واستعلام خلايا LTE مع قفل/إلغاء قفل خلية واحدة (EARFCN/PCI). | | AT وUSSD | طرفية AT تفاعلية، سجل الأوامر، استجابات المودم الخام، تدفقات بدء/متابعة/إلغاء USSD، والإبلاغ الواضح عن أخطاء المودم. | | الرسائل القصيرة | إرسال مباشر للرسائل الخلوية ورسائل IMS، المزامنة الواردة، التعامل مع الرسائل متعددة الأجزاء، تقارير التسليم، سجل المحادثات، حالة عدم القراءة، الطوابع الزمنية، وحالة التسليم لكل رسالة. | | WiFi Calling | إنشاء نفق IKEv2/ePDG، مصادقة EAP-AKA، تسجيل IMS، رسائل IMS القصيرة، ضوابط إعادة الاتصال، تشخيصات الحالة، والتوجيه لكل جهاز. | @@ -47,7 +47,7 @@ Vocat هي لوحة تحكم ويب مفتوحة المصدر ومجموعة أ | التوجيه عبر البروكسي | توجيه SOCKS صاعد، ربط الأجهزة، قواعد الدول، فحوصات الوصول عبر TCP، وفحوصات UDP Associate لمسارات بيانات WiFi Calling. | | الإشعارات | إعادة توجيه الرسائل القصيرة الواردة الجديدة عبر Telegram وBark والبريد الإلكتروني وPushplus وwebhooks الموقّعة. يتم تسليم كل رسالة كإشعار منفصل. | | بوت Telegram | حالة الجهاز، قائمة الملفات الشخصية المثبتة وتبديلها، ضوابط WiFi Calling، وإرسال الرسائل القصيرة. تتطلب الإجراءات الحساسة تأكيد المسؤول. | -| العمليات | المصادقة، الحماية من CSRF، سياسات الوصول، أحداث التدقيق، السجلات المباشرة، الاحتفاظ بالسجلات، فحوصات الصحة، تخطيط متجاوب، الوضع الداكن، وواجهة مستخدم بالإنجليزية/الصينية. | +| العمليات | المصادقة بكلمة المرور وبـ SSO (مثل Pocket ID وGitHub وGeneric OAuth2)، الحماية من CSRF، سياسات الوصول، أحداث التدقيق، السجلات المباشرة، الاحتفاظ بالسجلات، فحوصات الصحة، تخطيط متجاوب، الوضع الداكن، وواجهة مستخدم بالإنجليزية/الصينية. | | التوزيع | ملفات Linux الثنائية الثابتة، سكربت تثبيت systemd، تحديث ذاتي مع التحقق من SHA-256، صورة Docker، النشر إلى GHCR، وبنى إصدارات GitHub Actions. | ## الأجهزة المدعومة @@ -58,6 +58,7 @@ Vocat هي لوحة تحكم ويب مفتوحة المصدر ومجموعة أ - Quectel EC25 - عائلة Quectel EG25 - وحدات EG600 المتوافقة وذات الصلة +- سلسلة China Mobile ML307 (مثل ML307A-E5 ومختلف المصنّعين) تعتمد الميزات المتاحة على برنامج الوحدة الثابت (firmware)، وتكوين USB، وقدرات SIM/eSIM، وتعريفات المضيف، والشبكة اللاسلكية، وإعدادات المشغّل. @@ -180,7 +181,21 @@ docker run -d \ افتح `http://:7575` بعد بدء الحاوية. يلزم استخدام شبكة المضيف حتى تبقى واجهات شبكة QMI مرئية لـ Vocat، كما يلزم الوصول بصلاحيات مرتفعة إلى الأجهزة لاستخدام المنافذ التسلسلية وعقد تحكم QMI وواجهات TUN وإعدادات الشبكة والأجهزة المضافة بعد بدء الحاوية. يتيح الربط المباشر لـ `/dev` ظهور عقد `ttyUSB*` و`ttyACM*` و`cdc-wdm*` الجديدة وعقد MHI من نمط `wwan*` دون إعادة إنشاء الحاوية. -يمنح هذا الوضع Vocat عمدًا وصولًا واسعًا إلى أجهزة المضيف ومكدس الشبكة. استخدمه فقط على مضيف Linux موثوق. يتعرّف الاكتشاف التلقائي على مودمات Quectel USB المدعومة (معرّف مورّد USB هو `2c7c`) ومودمات PCIe/MHI التي يتيحها نظام Linux WWAN الفرعي؛ ولا يتعرّف على جميع ترتيبات المودمات الممكنة. إن تعيين عقد فردية فقط باستخدام `--device`، مثل `/dev/ttyUSB2` أو `/dev/cdc-wdm0` أو `/dev/wwan0qmi0`، يحصر الحاوية في تلك العقد الثابتة ولا يوفّر اكتشافًا كاملًا لعدة أجهزة أو للأجهزة الموصولة أثناء التشغيل. +يمنح هذا الوضع Vocat عمدًا وصولًا واسعًا إلى أجهزة المضيف ومكدس الشبكة. استخدمه فقط على مضيف Linux موثوق. يتعرّف الاكتشاف التلقائي على مودمات Quectel USB المدعومة (معرّف مورّد USB هو `2c7c`)، ووحدات سلسلة China Mobile ML307 عبر سلسلة تعريف المنتج، ومودمات PCIe/MHI التي يتيحها نظام Linux WWAN الفرعي؛ ولا يتعرّف على جميع ترتيبات المودمات الممكنة. إن تعيين عقد فردية فقط باستخدام `--device`، مثل `/dev/ttyUSB2` أو `/dev/cdc-wdm0` أو `/dev/wwan0qmi0`, يحصر الحاوية في تلك العقد الثابتة ولا يوفّر اكتشافًا كاملًا لعدة أجهزة أو للأجهزة الموصولة أثناء التشغيل. + +على مضيفي OpenWrt، ترفض سلسلة input في fw4 الاتصالات الجديدة من الواجهات التي لا تديرها، بما في ذلك واجهة TUN الخاصة بـ VoWiFi لكل جلسة والتي ينشئها Vocat. لذلك، يُدرج Vocat قواعد قبول مستهدفة باسم `vocat-ims-*` (من P-CSCF إلى العنوان الداخلي المتفاوض عليه) باستخدام ثنائي `nft` المضمّن في الصورة. بدون هذه القواعد، يعمل تسجيل IMS والرسائل القصيرة الصادرة، ولكن تُسقط الرسائل والمكالمات الواردة. إذا لم يتمكن ثنائي `nft` المضمّن من فحص حالة قواعد المضيف، يسجّل Vocat الملاحظة `nft could not inspect the OpenWrt fw4 input chain`؛ وجّه `VOCAT_NFT` حينئذٍ إلى nft الخاص بالمضيف: + +```bash +cat > /opt/vocat-host-nft <<'EOF' +#!/bin/sh +exec /host/lib/ld-musl-$(uname -m).so.1 --library-path /host/usr/lib:/host/lib /host/usr/sbin/nft "$@" +EOF +chmod 755 /opt/vocat-host-nft +# أضف إلى أمر docker run أعلاه: +# -v /usr:/host/usr:ro -v /lib:/host/lib:ro \ +# -v /opt/vocat-host-nft:/usr/local/bin/host-nft:ro \ +# -e VOCAT_NFT=/usr/local/bin/host-nft +``` تُنشر صورة GHCR لـ `linux/amd64` و`linux/arm64`. @@ -199,6 +214,11 @@ docker run -d \ يتحقق `vocat doctor --repair-dji-qmi` من وجود `qmicli` قبل تغيير أي ربط لبرنامج تشغيل USB أو تفعيل إشارة DTR. إذا كانت الأداة غير متاحة، يتوقف الأمر مع إرشاد لتثبيتها ويترك حالة الجهاز الحالية دون تغيير. +### بيانات مضيف خلوية عبر AT + +تتطلب بيانات ECM/RNDIS المُدارة عبر AT وجود Linux وiproute2 وBusyBox مع `udhcpc`. +يجب أن يدير VoCat واجهة شبكة المودم بشكل حصري؛ عطّل عملاء DHCP أو ملفات تعريف NetworkManager المتعارضة لتلك الواجهة. يحتفظ VoCat بعقد إيجار IPv4 ومسارات سياسة مُعلّمة دون تعديل التوجيه الافتراضي للنظام أو إعدادات محلل DNS. يتطلب هذا المسار حاليًا خدمة خلوية تدعم IPv4. + ## الإعدادات يقرأ Vocat ملف إعدادات JSON اختياريًا من `VOCAT_CONFIG`، ثم يطبق متغيرات البيئة `VOCAT_*`. متغيرات البيئة لها الأولوية. @@ -211,6 +231,8 @@ docker run -d \ | `VOCAT_SECURE_COOKIES` | `false` | يضع علامة آمنة على ملفات تعريف ارتباط الجلسة عند استخدام HTTPS. | | `VOCAT_SHUTDOWN_TIMEOUT` | `10s` | مهلة الإيقاف السلس. | | `VOCAT_MAX_REQUEST_BODY_BYTES` | `1048576` | الحد الأقصى لحجم جسم طلب API. | +| `VOCAT_MCP_ENABLED` | `true` | إتاحة نقطة نهاية MCP عند `/mcp` لوكلاء الذكاء الاصطناعي. عيّن `false` للتعطيل. | +| `VOCAT_PROBE_UNKNOWN_USB` | `false` | تمكين الفحص الاستدلالي باستخدام أوامر AT على أجهزة USB التسلسلية غير المعروفة. يغير الفحص إعدادات المنفذ التسلسلي ويمسح الإدخال؛ لا تفعّله إلا عندما تكون تلك المنافذ مخصصة للمودمات. تُكتشف هويات المودم المؤكدة تلقائيًا. مفتاح JSON: `probe_unknown_usb`. | | `VOCAT_REPO` | `MengMengCode/VoCat` | مستودع GitHub الموثوق الذي يستخدمه المحدّث الذاتي، بصيغة `owner/name`. | | `GITHUB_TOKEN` | فارغ | رمز GitHub اختياري للمستودعات الخاصة أو حدود API أعلى. | @@ -220,6 +242,79 @@ docker run -d \ لا تخزّن رموز Telegram، أو كلمات مرور SMTP، أو أسرار webhook، أو بيانات اعتماد SIM، أو بيانات خاصة أخرى في المستودع. قم بإعدادها عبر إعدادات التطبيق أو ملفات البيئة المحمية. +## الدخول الموحد (SSO) + +يدعم VoCat كلاً من Pocket ID عبر OpenID Connect، وتطبيقات OAuth على GitHub.com، ومزوّدي Generic OAuth2. اختر مزودًا واحدًا في **الإعدادات → الدخول الموحد (SSO)**. يبدأ SSO معطلاً ويبقى تسجيل الدخول بكلمة المرور متاحًا. سجّل الدخول بالمسؤول المحلي أولاً، واحفظ المزود، واربط حسابًا خارجيًا بشكل صريح قبل تمكين SSO. لا يمكن للحسابات الخارجية الأخرى التسجيل أو أن تصبح مسؤولة، بما في ذلك الحسابات التي تشترك في عنوان البريد الإلكتروني للحساب المرتبط. + +### Pocket ID + +1. استخدم أصل HTTPS لـ VoCat، مثل `https://vocat.example.com`، واضبط `VOCAT_SECURE_COOKIES=true`. في إعدادات SSO، اختر **Pocket ID**. يستخدم الأصل العام في البداية عنوان المتصفح الحالي؛ عدّل حقل **أصل VoCat العام** المرئي إذا لزم الأمر. يظهر عنوان URL المدمج لرد النداء وزر النسخ على الفور، مما يتيح نسخ الرابط قبل إنشاء عميل OIDC أو حفظ أي بيانات اعتماد: `https://vocat.example.com/api/auth/sso/callback`. +2. في Pocket ID، أنشئ عميل OIDC **سري** (Confidential) منفصلاً لـ VoCat. سجّل عنوان URL المطابق تمامًا لرد النداء وتحقق من السماح للمستخدم أو المجموعة المحددة باستخدام هذا العميل. يحتاج كل من VoCat وKomari إلى عملاء منفصلين وجلسات محلية مستقلة. +3. في VoCat، أدخل `APP_URL` العام الدقيق لـ Pocket ID كـ Issuer، وأدخل معرّف العميل (Client ID) وسر العميل (Client Secret) الصادرين للعميل المنشأ في الخطوة 2. يتضمن حقل Client ID تذكيرًا بمكان العثور على هذه القيمة. يتولى VoCat إدارة النطاقات (Scopes) وPKCE S256. يتم اختيار مصادقة الرمز المميز من بيانات تعريف Discovery التي تم التحقق منها، مع تفضيل `client_secret_basic` على `client_secret_post`. احفظ الإعدادات مع إبقاء SSO معطلاً. تؤدي تعديلات الأصل إلى تحديث عنوان رد النداء المعروض فورًا؛ وتسري لأغراض التفويض بعد الحفظ. +4. افحص الإعدادات المحفوظة؛ عند النجاح تظهر رسالة **اجتاز فحص الإعدادات**. ثم اربط الحساب. أتمم تسجيل الدخول عبر مفتاح المرور (Passkey) والموافقة في Pocket ID، وأكد الربط المُعاد في VoCat. يتحقق فحص الإعدادات من بيانات التعريف والاتصال؛ ولا يمكنه التحقق من سر العميل أو تفويض المستخدم بدون تسجيل الدخول هذا. +5. فعّل SSO واحفظ. استخدم نافذة متصفح خاصة للتحقق من تسجيل الدخول عبر SSO، والعودة إلى الصفحة المطلوبة، وأول طلب يغير الحالة. + +تعرض صفحة الإعدادات الحساب المرتبط وخيارًا لفك الارتباط. لتغيير الحساب الخارجي، فك ارتباطه أولاً، ثم اربط الحساب المطلوب وفعّل SSO مجددًا. + +تتم مطابقة Issuer بدقة تامة، بما في ذلك مساره والشرطة المائلة في نهايته. يستخدم تسجيل الدخول عبر Pocket ID القيمة `issuer + sub` من رمز ID المميز الموثوق؛ ولا يلزم وجود نقطة نهاية UserInfo أو مطالبة البريد الإلكتروني. يجب أن تستخدم نقطة نهاية Discovery ونقاط نهاية التفويض والرمز المميز وJWKS أصل الـ Issuer المضبوط. عمليات النشر التي تستخدم `INTERNAL_APP_URL` مع أصل خلفي مختلف غير مدعومة في هذا الإصدار. احتفظ بـ `APP_URL` العام كـ Issuer. يتم الاحتفاظ بطرق المصادقة والنطاقات المضبوطة صراحةً عند الحفظ. + +### GitHub و Generic OAuth2 + +بالنسبة إلى GitHub، أنشئ تطبيق OAuth على GitHub.com بنفس عنوان URL الدقيق لرد النداء، واختر **GitHub**، وأدخل معرّف العميل وسره، واتبع تسلسل الحفظ والفحص والربط والتمكين الموضح أعلاه. نقاط النهاية ثابتة على GitHub.com؛ النطاق الافتراضي هو `read:user`، ويلزم استخدام PKCE S256. يعتمد الربط على المعرّف العددي الثابت لحساب GitHub، لذا فإن تغيير اسم تسجيل الدخول لا يغير الحساب. لا يقع GitHub Enterprise ضمن نطاق هذا الإصدار. + +بالنسبة إلى Generic OAuth2، قدّم عناوين URL الخاصة بالتفويض والرمز المميز (Token) وUserInfo بالإضافة إلى بيانات اعتماد العميل. تظهر النطاقات، وحقل معرّف المستخدم عالي المستوى (الافتراضي `id`)، ومصادقة الرمز المميز، وخيارات توافق PKCE عند اختيار Generic OAuth2. يُطلب UserInfo باستخدام رمز وصول Bearer. يجب أن يكون المعرّف سلسلة غير فارغة أو عددًا صحيحًا؛ وتحتفظ المعرّفات الصحيحة الكبيرة بقيمتها الدقيقة. مصادقة الرمز المميز الافتراضية هي `client_secret_post` وPKCE هو S256. لا تعطّل PKCE إلا لمزود لا يدعمه. لا يقوم Generic OAuth2 بالتحقق من رمز OIDC ID المميز. + +### الشبكة وبيانات الاعتماد والجلسات + +تنطبق أيضًا سياسة شبكة المصدر الحالية في VoCat على مسارات SSO. إن ضبط SSO لا يجعل التثبيت متاحًا للعامة. يجب أن يتمكن المتصفح من الوصول إلى VoCat ونقطة نهاية التفويض؛ ويجب أن تتمكن عملية VoCat من الوصول إلى نقاط نهاية Discovery والرمز المميز وJWKS/UserInfo بشهادات TLS موثوقة. يؤدي حفظ الـ Issuer الدقيق عبر HTTPS لـ Pocket ID أو عناوين نقاط النهاية لـ Generic إلى الموافقة على تلك العناوين لاتصالات HTTPS، بما في ذلك العناوين الخاصة RFC1918/IPv6 ULA. تظل نقاط نهاية GitHub الثابتة مقتصرة على العناوين العامة. + +بالنسبة لخدمة HTTP الداخلية لمزود Generic، أدخل صراحة عنوان URL الكامل عبر HTTP في حقل Token/UserInfo. يجب أن يكون كل عنوان DNS مرشح واتصال فعلي لهذه الوجهات عبر HTTP عنوانًا خاصًا مسموحًا به. لا يمكن لـ Discovery في Pocket ID الموافقة على أصل آخر؛ يجب أن تستخدم نقاط نهايته أصل الـ Issuer المضبوط. يتطلب دائمًا الـ Issuer، وعنوان جلب Discovery، ونقطة نهاية تفويض المتصفح استخدام HTTPS. تظل عناوين الاسترجاع (loopback) وعناوين الربط المحلي (link-local) والبيانات الوصفية السحابية والوجهات ذات الاستخدام الخاص الأخرى محظورة لاتصالات IdP. ثبّت شهادات CA الخاصة في مخزن الثقة بالمضيف/الحاوية؛ ولا يوجد خيار لتجاوز التحقق من TLS. يُعد وصول المتصفح إلى VoCat عبر localhost أو HTTPS مستقلاً عن أذونات اتصال IdP هذه. + +يستخدم نشر Compose المقدّم شبكة المضيف. تحقق من DNS والتوجيه وثقة CA من داخل الحاوية الفعلية؛ ولا تفترض أن أسماء خدمات شبكة bridge متاحة. يجب أن تحافظ الخوادم الوكيلة العكسية (reverse proxies) على الأصل العام المضبوط. لا يشتق VoCat عناوين URL لرد النداء من ترويسات المضيف المُعاد توجيهها. يلزم النشر على مسار الجذر. للتطوير المحلي عبر Vite، استخدم أصل استرجاع متسقًا، مثل `http://127.0.0.1:5173`، للأصل العام وتسجيل رد النداء؛ وتقتصر استثناءات نقل IdP على بيئات الاختبار فقط. + +تحتفظ ملفات تعريف الارتباط للجلسة وCSRF بالسمة `SameSite=Strict`؛ بينما تستخدم ملفات تعريف ارتباط التفويض قصيرة الأجل `SameSite=Lax` لعمليات رد النداء عبر GET من المستوى الأعلى. يؤدي حفظ أي تكوين لـ SSO، بما في ذلك تعطيله أو تدوير السر، إلى إبطال جميع جلسات SSO. يؤدي فك ارتباط حساب إلى إبطال جلسات SSO المرتبطة بذلك الربط. تستمر جلسات كلمة المرور ورموز API المميزة بعد هذه الإجراءات. يؤدي تغيير كلمة مرور المسؤول المحلي إلى إبطال جميع الجلسات ورموز API، وإلغاء التفويضات قيد التنفيذ، والاحتفاظ بالربط الخارجي. تطلب الواجهة تسجيل الدخول مجددًا عند نجاح عملية تبطل جلسة SSO الحالية. + +اترك حقل السر فارغًا للاحتفاظ بالسر الحالي، أو أدخل سرًا جديدًا لاستبداله. لا تقدم صفحة الإعدادات خيارًا لمسح السر المحفوظ. يؤدي تغيير المزود أو معرّف العميل أو الـ Issuer أو نقاط نهاية الهوية/حقل المعرّف لـ Generic إلى تبديل مساحة اسم الهوية، ويتطلب بيانات اعتماد جديدة وربطًا جديدًا؛ وتتطلب عمليات الربط الحالية تأكيدًا صريحًا لإعادة التعيين. لتغيير الحساب المرتبط، فك ارتباطه أولاً ثم أنشئ ربطًا جديدًا. تُحفظ أسرار العميل فقط على الخادم داخل قاعدة بيانات SQLite المحمية؛ وتعمل علامة الإعداد الحساس على حجب القيم في الاستجابات ولا تشفّر قاعدة البيانات. لا يتم حفظ رموز الطرف الثالث أو إعادتها إلى المتصفح. + +يؤدي تسجيل الخروج القياسي إلى إنهاء جلسة VoCat الحالية فقط. إن تعطيل حساب في IdP أو تسجيل الخروج من Pocket ID لا يُبطل على الفور جلسة VoCat الحالية؛ بل تنتهي صلاحيتها وفقًا لـ `VOCAT_SESSION_TTL`، أو يمكن إبطالها بتعطيل SSO أو فك الارتباط في VoCat. نقطة النهاية العامة للقدرات هي `GET /api/auth/providers`. تتطلب واجهات برمجة تطبيقات إعدادات SSO والربط جلسة متصفح وCSRF للكتابة؛ ولا يمكن لرموز Bearer إدارتها. تُعيد تعارضات مراجعة الإعدادات الرمز `409 sso_config_changed`؛ بينما تُعيد تغييرات مصادر الهوية الرمز `409 sso_rebind_required`. + +### الترقية والتراجع + +يضيف SSO إصدار مخطط SQLite رقم 29. تحافظ الترقيات على المسؤول الحالي، والجلسات، ورموز API، وبيانات الأجهزة؛ ويظل SSO معطلاً حتى يتم ضبطه. قبل الترقية، سجّل رقم الإصدار الثنائي القديم أو ملخص الصورة، واحتفظ بنسخة احتياطية من الإعدادات وشهادات CA ودليل البيانات الدائمة بالكامل أثناء إيقاف VoCat، أو استخدم واجهة برمجة تطبيقات النسخ الاحتياطي في SQLite أو أمر `.backup` للحصول على لقطة حية متسقة. إن نسخ ملف قاعدة بيانات قيد التشغيل في وضع WAL وحده لا يُعد نسخة احتياطية موثوقة. + +عطّل SSO في الإصدار الجديد للتعافي من مشكلات المزود مع الحفاظ على تسجيل الدخول بكلمة المرور وبيانات العمل الحالية. تتطلب العودة إلى ملف ثنائي يعمل بالمخطط 28 إيقاف VoCat واستعادة لقطة البيانات والإعدادات المتطابقة السابقة للترقية. لا يمكن لمجرد التراجع عن صورة الحاوية أو خفض قيمة `PRAGMA user_version` تقليل إصدار قاعدة البيانات. استعد النسخة في دليل/وحدة تخزين منفصلة قدر الإمكان واحتفظ بقاعدة البيانات الأحدث للتحقيق. تؤدي استعادة لقطة إلى فقدان الرسائل وتغييرات المهام المسجلة بعد ذلك النسخ الاحتياطي. بالنسبة إلى Docker، حدّث بسحب الصورة الجديدة وإعادة إنشاء الحاوية. + +قبل الإصدار الإنتاجي، أتمم الاختبار الفعلي لمفتاح المرور/سياسة الوصول في Pocket ID وتجربة الترقية والتراجع الخاصة بعملية النشر تلك. + +## رموز API المميزة وMCP + +تتم مصادقة العملاء بخلاف المتصفح على نفس نقاط النهاية `/api/...` باستخدام رمز Bearer المميز بدلاً من ملف تعريف ارتباط جلسة المتصفح وتدفق CSRF: + +```bash +vocat token create --read-only --ttl 720h ci-runner # يطبع الرمز المميز مرة واحدة فقط +curl -H "Authorization: Bearer vcat_..." http://127.0.0.1:7575/api/devices +``` + +يدير كل من `vocat token list` و`vocat token revoke ` قائمة الرموز. تعمل الرموز المميزة بصلاحيات المسؤول؛ ولا يمكن لرمز `--read-only` إلا إرسال طلبات `GET` (استثناء موثق واحد: قراءة سلسلة رسائل قصيرة تضع عليها علامة مقروءة محليًا — القاعدة هي «لا تعديلات على الأجهزة أو الشبكة»). يؤدي تدوير كلمة مرور المسؤول إلى إبطال كل الرموز المميزة. أنشئ الرموز بعد أول `bootstrap-admin`. يحتاج عملاء Bearer البعيدون أيضًا إلى سياسة وصول للشبكة تسمح بعنوان المصدر الخاص بهم؛ ولا يخدم الوضع الافتراضي `internal` سوى الشبكات الخاصة. + +افتراضيًا (عيّن `VOCAT_MCP_ENABLED=false` للتعطيل)، تعرض نفس الخدمة نقطة نهاية MCP (Model Context Protocol) لوكلاء الذكاء الاصطناعي عند `http://:7575/mcp` باستخدام نقل Streamable HTTP والمصادقة برمز Bearer. تغطي الأدوات حالة الأجهزة، وسلاسل الرسائل القصيرة مع الإرسال المحمي، وقائمة ملفات تعريف eSIM وتبديلها، وحالة وإعادة اتصال WiFi Calling؛ ولا يتم كشف العمليات الحساسة (أوامر AT المباشرة، وتنزيل/حذف eSIM، ورمز PIN لشريحة SIM، واختيار الشبكة، وتحديث النظام)، ولا يتضمن إخراج الأدوات أبدًا معرّف IMSI أو رمز PIN أو رموز التنشيط. يتلقى الرمز المخصص للقراءة فقط قائمة أدوات بدون أدوات الكتابة. يتبع إرسال الرسائل القصيرة نفس سياسة الوجهات المحظورة والحد الساعي الشامل لواجهة الويب ويتم عبر مسار AT الخلوي (يظل التوجيه المفضل لـ IMS في واجهة الويب عند تمكين WiFi Calling مقتصرًا على جانب الويب، ولا تُكتب الرسائل المرسلة عبر MCP في سجل محادثات الويب بعد). + +مثال على إعداد العميل (باستخدام Claude Code كمثال): + +```json +{ + "mcpServers": { + "vocat": { + "type": "http", + "url": "http://192.168.1.10:7575/mcp", + "headers": { "Authorization": "Bearer vcat_..." } + } + } +} +``` + +انظر [docs/API_MCP_DEVELOPMENT_PLAN.md](API_MCP_DEVELOPMENT_PLAN.md) لسجل التصميم. + ## بوت Telegram عند تفعيل إشعارات Telegram وإعداد كلٍّ من Chat ID وAdmin ID، يدعم البوت: @@ -230,10 +325,17 @@ docker run -d \ /switch /wfc /sms +/ussd +/ussd_reply +/ussd_cancel ``` تستخدم عمليتا تبديل الملفات الشخصية وإرسال الرسائل القصيرة أزرار تأكيد لمرة واحدة. لا يعرض البوت أوامر تنزيل أو حذف أو إعادة تسمية eSIM. +تتشارك طرفية الويب وTelegram توجيه USSD. مع تمكين WiFi Calling، تستخدم الطلبات IMS/USSI وتُرجع خطأ في الجاهزية حتى يتم تسجيل IMS، دون الرجوع إلى AT+CUSD الخلوي. مع تعطيل WiFi Calling، تستخدم الطلبات USSD الخلوي. تظل الردود على القوائم وعمليات الإلغاء مرتبطة بالجهاز والمحادثة الأصلية. يتم الإبلاغ عن أخطاء الإلغاء مع الاحتفاظ بالجلسة لإعادة المحاولة؛ ولا تتم إعادة إرسال طلب غير مؤكد تلقائيًا عبر قناة أخرى. + +تتبع معالجة محادثات USSI المعيار [3GPP TS 24.390](https://www.etsi.org/deliver/etsi_ts/124300_124399/124390/18.00.00_60/ts_124390v180000p.pdf). يحتفظ فشل أمر BYE بسجل إلغاء، ولكن يتم حظر أي إدخال إضافي في القائمة بمجرد بدء الإلغاء ([RFC 3261، القسم 15.1.1](https://www.rfc-editor.org/rfc/rfc3261.html#section-15.1.1)). + ## التحديث تحقق من وجود GitHub Release أحدث: @@ -356,10 +458,12 @@ cd web && npm run build انظر [LICENSE](../LICENSE). - +## Star History + + - - - Star History Chart + + + Star History Chart diff --git a/docs/README.es.md b/docs/README.es.md index b0ee392e..b64a9609 100644 --- a/docs/README.es.md +++ b/docs/README.es.md @@ -24,7 +24,7 @@ [English](../README.md) | [العربية](README.ar.md) | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [Français](README.fr.md) | [Русский](README.ru.md) | **Español** | [日本語](README.ja.md) -Vocat es un panel de control web de código abierto y un conjunto de herramientas de ingeniería para módems celulares Quectel de clase EC20/EC25. Combina, en un único servicio autocontenido, el descubrimiento de módems, el estado de radio en vivo, terminales AT y USSD, SMS, WiFi Calling, gestión de eSIM, selección de red, enrutamiento por proxy, notificaciones, registros de auditoría y automatización de versiones. +Vocat es un panel de control web de código abierto y un conjunto de herramientas de ingeniería para módems celulares Quectel de clase EC20/EC25 y China Mobile de serie ML307. Combina, en un único servicio autocontenido, el descubrimiento de módems, el estado de radio en vivo, terminales AT y USSD, SMS, WiFi Calling, gestión de eSIM, selección de red, enrutamiento por proxy, notificaciones, registros de auditoría y automatización de versiones. El backend está escrito en Go, la interfaz está construida con React y TypeScript, y el frontend de producción está incrustado en el binario de Go. Un único ejecutable contiene la aplicación web y utiliza SQLite para el estado persistente. @@ -38,7 +38,7 @@ El backend está escrito en Go, la interfaz está construida con React y TypeScr | Área | Lo que proporciona Vocat | | --- | --- | | Gestión de dispositivos | Descubrimiento serie/USB automático, soporte para múltiples módems, nombres de dispositivo amigables, actualizaciones en vivo de la vista general, reinicio del módulo, modo avión y controles del modo de red USB. | -| Radio y red | Estado de registro, operador, métricas de señal, RSRP/RSRQ/SINR, modo de red, banda, canal, búsqueda de operadores y selección de red automática o manual. | +| Radio y red | Estado de registro, operador, métricas de señal, RSRP/RSRQ/SINR, modo de red, banda, canal, búsqueda de operadores, selección de red automática o manual, y consulta de celdas LTE con bloqueo/desbloqueo de celda única (EARFCN/PCI). | | AT y USSD | Terminal AT interactivo, historial de comandos, respuestas sin procesar del módem, flujos de inicio/continuación/cancelación de USSD y reporte claro de errores del módem. | | SMS | Envío directo de SMS celulares e IMS, sincronización entrante, manejo multiparte, informes de entrega, historial de conversaciones, estado de no leído, marcas de tiempo y estado de entrega por mensaje. | | WiFi Calling | Establecimiento de túnel IKEv2/ePDG, autenticación EAP-AKA, registro IMS, SMS IMS, controles de reconexión, diagnósticos de estado y enrutamiento por dispositivo. | @@ -47,17 +47,18 @@ El backend está escrito en Go, la interfaz está construida con React y TypeScr | Enrutamiento por proxy | Enrutamiento SOCKS ascendente, vinculaciones de dispositivos, reglas por país, comprobaciones de accesibilidad TCP y comprobaciones UDP Associate para las rutas de datos de WiFi Calling. | | Notificaciones | Reenvío de nuevos SMS entrantes a través de Telegram, Bark, correo electrónico, Pushplus y webhooks firmados. Cada SMS se entrega como una notificación individual. | | Bot de Telegram | Estado del dispositivo, lista y cambio de perfiles instalados, controles de WiFi Calling y envío de SMS. Las acciones sensibles requieren confirmación del administrador. | -| Operaciones | Autenticación, protección CSRF, políticas de acceso, eventos de auditoría, registros en vivo, retención de registros, comprobaciones de salud, diseño adaptable, modo oscuro e interfaz de usuario en inglés/chino. | +| Operaciones | Autenticación por contraseña y SSO (Pocket ID, GitHub, Generic OAuth2), protección CSRF, políticas de acceso, eventos de auditoría, registros en vivo, retención de registros, comprobaciones de salud, diseño adaptable, modo oscuro e interfaz de usuario en inglés/chino. | | Distribución | Binarios estáticos de Linux, script de instalación systemd, autoactualización con verificación SHA-256, imagen Docker, publicación en GHCR y compilaciones de versión de GitHub Actions. | ## Hardware compatible -Vocat está dirigido a módulos Quectel basados en Qualcomm que exponen interfaces AT, QMI, serie y de red USB compatibles, incluyendo: +Vocat está dirigido a módulos Quectel basados en Qualcomm y módulos China Mobile de serie ML307 que exponen interfaces AT, QMI, serie y de red USB compatibles, incluyendo: - Quectel EC20 - Quectel EC25 - Familia Quectel EG25 - Módulos EG600 compatibles y relacionados +- Serie China Mobile ML307 (por ejemplo, ML307A-E5, diversos OEM) Las funciones disponibles dependen del firmware del módulo, la composición USB, las capacidades SIM/eSIM, los controladores del host, la red de radio y la configuración del operador. @@ -180,7 +181,21 @@ docker run -d \ Abra `http://:7575` después de que se inicie el contenedor. La red del host es necesaria para que las interfaces de red QMI permanezcan visibles para Vocat, mientras que el acceso privilegiado a dispositivos es necesario para los puertos serie, los nodos de control QMI, las interfaces TUN, la configuración de red y los dispositivos añadidos después del inicio del contenedor. El montaje bind de `/dev` hace visibles los nuevos nodos `ttyUSB*`, `ttyACM*`, `cdc-wdm*` y MHI `wwan*` sin recrear el contenedor. -Este modo otorga intencionadamente a Vocat un amplio acceso a los dispositivos y a la pila de red del host. Úselo solo en un host Linux de confianza. El descubrimiento automático identifica los módems USB Quectel compatibles (ID de fabricante USB `2c7c`) y los módems PCIe/MHI expuestos a través del subsistema Linux WWAN; no identifica disposiciones arbitrarias de módems. Mapear solo nodos individuales con `--device`, como `/dev/ttyUSB2`, `/dev/cdc-wdm0` o `/dev/wwan0qmi0`, limita el contenedor a esos nodos fijos y no permite un descubrimiento completo de múltiples dispositivos o de conexiones en caliente. +Este modo otorga intencionadamente a Vocat un amplio acceso a los dispositivos y a la pila de red del host. Úselo solo en un host Linux de confianza. El descubrimiento automático identifica los módems USB Quectel compatibles (ID de fabricante USB `2c7c`), módulos de la serie China Mobile ML307 por cadena de producto y los módems PCIe/MHI expuestos a través del subsistema Linux WWAN; no identifica disposiciones arbitrarias de módems. Mapear solo nodos individuales con `--device`, como `/dev/ttyUSB2`, `/dev/cdc-wdm0` o `/dev/wwan0qmi0`, limita el contenedor a esos nodos fijos y no permite un descubrimiento completo de múltiples dispositivos o de conexiones en caliente. + +En hosts OpenWrt, la cadena input de fw4 rechaza nuevas conexiones de interfaces que no gestiona, incluida la interfaz TUN VoWiFi por sesión creada por Vocat. Por lo tanto, Vocat inserta reglas de aceptación dirigidas `vocat-ims-*` (desde el P-CSCF hacia la dirección interna negociada) utilizando el binario `nft` incluido en la imagen. Sin ellas, el registro IMS y los SMS salientes funcionan, pero los SMS y llamadas entrantes se descartan. Si el binario `nft` incluido no puede inspeccionar el estado de las reglas del host, Vocat registra `nft could not inspect the OpenWrt fw4 input chain`; apunte `VOCAT_NFT` al nft del host: + +```bash +cat > /opt/vocat-host-nft <<'EOF' +#!/bin/sh +exec /host/lib/ld-musl-$(uname -m).so.1 --library-path /host/usr/lib:/host/lib /host/usr/sbin/nft "$@" +EOF +chmod 755 /opt/vocat-host-nft +# Añada al comando docker run anterior: +# -v /usr:/host/usr:ro -v /lib:/host/lib:ro \ +# -v /opt/vocat-host-nft:/usr/local/bin/host-nft:ro \ +# -e VOCAT_NFT=/usr/local/bin/host-nft +``` La imagen GHCR se publica para `linux/amd64` y `linux/arm64`. @@ -199,6 +214,11 @@ VoCat utiliza `qmicli` para verificar que un canal de control QMI esté listo y `vocat doctor --repair-dji-qmi` comprueba la presencia de `qmicli` antes de cambiar cualquier asociación de controladores USB o activar la señal DTR. Si la utilidad no está disponible, el comando se detiene con una indicación para instalarla y deja intacto el estado actual del dispositivo. +### Datos de host celulares vía AT + +Los datos ECM/RNDIS gestionados mediante AT requieren Linux, iproute2 y BusyBox con `udhcpc`. +VoCat debe gestionar de forma exclusiva la interfaz de red del módem; deshabilite clientes DHCP o perfiles de NetworkManager en conflicto para esa interfaz. VoCat mantiene una concesión IPv4 y rutas de directiva marcadas sin modificar el enrutamiento predeterminado ni la configuración de resolución DNS del sistema. Esta ruta requiere actualmente un servicio celular compatible con IPv4. + ## Configuración Vocat lee un archivo de configuración JSON opcional desde `VOCAT_CONFIG` y luego aplica las variables de entorno `VOCAT_*`. Las variables de entorno tienen prioridad. @@ -211,6 +231,8 @@ Vocat lee un archivo de configuración JSON opcional desde `VOCAT_CONFIG` y lueg | `VOCAT_SECURE_COOKIES` | `false` | Marca las cookies de sesión como seguras cuando se usa HTTPS. | | `VOCAT_SHUTDOWN_TIMEOUT` | `10s` | Tiempo de espera de apagado ordenado. | | `VOCAT_MAX_REQUEST_BODY_BYTES` | `1048576` | Tamaño máximo del cuerpo de solicitud de la API. | +| `VOCAT_MCP_ENABLED` | `true` | Expone el endpoint MCP en `/mcp` para agentes de IA. Establezca `false` para deshabilitarlo. | +| `VOCAT_PROBE_UNKNOWN_USB` | `false` | Habilita el sondeo heurístico con comandos AT en dispositivos serie USB no reconocidos. El sondeo cambia la configuración serie y borra la entrada; habilítelo solo cuando esos puertos estén dedicados a módems. Las identidades de módems verificados se descubren automáticamente. Clave JSON: `probe_unknown_usb`. | | `VOCAT_REPO` | `MengMengCode/VoCat` | Repositorio de GitHub de confianza usado por el autoactualizador, en formato `owner/name`. | | `GITHUB_TOKEN` | vacío | Token de GitHub opcional para repositorios privados o límites de API más altos. | @@ -220,6 +242,79 @@ Las credenciales del administrador se almacenan únicamente en SQLite. Inicialic No almacene tokens de Telegram, contraseñas SMTP, secretos de webhook, credenciales SIM u otros datos privados en el repositorio. Configúrelos a través de los ajustes de la aplicación o archivos de entorno protegidos. +## Inicio de sesión único (SSO) + +VoCat admite Pocket ID mediante OpenID Connect, aplicaciones OAuth de GitHub.com y proveedores Generic OAuth2. Seleccione un proveedor en **Ajustes → Inicio de sesión único (SSO)**. El SSO comienza deshabilitado y el inicio de sesión con contraseña permanece disponible. Inicie sesión primero con el administrador local, guarde un proveedor y vincule explícitamente una cuenta externa antes de habilitar el SSO. Otras cuentas externas no pueden registrarse ni convertirse en administradores, incluidas las cuentas que compartan la dirección de correo electrónico de la cuenta vinculada. + +### Pocket ID + +1. Utilice un origen HTTPS para VoCat, como `https://vocat.example.com`, y configure `VOCAT_SECURE_COOKIES=true`. En la configuración de SSO, seleccione **Pocket ID**. El origen público utiliza inicialmente la dirección actual del navegador; edite el campo visible **Origen público de VoCat** si es necesario. La URL de retorno compacta y el botón Copiar aparecen de inmediato, por lo que la URL se puede copiar antes de crear el cliente OIDC o guardar credenciales: `https://vocat.example.com/api/auth/sso/callback`. +2. En Pocket ID, cree un cliente OIDC **Confidencial** independiente para VoCat. Registre la URL de retorno exacta y verifique que el usuario o grupo previsto tenga permiso para utilizar este cliente. VoCat y Komari necesitan clientes independientes y sesiones locales separadas. +3. En VoCat, introduzca la `APP_URL` pública exacta de Pocket ID como Issuer, e introduzca el Client ID y el Client Secret emitidos para el cliente creado en el paso 2. El campo Client ID incluye un recordatorio de dónde encontrar este valor. Los scopes y PKCE S256 son gestionados por VoCat. La autenticación de token se selecciona a partir de los metadatos de Discovery validados, prefiriendo `client_secret_basic` sobre `client_secret_post`. Guarde mientras el SSO permanezca deshabilitado. Las ediciones del origen actualizan la URL de retorno mostrada de inmediato; surten efecto para la autorización tras guardar. +4. Compruebe la configuración guardada; si tiene éxito, se mostrará **Comprobación de configuración superada**. A continuación, vincule la cuenta. Complete el inicio de sesión con Passkey y el consentimiento de Pocket ID, y confirme la vinculación devuelta en VoCat. La comprobación de configuración valida los metadatos y la conectividad; no puede verificar un secreto de cliente ni la autorización del usuario sin este inicio de sesión. +5. Habilite el SSO y guarde. Utilice una ventana privada del navegador para verificar el inicio de sesión por SSO, el retorno a la página solicitada y la primera solicitud que modifique el estado. + +La página de ajustes muestra la cuenta vinculada y una acción de desvinculación. Para cambiar la cuenta externa, desvincúlela primero, luego vincule la cuenta deseada y habilite de nuevo el SSO. + +El Issuer se compara de forma estricta, incluyendo su ruta y la barra diagonal final. El inicio de sesión de Pocket ID utiliza el `issuer + sub` del ID Token validado; no se requiere un endpoint UserInfo ni la notificación de correo electrónico. Discovery y sus endpoints de autorización, Token y JWKS deben utilizar el origen del Issuer configurado. Las implementaciones que utilicen `INTERNAL_APP_URL` con un origen de backend diferente no son compatibles en esta versión. Mantenga la `APP_URL` pública como Issuer. Los métodos de autenticación y scopes explícitamente configurados se conservan al guardar. + +### GitHub y Generic OAuth2 + +Para GitHub, cree una aplicación OAuth en GitHub.com con exactamente la misma URL de retorno, seleccione **GitHub**, introduzca su Client ID y Secret, y siga la secuencia de guardar, comprobar, vincular y habilitar descrita anteriormente. Los endpoints son fijos en GitHub.com; el scope predeterminado es `read:user` y se requiere PKCE S256. La vinculación sigue el identificador de cuenta numérico estable de GitHub, por lo que cambiar el nombre de usuario no cambia la cuenta. GitHub Enterprise queda fuera del alcance de esta versión. + +Para Generic OAuth2, proporcione las URL de autorización, Token y UserInfo además de las credenciales del cliente. Los scopes, el campo de ID de usuario de nivel superior (por defecto `id`), la autenticación de token y las opciones de compatibilidad con PKCE se muestran cuando se selecciona Generic OAuth2. UserInfo se solicita con un token de acceso Bearer. El ID debe ser una cadena no vacía o un número entero; los identificadores enteros grandes conservan su valor exacto. La autenticación de token predeterminada es `client_secret_post` y PKCE es S256. Deshabilite PKCE únicamente para un proveedor que no lo admita. Generic OAuth2 no realiza la verificación de ID Token OIDC. + +### Red, credenciales y sesiones + +La directiva de red de origen existente de VoCat también se aplica a las rutas de SSO. Configurar SSO no hace que una instalación sea accesible públicamente. El navegador debe poder acceder a VoCat y al endpoint de autorización; el proceso de VoCat debe poder comunicarse con Discovery, los endpoints de Token y JWKS/UserInfo con certificados TLS de confianza. Guardar el Issuer HTTPS exacto de Pocket ID o las URL de endpoints de Generic aprueba esas direcciones para conexiones HTTPS, incluidas las direcciones privadas RFC1918/IPv6 ULA. Los endpoints fijos de GitHub permanecen restringidos a direcciones públicas. + +Para el servicio HTTP interno de un proveedor Generic, introduzca explícitamente su URL HTTP completa en el campo Token/UserInfo. Cada candidato DNS y conexión real para estos destinos HTTP debe ser una dirección privada permitida. Discovery de Pocket ID no puede aprobar otro origen; sus endpoints deben utilizar el origen del Issuer configurado. El Issuer, la URL de obtención de Discovery y el endpoint de autorización del navegador siempre requieren HTTPS. Las direcciones de bucle local (loopback), enlace local (link-local), metadatos en la nube y otros destinos de uso especial permanecen bloqueados para las conexiones con el IdP. Instale certificados de CA privados en el almacén de confianza del host/contenedor; no existe opción para eludir la verificación TLS. El acceso del navegador a VoCat mediante localhost o HTTPS es independiente de estos permisos de conexión con el IdP. + +El despliegue de Compose proporcionado utiliza la red del host. Verifique el DNS, el enrutamiento y la confianza de CA desde el propio contenedor; no asuma que los nombres de servicio en red tipo bridge estén disponibles. Los servidores proxy inversos deben conservar el origen público configurado. VoCat no deriva las URL de retorno a partir de cabeceras de host reenviadas. Se requiere un despliegue en la ruta raíz. Para el desarrollo local con Vite, utilice un único origen de bucle local coherente, como `http://127.0.0.1:5173`, para el origen público y el registro de la URL de retorno; las excepciones de transporte de IdP solo deben usarse en entornos de prueba. + +Las cookies de sesión y CSRF conservan `SameSite=Strict`; las cookies de autorización de corta duración utilizan `SameSite=Lax` para retornos GET de nivel superior. Guardar cualquier configuración de SSO, incluida su desactivación o la rotación del secreto, revoca todas las sesiones de SSO. Desvincular una cuenta revoca las sesiones de SSO asociadas a esa vinculación. Las sesiones por contraseña y los tokens de API sobreviven a estas acciones. Cambiar la contraseña del administrador local revoca todas las sesiones y tokens de API, cancela las autorizaciones en curso y conserva la vinculación externa. La interfaz solicita iniciar sesión nuevamente cuando una operación exitosa revoca la sesión de SSO actual. + +Deje el campo de secreto vacío para conservar un secreto existente, o introduzca un nuevo secreto para reemplazarlo. La página de ajustes no ofrece un control para borrar un secreto. Cambiar de proveedor, Client ID, Issuer o los endpoints/campo de ID de Generic cambia el espacio de nombres de identidad y requiere nuevas credenciales y una nueva vinculación; las vinculaciones existentes requieren confirmación explícita de restablecimiento. Para cambiar la cuenta vinculada, desvincúlela y luego cree una nueva vinculación. Los secretos de cliente se conservan únicamente en el servidor dentro de la base de datos SQLite protegida; la etiqueta de ajuste sensible ofusca las respuestas y no cifra la base de datos. Los tokens de terceros no se almacenan ni se devuelven al navegador. + +Cerrar sesión finaliza únicamente la sesión actual de VoCat. Deshabilitar una cuenta de IdP o cerrar sesión en Pocket ID no revoca de inmediato una sesión existente de VoCat; esta expira según `VOCAT_SESSION_TTL`, o puede revocarse deshabilitando el SSO o desvinculando la cuenta en VoCat. El endpoint público de capacidades es `GET /api/auth/providers`. Las API de ajustes y vinculación de SSO requieren una sesión de navegador y CSRF para operaciones de escritura; los tokens Bearer no pueden gestionarlas. Los conflictos de revisión de configuración devuelven `409 sso_config_changed`; los cambios de origen de identidad devuelven `409 sso_rebind_required`. + +### Actualización y reversión + +SSO añade la versión de esquema SQLite 29. Las actualizaciones conservan el administrador existente, las sesiones, los tokens de API y los datos de los dispositivos; el SSO permanece deshabilitado hasta que se configure. Antes de actualizar, anote el binario anterior o el digest de la imagen y haga una copia de seguridad de la configuración, los certificados CA y el directorio de datos persistente completo mientras VoCat está detenido, o use la API de copia de seguridad de SQLite / `.backup` para obtener una instantánea coherente en caliente. Copiar únicamente un archivo de base de datos en ejecución en modo WAL no es una copia de seguridad confiable. + +Deshabilite el SSO en la nueva versión para solucionar problemas con el proveedor mientras mantiene el inicio de sesión con contraseña y los datos actuales. Volver a un binario con esquema 28 requiere detener VoCat y restaurar la instantánea de datos y configuración previa a la actualización correspondiente. La simple reversión de la imagen del contenedor o la reducción de `PRAGMA user_version` no pueden degradar la base de datos. Restaure en un directorio/volumen separado siempre que sea posible y conserve la base de datos más reciente para su investigación. Restaurar una instantánea provoca la pérdida de mensajes y cambios en tareas registrados después de esa copia de seguridad. Para Docker, actualice descargando la nueva imagen y recreando el contenedor. + +Antes de un despliegue en producción, complete la prueba real de Passkey/política de acceso de Pocket ID y un ensayo de actualización y reversión para ese entorno. + +## Tokens de API y MCP + +Los clientes que no son navegadores se autentican contra los mismos endpoints `/api/...` con un token Bearer en lugar de la cookie de sesión y el flujo CSRF del navegador: + +```bash +vocat token create --read-only --ttl 720h ci-runner # muestra el token una sola vez +curl -H "Authorization: Bearer vcat_..." http://127.0.0.1:7575/api/devices +``` + +`vocat token list` y `vocat token revoke ` gestionan el inventario. Los tokens actúan con privilegios de administrador; un token `--read-only` solo puede emitir solicitudes `GET` (una excepción documentada: leer un hilo de SMS lo marca como leído localmente — la regla es «sin modificaciones de hardware o red»). Cambiar la contraseña del administrador revoca todos los tokens. Cree tokens después del primer `bootstrap-admin`. Los clientes remotos con Bearer también necesitan una política de acceso de red que admita su dirección de origen; el modo predeterminado `internal` solo atiende redes privadas. + +Por defecto (`VOCAT_MCP_ENABLED=false` para deshabilitarlo), el mismo servicio expone un endpoint MCP (Model Context Protocol) para agentes de IA en `http://:7575/mcp` utilizando el transporte Streamable HTTP y autenticación por token Bearer. Las herramientas cubren el estado de los dispositivos, hilos de SMS y envío protegido, listado y cambio de perfiles eSIM, y estado/reconexión de WiFi Calling; las operaciones sensibles (comandos AT directos, descarga/eliminación de eSIM, PIN de SIM, selección de red, actualización del sistema) no están expuestas, y la salida de las herramientas nunca incluye IMSI, PIN de SIM ni códigos de activación. Un token de solo lectura recibe una lista de herramientas sin las herramientas de escritura. El envío de SMS sigue la misma política de destinos bloqueados y el mismo límite horario global que la interfaz web y se envía a través de la ruta celular AT (el enrutamiento preferente por IMS de la interfaz web cuando WiFi Calling está habilitado permanece en el lado web, y los mensajes enviados a través de MCP aún no se registran en el historial de conversaciones web). + +Ejemplo de configuración de cliente (con Claude Code): + +```json +{ + "mcpServers": { + "vocat": { + "type": "http", + "url": "http://192.168.1.10:7575/mcp", + "headers": { "Authorization": "Bearer vcat_..." } + } + } +} +``` + +Consulte [docs/API_MCP_DEVELOPMENT_PLAN.md](API_MCP_DEVELOPMENT_PLAN.md) para el documento de diseño. + ## Bot de Telegram Cuando las notificaciones de Telegram están habilitadas y tanto el Chat ID como el Admin ID están configurados, el bot admite: @@ -230,10 +325,17 @@ Cuando las notificaciones de Telegram están habilitadas y tanto el Chat ID como /switch /wfc /sms +/ussd +/ussd_reply +/ussd_cancel ``` El cambio de perfil y el envío de SMS usan botones de confirmación de un solo uso. El bot no expone comandos de descarga, eliminación o renombrado de eSIM. +El terminal web y Telegram comparten el enrutamiento USSD. Con WiFi Calling habilitado, las solicitudes utilizan IMS/USSI y devuelven un error de espera hasta que IMS se registre, sin recurrir a AT+CUSD celular. Con WiFi Calling deshabilitado, las solicitudes utilizan USSD celular. Las respuestas a los menús y las cancelaciones permanecen vinculadas al dispositivo y diálogo originales. Los errores de cancelación se reportan y conservan la sesión para reintentar; un envío incierto nunca se reenvía automáticamente por otra vía. + +El manejo de diálogos USSI sigue la norma [3GPP TS 24.390](https://www.etsi.org/deliver/etsi_ts/124300_124399/124390/18.00.00_60/ts_124390v180000p.pdf). Si falla un BYE, se conserva un registro de cancelación, pero cualquier entrada adicional en el menú se bloquea una vez iniciada la cancelación ([RFC 3261, sección 15.1.1](https://www.rfc-editor.org/rfc/rfc3261.html#section-15.1.1)). + ## Actualización Comprobar si hay una GitHub Release más reciente: @@ -356,10 +458,12 @@ cd web && npm run build Consulte [LICENSE](../LICENSE). - +## Star History + + - - - Star History Chart + + + Star History Chart diff --git a/docs/README.fr.md b/docs/README.fr.md index 8ac03634..ca2346b1 100644 --- a/docs/README.fr.md +++ b/docs/README.fr.md @@ -24,7 +24,7 @@ [English](../README.md) | [العربية](README.ar.md) | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | **Français** | [Русский](README.ru.md) | [Español](README.es.md) | [日本語](README.ja.md) -Vocat est un panneau de contrôle web open-source et une boîte à outils d'ingénierie pour les modems cellulaires Quectel de classe EC20/EC25. Il réunit, dans un service autonome unique, la découverte de modems, l'état radio en direct, les terminaux AT et USSD, les SMS, les appels WiFi (WiFi Calling), la gestion eSIM, la sélection de réseau, le routage par proxy, les notifications, les journaux d'audit et l'automatisation des versions. +Vocat est un panneau de contrôle web open-source et une boîte à outils d'ingénierie pour les modems cellulaires Quectel de classe EC20/EC25 et China Mobile de série ML307. Il réunit, dans un service autonome unique, la découverte de modems, l'état radio en direct, les terminaux AT et USSD, les SMS, les appels WiFi (WiFi Calling), la gestion eSIM, la sélection de réseau, le routage par proxy, les notifications, les journaux d'audit et l'automatisation des versions. Le backend est écrit en Go, l'interface est construite avec React et TypeScript, et le frontend de production est intégré dans le binaire Go. Un seul exécutable contient l'application web et utilise SQLite pour l'état persistant. @@ -38,7 +38,7 @@ Le backend est écrit en Go, l'interface est construite avec React et TypeScript | Domaine | Ce que Vocat fournit | | --- | --- | | Gestion des appareils | Découverte série/USB automatique, prise en charge de plusieurs modems, noms d'appareils conviviaux, mises à jour en direct de la vue d'ensemble, redémarrage du module, mode avion et contrôles du mode réseau USB. | -| Radio et réseau | État d'enregistrement, opérateur, métriques de signal, RSRP/RSRQ/SINR, mode réseau, bande, canal, recherche d'opérateurs et sélection de réseau automatique ou manuelle. | +| Radio et réseau | État d'enregistrement, opérateur, métriques de signal, RSRP/RSRQ/SINR, mode réseau, bande, canal, recherche d'opérateurs, sélection de réseau automatique ou manuelle, et interrogation des cellules LTE avec verrouillage/déverrouillage d'une cellule unique (EARFCN/PCI). | | AT et USSD | Terminal AT interactif, historique des commandes, réponses brutes du modem, flux de démarrage/poursuite/annulation USSD et rapport d'erreurs modem clair. | | SMS | Envoi direct de SMS cellulaires et IMS, synchronisation entrante, gestion des messages multiparties, rapports de livraison, historique des conversations, état non lu, horodatages et statut de livraison par message. | | WiFi Calling | Établissement de tunnel IKEv2/ePDG, authentification EAP-AKA, enregistrement IMS, SMS IMS, contrôles de reconnexion, diagnostics d'état et routage par appareil. | @@ -47,17 +47,18 @@ Le backend est écrit en Go, l'interface est construite avec React et TypeScript | Routage par proxy | Routage SOCKS amont, liaisons d'appareils, règles par pays, vérifications d'accessibilité TCP et vérifications UDP Associate pour les chemins de données WiFi Calling. | | Notifications | Transfert des nouveaux SMS entrants via Telegram, Bark, e-mail, Pushplus et webhooks signés. Chaque SMS est livré comme une notification individuelle. | | Bot Telegram | État de l'appareil, liste et commutation des profils installés, contrôles WiFi Calling et envoi de SMS. Les actions sensibles nécessitent une confirmation de l'administrateur. | -| Exploitation | Authentification, protection CSRF, politiques d'accès, événements d'audit, journaux en direct, rétention des journaux, vérifications de santé, mise en page réactive, mode sombre et interface utilisateur en anglais/chinois. | +| Exploitation | Authentification par mot de passe et SSO (Pocket ID, GitHub, Generic OAuth2), protection CSRF, politiques d'accès, événements d'audit, journaux en direct, rétention des journaux, vérifications de santé, mise en page réactive, mode sombre et interface utilisateur en anglais/chinois. | | Distribution | Binaires Linux statiques, script d'installation systemd, auto-mise à jour avec vérification SHA-256, image Docker, publication GHCR et builds de version GitHub Actions. | ## Matériel pris en charge -Vocat cible les modules Quectel à base Qualcomm qui exposent des interfaces AT, QMI, série et réseau USB compatibles, notamment : +Vocat cible les modules Quectel à base Qualcomm et les modules China Mobile de série ML307 qui exposent des interfaces AT, QMI, série et réseau USB compatibles, notamment : - Quectel EC20 - Quectel EC25 - Famille Quectel EG25 - Modules EG600 compatibles et apparentés +- Série China Mobile ML307 (par exemple ML307A-E5, divers OEM) Les fonctionnalités disponibles dépendent du firmware du module, de la composition USB, des capacités SIM/eSIM, des pilotes hôtes, du réseau radio et de la configuration de l'opérateur. @@ -65,13 +66,13 @@ Les fonctionnalités disponibles dépendent du firmware du module, de la composi ### Installation Linux en un clic -En tant que root (y compris OpenWrt/Kwrt, où `sudo` est normalement absent) : +En tant que root (y compris OpenWrt/Kwrt, où `sudo` est généralement absent) : ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh | bash ``` -Depuis un utilisateur normal sur une distribution disposant de sudo : +Depuis un utilisateur ordinaire sur une distribution disposant de sudo : ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh | sudo bash @@ -90,27 +91,23 @@ curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/i sudo bash install.sh 0.0.2 ``` -VoWiFi IMS nécessite Linux XFRM/IPsec. Sur OpenWrt/Kwrt, le programme d'installation tente -d'installer les paquets correspondants `ip-full`, `kmod-ipsec`, `kmod-ipsec4/6`, -`kmod-crypto-authenc`, AES-CBC et SHA1 depuis le dépôt du firmware lui-même. -Si des modules noyau correspondants ne sont pas disponibles, utilisez un firmware qui les inclut ; -ne forcez jamais l'installation de kmods compilés pour un noyau différent. +VoWiFi IMS nécessite Linux XFRM/IPsec. Sur OpenWrt/Kwrt, le script d'installation tente d'installer depuis les dépôts du firmware les paquets correspondants : `ip-full`, `kmod-ipsec`, `kmod-ipsec4/6`, `kmod-crypto-authenc`, AES-CBC et SHA1. Si les modules de noyau correspondants ne sont pas disponibles, utilisez un firmware qui les intègre ; ne forcez jamais l'installation de modules kmod compilés pour un autre noyau. -Si votre noyau ne peut pas fournir XFRM/IPsec et que vous avez uniquement besoin de fonctionnalités sans VoWiFi, comme les SMS cellulaires ou les données, installez avec `--skip-vowifi-check` : +Si votre noyau ne peut pas fournir XFRM/IPsec et que vous n'avez besoin que de fonctionnalités hors VoWiFi comme les SMS ou données cellulaires, installez avec `--skip-vowifi-check` : ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh -o install.sh sudo bash install.sh --skip-vowifi-check ``` -Le programme d'installation : +L'installateur : -- détecte `amd64`, `386`, `arm64`, `aarch64` ou `armv7` ; +- détecte l'architecture `amd64`, `386`, `arm64`, `aarch64` ou `armv7` ; - télécharge le binaire GitHub Release correspondant ; - le vérifie par rapport à `SHA256SUMS` ; - installe Vocat sous `/opt/vocat` ; -- crée un service systemd renforcé disposant des accès matériel et réseau requis par Vocat ; -- stocke la configuration d'exécution dans `/etc/vocat/env` ; +- crée un service systemd sécurisé avec les accès matériel et réseau requis par Vocat ; +- enregistre la configuration d'exécution dans `/etc/vocat/env` ; - génère un mot de passe administrateur initial aléatoire lors de la première installation. Après l'installation, ouvrez : @@ -131,7 +128,7 @@ Téléchargez le binaire correspondant et `SHA256SUMS` depuis GitHub Releases : | Linux AArch64 | `vocat-linux-aarch64` | | Linux ARMv7 | `vocat-linux-armv7` | -Vérifiez-le et installez-le : +Vérifiez et installez : ```bash sha256sum -c SHA256SUMS --ignore-missing @@ -145,15 +142,11 @@ sudo env \ /opt/vocat/bin/vocat serve ``` -Cette commande manuelle exécute Vocat au premier plan. Utilisez `vocat serve` pour que le -processus démarre directement le serveur ; exécuter `vocat` sans argument en tant que root -sur un TTY ouvre plutôt le menu de gestion interactif. Utilisez le programme d'installation -en un clic lorsqu'un service systemd géré et un redémarrage automatique sont requis. +Cette commande manuelle exécute Vocat au premier plan. Utilisez `vocat serve` pour que le processus démarre directement le serveur ; exécuter `vocat` sans argument en tant que root dans un TTY ouvre plutôt le menu de gestion interactif. Utilisez l'installateur en un clic lorsqu'un service systemd géré et un redémarrage automatique sont requis. ### Docker -Pour un hôte Linux qui doit découvrir chaque modem Quectel pris en charge connecté et -continuer à voir les événements de branchement à chaud USB, exécutez Vocat en mode d'accès matériel : +Pour un hôte Linux qui doit découvrir chaque modem Quectel pris en charge connecté et continuer à voir les événements de branchement à chaud USB, exécutez Vocat en mode accès matériel : ```bash docker pull ghcr.io/mengmengcode/vocat:latest @@ -178,47 +171,141 @@ docker run -d \ ghcr.io/mengmengcode/vocat:latest ``` -Ouvrez `http://:7575` après le démarrage du conteneur. Le réseau de l'hôte est nécessaire pour que les interfaces réseau QMI restent visibles par Vocat, tandis que l'accès privilégié aux périphériques est nécessaire pour les ports série, les nœuds de contrôle QMI, les interfaces TUN, la configuration réseau et les périphériques ajoutés après le démarrage du conteneur. Le montage bind de `/dev` rend les nouveaux nœuds `ttyUSB*`, `ttyACM*`, `cdc-wdm*` et MHI `wwan*` visibles sans recréer le conteneur. +Ouvrez `http://:7575` après le démarrage du conteneur. Le réseau hôte est requis pour que les interfaces réseau QMI restent visibles par Vocat, tandis que l'accès privilégié aux périphériques est nécessaire pour les ports série, les nœuds de contrôle QMI, les interfaces TUN, la configuration réseau et les périphériques ajoutés après le démarrage du conteneur. Le montage de liaison `/dev` rend les nouveaux nœuds `ttyUSB*`, `ttyACM*`, `cdc-wdm*` et `wwan*` MHI visibles sans recréer le conteneur. -Ce mode donne intentionnellement à Vocat un large accès aux périphériques et à la pile réseau de l'hôte. Ne l'utilisez que sur un hôte Linux de confiance. La découverte automatique identifie les modems USB Quectel pris en charge (identifiant de fabricant USB `2c7c`) et les modems PCIe/MHI exposés par le sous-système Linux WWAN ; elle ne reconnaît pas toutes les configurations de modems. Le mappage de nœuds individuels uniquement avec `--device`, comme `/dev/ttyUSB2`, `/dev/cdc-wdm0` ou `/dev/wwan0qmi0`, limite le conteneur à ces nœuds fixes et ne permet pas une découverte complète de plusieurs périphériques ou des branchements à chaud. +Ce mode donne délibérément à Vocat un accès étendu aux périphériques et à la pile réseau de l'hôte. Ne l'utilisez que sur un hôte Linux de confiance. La découverte automatique identifie les modems USB Quectel pris en charge (identifiant de vendeur USB `2c7c`), les modules China Mobile de série ML307 par chaîne de produit, et les modems PCIe/MHI exposés via le sous-système WWAN de Linux ; elle n'identifie pas les configurations arbitraires de modems. Mapper uniquement des nœuds individuels avec `--device`, tels que `/dev/ttyUSB2`, `/dev/cdc-wdm0` ou `/dev/wwan0qmi0`, limite le conteneur à ces nœuds fixes et ne fournit pas de découverte complète multi-appareils ou de branchement à chaud. + +Sur les hôtes OpenWrt, la chaîne input de fw4 rejette les nouvelles connexions provenant des interfaces qu'elle ne gère pas, y compris l'interface TUN VoWiFi créée par Vocat pour chaque session. Vocat insère donc des règles d'autorisation `vocat-ims-*` ciblées (du P-CSCF vers l'adresse interne négociée) à l'aide du binaire `nft` inclus dans l'image. Sans ces règles, l'enregistrement IMS et les SMS sortants fonctionnent, mais les SMS et les appels entrants sont rejetés. Si le binaire `nft` inclus ne peut pas lire le jeu de règles de l'hôte, Vocat consigne `nft could not inspect the OpenWrt fw4 input chain` ; pointez alors `VOCAT_NFT` vers le nft de l'hôte : + +```bash +cat > /opt/vocat-host-nft <<'EOF' +#!/bin/sh +exec /host/lib/ld-musl-$(uname -m).so.1 --library-path /host/usr/lib:/host/lib /host/usr/sbin/nft "$@" +EOF +chmod 755 /opt/vocat-host-nft +# Ajoutez à la commande docker run ci-dessus : +# -v /usr:/host/usr:ro -v /lib:/host/lib:ro \ +# -v /opt/vocat-host-nft:/usr/local/bin/host-nft:ro \ +# -e VOCAT_NFT=/usr/local/bin/host-nft +``` L'image GHCR est publiée pour `linux/amd64` et `linux/arm64`. > [!TIP] -> **Note de déploiement NAS / QNAP Container Station** : -> Sur les systèmes d'exploitation NAS tels que QNAP QTS / QuTS hero (Container Station), les comptes administrateur personnalisés non root et les mécanismes d'isolation des volumes peuvent faire pointer les volumes nommés Docker (par exemple `-v vocat-data:/opt/vocat/data`) vers des chemins isolés différents entre l'initialisation unique `bootstrap-admin` et le conteneur du service en arrière-plan, entraînant des erreurs « Mot de passe incorrect » lors de la connexion web. -> Pour les environnements NAS, il est fortement recommandé de remplacer les volumes nommés par un montage bind utilisant un chemin absolu de l'hôte (par exemple `-v /share/Container/vocat/data:/opt/vocat/data` sur QNAP), aussi bien pour l'initialisation que pour l'exécution, afin de garantir une persistance cohérente de la base de données SQLite. +> **Remarque sur le déploiement NAS / QNAP Container Station** : +> Sur les systèmes d'exploitation NAS comme QNAP QTS / QuTS hero (Container Station), les comptes administrateurs personnalisés non-root et les mécanismes d'isolation de volume peuvent amener les volumes nommés Docker (par exemple `-v vocat-data:/opt/vocat/data`) à pointer vers des chemins isolés différents entre l'initialisation ponctuelle `bootstrap-admin` et le conteneur de service démon, provoquant des erreurs « Mot de passe incorrect » lors de la connexion Web. +> Pour les environnements NAS, il est fortement recommandé de remplacer les volumes nommés par un montage de liaison avec chemin absolu sur l'hôte (par exemple `-v /share/Container/vocat/data:/opt/vocat/data` sur QNAP) pour l'initialisation et l'exécution, afin de garantir la persistance cohérente de la base de données SQLite. ### Lecteurs de cartes SIM USB -Les lecteurs de cartes SIM USB utilisent le service Linux PC/SC. Avec les gestionnaires de paquets pris en charge, le programme d'installation en un clic installe et démarre automatiquement `pcscd`, et installe le pilote CCID. Sur Debian/Ubuntu, la commande manuelle équivalente est `apt install pcscd libccid`. Si USB détecte un lecteur CCID mais que PC/SC est indisponible, VoCat laisse le lecteur visible dans la boîte de dialogue d'ajout d'appareil et signale le service ou le pilote manquant au lieu de le masquer silencieusement. +Les lecteurs de cartes SIM USB utilisent le service Linux PC/SC. L'installateur en un clic installe et démarre automatiquement `pcscd` ainsi que le pilote CCID sur les gestionnaires de paquets pris en charge. Sur Debian/Ubuntu, la commande manuelle équivalente est `apt install pcscd libccid`. Si l'USB détecte un lecteur CCID mais que PC/SC est indisponible, VoCat laisse le lecteur visible dans la boîte de dialogue d'ajout d'appareil et signale le service ou le pilote manquant au lieu de le masquer silencieusement. ### Utilitaires QMI en ligne de commande -VoCat utilise `qmicli` pour vérifier qu'un canal de contrôle QMI est prêt et `qmi-proxy` pour en multiplexer l'accès. Les sessions de données par paquets sont gérées par le client QMI WDS intégré, plutôt que par les fichiers d'état CID/PDH de `qmi-network`. Le programme d'installation en un clic installe et vérifie les utilitaires correspondants. Pour un déploiement manuel, Debian/Ubuntu utilise `apt install libqmi-utils` ; Arch Linux utilise `pacman -S libqmi`, Alpine utilise `apk add qmi-utils` et OpenWrt utilise `opkg install qmi-utils`. +VoCat utilise `qmicli` pour vérifier qu'un canal de contrôle QMI est prêt et `qmi-proxy` pour multiplexer son accès. Les sessions de données par paquets sont gérées par le client QMI WDS intégré plutôt que par les fichiers d'état CID/PDH de `qmi-network`. L'installateur en un clic installe et vérifie les utilitaires correspondants. Pour un déploiement manuel, Debian/Ubuntu utilise `apt install libqmi-utils` ; Arch Linux utilise `pacman -S libqmi`, Alpine utilise `apk add qmi-utils`, et OpenWrt utilise `opkg install qmi-utils`. + +`vocat doctor --repair-dji-qmi` vérifie la présence de `qmicli` avant de modifier toute liaison de pilote USB ou d'activer le signal DTR. Si l'utilitaire est indisponible, la commande s'arrête avec une indication d'installation et conserve l'état actuel de l'appareil sans modification. -`vocat doctor --repair-dji-qmi` vérifie la présence de `qmicli` avant de modifier toute association de pilote USB ou d'activer le signal DTR. Si l'utilitaire est indisponible, la commande s'arrête avec une indication d'installation et laisse l'état actuel de l'appareil inchangé. +### Données d'hôte cellulaires via AT + +Les données ECM/RNDIS gérées via AT nécessitent Linux, iproute2 et BusyBox avec `udhcpc`. +VoCat doit gérer de manière exclusive l'interface réseau du modem ; désactivez les clients DHCP ou profils NetworkManager concurrents pour cette interface. VoCat gère un bail IPv4 et des routes de politique marquées sans modifier la route par défaut ni la configuration de résolution DNS du système. Ce chemin nécessite actuellement un service cellulaire compatible IPv4. ## Configuration Vocat lit un fichier de configuration JSON optionnel depuis `VOCAT_CONFIG`, puis applique les variables d'environnement `VOCAT_*`. Les variables d'environnement ont la priorité. -| Variable d'environnement | Par défaut | Description | +| Variable d'environnement | Valeur par défaut | Description | | --- | --- | --- | | `VOCAT_ADDR` | `0.0.0.0:7575` | Adresse d'écoute HTTP. | | `VOCAT_DATABASE_PATH` | `./data/vocat.db` | Chemin de la base de données SQLite. | -| `VOCAT_SESSION_TTL` | `24h` | Durée de vie de la session d'authentification. | -| `VOCAT_SECURE_COOKIES` | `false` | Marque les cookies de session comme sécurisés lorsque HTTPS est utilisé. | +| `VOCAT_SESSION_TTL` | `24h` | Durée de validité de la session d'authentification. | +| `VOCAT_SECURE_COOKIES` | `false` | Marque les cookies de session comme sécurisés en cas d'utilisation de HTTPS. | | `VOCAT_SHUTDOWN_TIMEOUT` | `10s` | Délai d'arrêt gracieux. | | `VOCAT_MAX_REQUEST_BODY_BYTES` | `1048576` | Taille maximale du corps de requête API. | -| `VOCAT_REPO` | `MengMengCode/VoCat` | Dépôt GitHub de confiance utilisé par l'auto-updater, au format `owner/name`. | +| `VOCAT_MCP_ENABLED` | `true` | Exposer le point de terminaison MCP sur `/mcp` pour les agents IA. Définissez `false` pour le désactiver. | +| `VOCAT_PROBE_UNKNOWN_USB` | `false` | Autoriser le sondage par commandes AT des périphériques série USB non reconnus. Le sondage modifie les paramètres série et vide le tampon d'entrée ; activez uniquement si ces ports sont réservés aux modems. Les identités des modems vérifiés sont découvertes automatiquement. Clé JSON : `probe_unknown_usb`. | +| `VOCAT_REPO` | `MengMengCode/VoCat` | Dépôt GitHub de confiance utilisé par l'auto-mise à jour, sous la forme `propriétaire/nom`. | | `GITHUB_TOKEN` | vide | Jeton GitHub optionnel pour les dépôts privés ou des limites d'API plus élevées. | -Les bundles opérateur Apple fournis par l'utilisateur peuvent être convertis avec `vocat carrier import-ipcc` en profils opérateur vérifiables et limités à une liste d'éléments autorisés ; voir [docs/CARRIER_IPCC_IMPORT.md](CARRIER_IPCC_IMPORT.md). +Les paquets de configuration opérateur Apple fournis par l'utilisateur peuvent être convertis en profils opérateur examinables et conformes à une liste d'autorisation avec `vocat carrier import-ipcc` ; voir [docs/CARRIER_IPCC_IMPORT.md](CARRIER_IPCC_IMPORT.md). + +Les identifiants administrateur sont enregistrés uniquement dans SQLite. Initialisez une base de données vide une seule fois avec `vocat bootstrap-admin` ; les variables d'environnement et la configuration JSON ne peuvent pas définir ou écraser le nom d'utilisateur ou le mot de passe administrateur. + +Ne stockez pas de jetons Telegram, de mots de passe SMTP, de secrets de webhook, d'identifiants SIM ou d'autres données privées dans le dépôt. Configurez-les via les paramètres de l'application ou des fichiers d'environnement protégés. + +## Authentification unique (SSO) + +VoCat prend en charge Pocket ID via OpenID Connect, les applications OAuth GitHub.com et les fournisseurs Generic OAuth2. Sélectionnez un fournisseur dans **Paramètres → Authentification unique (SSO)**. Le SSO est désactivé par défaut et la connexion par mot de passe reste disponible. Connectez-vous d'abord avec le compte administrateur local, enregistrez un fournisseur et associez explicitement un compte externe avant d'activer le SSO. Les autres comptes externes ne peuvent pas s'inscrire ni devenir administrateurs, même s'ils partagent la même adresse e-mail que le compte associé. + +### Pocket ID + +1. Utilisez une origine HTTPS pour VoCat, telle que `https://vocat.example.com`, et définissez `VOCAT_SECURE_COOKIES=true`. Dans les paramètres SSO, choisissez **Pocket ID**. L'origine publique utilise initialement l'adresse actuelle du navigateur ; modifiez le champ visible **Origine publique de VoCat** si nécessaire. L'URL de rappel compacte et le bouton de copie apparaissent immédiatement, ce qui permet de copier l'URL avant de créer le client OIDC ou d'enregistrer des identifiants : `https://vocat.example.com/api/auth/sso/callback`. +2. Dans Pocket ID, créez un client OIDC distinct de type **Confidential** pour VoCat. Enregistrez l'URL de rappel exacte et vérifiez que l'utilisateur ou le groupe ciblé est autorisé à utiliser ce client. VoCat et Komari nécessitent des clients distincts et des sessions locales séparées. +3. Dans VoCat, saisissez l'URL publique exacte (`APP_URL`) de Pocket ID comme Issuer, puis saisissez le Client ID et le Client Secret émis pour le client créé à l'étape 2. Le champ Client ID inclut un rappel de l'emplacement de cette valeur. Les portées (scopes) et PKCE S256 sont gérés par VoCat. L'authentification du client pour le jeton est sélectionnée à partir des métadonnées Discovery validées, en préférant `client_secret_basic` à `client_secret_post`. Enregistrez tout en laissant le SSO désactivé. La modification de l'origine met immédiatement à jour l'URL de rappel affichée ; elle prend effet pour l'autorisation après l'enregistrement. +4. Vérifiez la configuration enregistrée ; en cas de succès, le message **Vérification de la configuration réussie** s'affiche. Associez ensuite le compte. Effectuez la connexion par Passkey et le consentement dans Pocket ID, puis confirmez l'association retournée dans VoCat. La vérification de configuration valide uniquement les métadonnées et la connectivité réseau ; elle ne peut pas vérifier le secret client ni l'autorisation de l'utilisateur sans cette connexion. +5. Activez le SSO et enregistrez. Utilisez une fenêtre de navigation privée pour vérifier la connexion SSO, le retour à la page demandée et la première requête modifiant l'état. + +La page des paramètres affiche le compte associé et une action de dissociation. Pour modifier le compte externe, dissociez-le d'abord, puis associez le nouveau compte souhaité et réactivez le SSO. + +L'Issuer est comparé de manière stricte, y compris son chemin et la barre oblique finale. La connexion Pocket ID utilise `issuer + sub` du jeton ID validé ; aucun point de terminaison UserInfo ni revendication d'e-mail n'est requis. Discovery et ses points de terminaison d'autorisation, de jeton (Token) et JWKS doivent utiliser l'origine de l'Issuer configuré. Les déploiements utilisant `INTERNAL_APP_URL` avec une origine backend différente ne sont pas pris en charge dans cette version. Conservez l'`APP_URL` publique comme Issuer. Les méthodes d'authentification et les scopes explicitement configurés sont conservés lors de l'enregistrement. + +### GitHub et Generic OAuth2 + +Pour GitHub, créez une application OAuth sur GitHub.com avec exactement la même URL de rappel, sélectionnez **GitHub**, saisissez son Client ID et son Secret, puis suivez la séquence enregistrement, vérification, association et activation ci-dessus. Les points de terminaison sont fixes sur GitHub.com ; la portée par défaut est `read:user` et PKCE S256 est requis. L'association repose sur l'identifiant numérique stable du compte GitHub, ainsi changer de nom d'utilisateur ne modifie pas l'association. GitHub Enterprise ne fait pas partie du périmètre de cette version. + +Pour Generic OAuth2, fournissez les URL d'autorisation, de jeton (Token) et UserInfo en plus des identifiants client. Les scopes, le champ d'identifiant utilisateur de premier niveau (par défaut `id`), l'authentification par jeton et les options de compatibilité PKCE sont affichés lorsque Generic OAuth2 est sélectionné. UserInfo est interrogé avec un jeton d'accès Bearer. L'ID doit être une chaîne non vide ou un entier ; les grands identifiants entiers conservent leur valeur exacte. L'authentification par jeton par défaut est `client_secret_post` et PKCE est S256. Désactivez PKCE uniquement pour un fournisseur qui ne le prend pas en charge. Generic OAuth2 n'effectue pas de vérification de jeton ID OIDC. + +### Réseau, identifiants et sessions + +La politique de réseau source existante de VoCat s'applique également aux routes SSO. La configuration du SSO ne rend pas une installation accessible publiquement. Le navigateur doit pouvoir joindre VoCat et le point de terminaison d'autorisation ; le processus VoCat doit pouvoir joindre Discovery, les points de terminaison Token et JWKS/UserInfo avec des certificats TLS de confiance. L'enregistrement de l'Issuer HTTPS exact de Pocket ID ou des URL de Generic approuve ces adresses pour les connexions HTTPS, y compris les adresses privées RFC1918/IPv6 ULA. Les points de terminaison fixes de GitHub restent restreints aux adresses publiques. + +Pour le service HTTP interne d'un fournisseur Generic, saisissez explicitement son URL HTTP complète dans le champ Token/UserInfo. Chaque candidat DNS et connexion réelle vers ces destinations HTTP doit être une adresse privée autorisée. Discovery de Pocket ID ne peut pas approuver une autre origine ; ses points de terminaison doivent utiliser l'origine de l'Issuer configuré. L'Issuer, l'URL de récupération Discovery et le point de terminaison d'autorisation du navigateur exigent toujours HTTPS. Les adresses de bouclage (loopback), lien local (link-local), métadonnées cloud et autres adresses spéciales restent bloquées pour les connexions IdP. Installez les certificats CA privés dans le magasin de confiance de l'hôte/du conteneur ; il n'y a pas d'option pour contourner la vérification TLS. L'accès du navigateur à VoCat via localhost ou HTTPS est indépendant de ces autorisations de connexion IdP. -Les identifiants de l'administrateur sont stockés uniquement dans SQLite. Initialisez une base de données vide une seule fois avec `vocat bootstrap-admin` ; les variables d'environnement et la configuration JSON ne peuvent ni définir ni remplacer le nom d'utilisateur ou le mot de passe de l'administrateur. +Le déploiement Compose fourni utilise le réseau hôte. Vérifiez le DNS, le routage et la confiance CA depuis le conteneur lui-même ; ne supposez pas que les noms de services en réseau ponté (bridge) sont disponibles. Les proxys inverses doivent conserver l'origine publique configurée. VoCat ne déduit pas les URL de rappel à partir des en-têtes host transférés. Un déploiement à la racine est requis. Pour le développement local Vite, utilisez une origine de bouclage cohérente, telle que `http://127.0.0.1:5173`, pour l'origine publique et l'enregistrement de rappel ; les exceptions de transport IdP ne doivent être utilisées que dans les fixtures de test. -Ne stockez pas de jetons Telegram, mots de passe SMTP, secrets de webhook, identifiants SIM ou autres données privées dans le dépôt. Configurez-les via les paramètres de l'application ou des fichiers d'environnement protégés. +Les cookies de session et CSRF conservent `SameSite=Strict` ; les cookies d'autorisation éphémères utilisent `SameSite=Lax` pour les rappels GET de premier niveau. L'enregistrement de toute configuration SSO, y compris sa désactivation ou la rotation du secret, révoque toutes les sessions SSO. La dissociation d'un compte révoque les sessions SSO associées à cette liaison. Les sessions par mot de passe et les jetons d'API subsistent après ces actions. La modification du mot de passe de l'administrateur local révoque toutes les sessions et tous les jetons d'API, annule les autorisations en cours et conserve l'association externe. L'interface demande une nouvelle connexion lorsqu'une opération réussie révoque la session SSO actuelle. + +Laissez le champ du secret vide pour conserver un secret existant, ou entrez un nouveau secret pour le remplacer. La page des paramètres ne propose pas de commande pour effacer un secret enregistré. La modification du fournisseur, du Client ID, de l'Issuer ou des points de terminaison/champ d'ID de Generic bascule l'espace de noms d'identité, exige de nouveaux identifiants et une nouvelle association ; les associations existantes nécessitent une confirmation explicite de réinitialisation. Pour modifier le compte associé, dissociez-le d'abord puis créez une nouvelle association. Les secrets clients sont conservés uniquement sur le serveur dans la base de données SQLite protégée ; l'indicateur de paramètre sensible masque les réponses et ne chiffre pas la base de données. Les jetons tiers ne sont pas persistés ni renvoyés au navigateur. + +La déconnexion standard met fin uniquement à la session VoCat en cours. La désactivation d'un compte IdP ou la déconnexion de Pocket ID ne révoque pas immédiatement une session VoCat existante ; elle expire selon `VOCAT_SESSION_TTL` ou peut être révoquée en désactivant le SSO ou en dissociant le compte dans VoCat. Le point de terminaison public des fonctionnalités est `GET /api/auth/providers`. Les API de configuration et de liaison SSO nécessitent une session de navigateur et CSRF pour les écritures ; les jetons Bearer ne peuvent pas les gérer. Les conflits de révision de configuration renvoient `409 sso_config_changed` ; les changements de source d'identité renvoient `409 sso_rebind_required`. + +### Mise à niveau et restauration + +Le SSO introduit la version 29 du schéma SQLite. Les mises à niveau conservent l'administrateur existant, les sessions, les jetons d'API et les données d'appareils ; le SSO reste désactivé tant qu'il n'est pas configuré. Avant la mise à niveau, notez l'ancienne version binaire ou le digest de l'image, et sauvegardez la configuration, les certificats CA et le répertoire de données persistant complet pendant que VoCat est arrêté, ou utilisez l'API de sauvegarde SQLite / la commande `.backup` pour obtenir un instantané cohérent à chaud. La simple copie d'un fichier de base de données en mode WAL en cours d'exécution n'est pas une sauvegarde fiable. + +Désactivez le SSO dans la nouvelle version pour résoudre d'éventuels problèmes de fournisseur tout en conservant la connexion par mot de passe et les données métier actuelles. Le retour à un binaire avec le schéma 28 nécessite d'arrêter VoCat et de restaurer l'instantané de données et de configuration antérieur correspondant. La simple restauration de l'image du conteneur ou la diminution de `PRAGMA user_version` ne peut pas rétrograder la base de données. Restaurez si possible dans un répertoire/volume distinct et conservez la base de données la plus récente à des fins d'analyse. La restauration d'un instantané entraîne la perte des SMS et des modifications de tâches enregistrés après cette sauvegarde. Pour Docker, mettez à jour en tirant la nouvelle image et en recréant le conteneur. + +Avant un déploiement en production, effectuez le test réel Passkey/politique d'accès Pocket ID et une répétition de la mise à niveau et de la restauration. + +## Jetons d'API et MCP + +Les clients autres que les navigateurs s'authentifient auprès des mêmes points de terminaison `/api/...` à l'aide d'un jeton Bearer au lieu du cookie de session et du flux CSRF du navigateur : + +```bash +vocat token create --read-only --ttl 720h ci-runner # affiche le jeton une seule fois +curl -H "Authorization: Bearer vcat_..." http://127.0.0.1:7575/api/devices +``` + +`vocat token list` et `vocat token revoke ` gèrent l'inventaire. Les jetons disposent des privilèges administrateur ; un jeton `--read-only` ne peut exécuter que des requêtes `GET` (une exception documentée : la lecture d'un fil de SMS le marque localement comme lu — la règle étant « aucune modification matérielle ou réseau »). La rotation du mot de passe administrateur révoque tous les jetons. Créez des jetons après le premier `bootstrap-admin`. Les clients distants utilisant Bearer ont également besoin d'une politique d'accès réseau autorisant leur adresse source ; le mode par défaut `internal` ne dessert que les réseaux privés. + +Par défaut (définir `VOCAT_MCP_ENABLED=false` pour le désactiver), le même service expose un point de terminaison MCP (Model Context Protocol) pour les agents IA sur `http://:7575/mcp` en utilisant le transport Streamable HTTP et l'authentification par jeton Bearer. Les outils couvrent l'état des appareils, les fils de SMS et l'envoi sécurisé, la liste et la commutation des profils eSIM, ainsi que l'état et la reconnexion WiFi Calling ; les opérations sensibles (commandes AT directes, téléchargement/suppression d'eSIM, code PIN de la SIM, sélection de réseau, mise à jour du système) ne sont pas exposées, et la sortie des outils n'inclut jamais l'IMSI, le code PIN de la SIM ou les codes d'activation. Un jeton en lecture seule reçoit une liste d'outils dépourvue des outils d'écriture. L'envoi de SMS suit la même politique de numéros bloqués et la même limite horaire globale que l'interface Web, et s'effectue via le chemin AT cellulaire (le routage préférentiel IMS de l'interface Web lorsque WiFi Calling est activé reste côté Web, et les messages envoyés via MCP ne sont pas encore consignés dans l'historique des conversations Web). + +Exemple de configuration client (avec Claude Code) : + +```json +{ + "mcpServers": { + "vocat": { + "type": "http", + "url": "http://192.168.1.10:7575/mcp", + "headers": { "Authorization": "Bearer vcat_..." } + } + } +} +``` + +Consultez [docs/API_MCP_DEVELOPMENT_PLAN.md](API_MCP_DEVELOPMENT_PLAN.md) pour le document de conception. ## Bot Telegram @@ -230,13 +317,20 @@ Lorsque les notifications Telegram sont activées et que le Chat ID et l'Admin I /switch /wfc /sms +/ussd +/ussd_reply +/ussd_cancel ``` -La commutation de profil et l'envoi de SMS utilisent des boutons de confirmation à usage unique. Le bot n'expose pas les commandes de téléchargement, de suppression ou de renommage eSIM. +La commutation de profil et l'envoi de SMS utilisent des boutons de confirmation à usage unique. Le bot n'expose pas de commandes de téléchargement, suppression ou renommage d'eSIM. + +Le terminal web et Telegram partagent le routage USSD. Avec WiFi Calling activé, les requêtes utilisent IMS/USSI et renvoient une erreur d'attente jusqu'à l'enregistrement IMS, sans basculer sur AT+CUSD cellulaire. Avec WiFi Calling désactivé, les requêtes utilisent l'USSD cellulaire. Les réponses aux menus et les annulations restent liées à l'appareil et au dialogue d'origine. Les erreurs d'annulation sont signalées et conservent la session pour une nouvelle tentative ; un envoi incertain n'est jamais réémis automatiquement sur un autre canal. + +La gestion des dialogues USSI est conforme à la norme [3GPP TS 24.390](https://www.etsi.org/deliver/etsi_ts/124300_124399/124390/18.00.00_60/ts_124390v180000p.pdf). Un échec de BYE conserve un enregistrement d'annulation, mais toute saisie supplémentaire dans le menu est bloquée dès que l'annulation est initiée ([RFC 3261, section 15.1.1](https://www.rfc-editor.org/rfc/rfc3261.html#section-15.1.1)). ## Mise à jour -Vérifier l'existence d'une GitHub Release plus récente : +Vérifier la présence d'une version GitHub Release plus récente : ```bash vocat update --check --repo MengMengCode/VoCat @@ -248,7 +342,7 @@ Installer la dernière version : sudo vocat update --repo MengMengCode/VoCat ``` -L'updater télécharge le binaire correspondant à l'architecture Linux actuelle, le vérifie avec le `SHA256SUMS` publié, remplace l'exécutable de manière atomique et redémarre le service systemd `vocat` lorsqu'il est disponible. +Le programme de mise à jour télécharge le binaire correspondant à l'architecture Linux actuelle, le vérifie avec le fichier `SHA256SUMS` publié, remplace l'exécutable de manière atomique et redémarre le service systemd `vocat` lorsqu'il est disponible. Pour les installations Docker : @@ -256,7 +350,7 @@ Pour les installations Docker : docker pull ghcr.io/mengmengcode/vocat:latest ``` -Recréez le conteneur après avoir tiré la nouvelle image. +Recréez le conteneur après avoir téléchargé la nouvelle image. ## Développement @@ -274,7 +368,7 @@ npm install npm run dev ``` -Construire le frontend intégré et démarrer le backend : +Compiler le frontend intégré et démarrer le backend : ```bash cd web @@ -289,7 +383,7 @@ Exécuter tous les tests : go test ./... ``` -Construire un binaire de production : +Compiler un binaire de production : ```bash go build -trimpath -ldflags "-s -w" -o vocat ./cmd/vocat @@ -297,10 +391,10 @@ go build -trimpath -ldflags "-s -w" -o vocat ./cmd/vocat ## Automatisation des versions -Pousser un tag de version déclenche deux workflows GitHub Actions : +Pousser un tag de version déclenche deux flux de travail GitHub Actions : -- `release-binaries` construit et publie les binaires `amd64`, `386`, `arm64`, `aarch64` et `armv7` ainsi que `SHA256SUMS`. -- `docker` construit et publie une image multi-architecture vers GitHub Container Registry. +- `release-binaries` compile et publie les binaires `amd64`, `386`, `arm64`, `aarch64` et `armv7` ainsi que `SHA256SUMS`. +- `docker` compile et publie une image multi-architecture sur GitHub Container Registry. ```bash git tag v0.2.0 @@ -312,27 +406,27 @@ git push origin v0.2.0 ```text cmd/vocat/ Point d'entrée de l'application et CLI internal/device/ Découverte de modems et contrôle des appareils -internal/modem/ Session AT et gestion des réponses +internal/modem/ Session AT et traitement des réponses internal/server/ API HTTP, notifications et serveur web intégré internal/store/ Persistance SQLite -internal/update/ Auto-updater GitHub Release -internal/vowifi/ Runtime IKE, EAP-AKA, IMS et WiFi Calling -scripts/install.sh Installeur et updater Linux +internal/update/ Auto-mise à jour via GitHub Release +internal/vowifi/ Environnement d'exécution IKE, EAP-AKA, IMS et WiFi Calling +scripts/install.sh Script d'installation et de mise à jour Linux web/src/ Frontend React et TypeScript .github/workflows/ Automatisation des versions binaires et Docker ``` ## Utilisation responsable -Les opérations sur les modems cellulaires et les eSIM peuvent affecter le service de l'abonné, les profils stockés, l'enregistrement réseau et l'état du matériel. Effectuez des sauvegardes, examinez attentivement les actions destructrices et n'utilisez le logiciel que dans des environnements légaux où vous êtes autorisé à exploiter le matériel et les ressources réseau connectés. +Les opérations sur les modems cellulaires et les eSIM peuvent affecter le service abonné, les profils enregistrés, l'enregistrement réseau et l'état du matériel. Conservez des sauvegardes, examinez attentivement les actions destructrices et n'utilisez le logiciel que dans des environnements légitimes où vous êtes autorisé à exploiter le matériel et les ressources réseau connectés. -Vocat ne contourne ni l'authentification de l'opérateur, ni la politique réseau, ni la sécurité matérielle, ni les exigences de confiance eSIM. La prise en charge d'une opération signifie que Vocat peut la demander au modem ou à l'eUICC ; l'appareil, le profil, le réseau ou l'opérateur peut toujours la refuser. +Vocat ne contourne pas l'authentification de l'opérateur, les politiques réseau, la sécurité matérielle ou les exigences de confiance eSIM. La prise en charge d'une opération signifie que Vocat peut la demander au modem ou à l'eUICC ; l'appareil, le profil, le réseau ou l'opérateur peuvent toujours la refuser. ## Contribution -Les issues et pull requests sont les bienvenues. Gardez des changements ciblés, incluez des tests lorsque c'est possible, évitez de committer des identifiants ou des données d'abonnés, et documentez clairement les comportements spécifiques au matériel. +Les issues et pull requests sont les bienvenues. Gardez les modifications ciblées, incluez des tests lorsque c'est faisable, évitez d'enregistrer des identifiants ou des données d'abonnés et documentez clairement les comportements spécifiques au matériel. -Avant de soumettre un changement : +Avant de soumettre une modification : ```bash go test ./... @@ -356,10 +450,12 @@ cd web && npm run build Voir [LICENSE](../LICENSE). - +## Star History + + - - - Star History Chart + + + Star History Chart - +\n \ No newline at end of file diff --git a/docs/README.ja.md b/docs/README.ja.md index 09fd359d..93c586df 100644 --- a/docs/README.ja.md +++ b/docs/README.ja.md @@ -24,7 +24,7 @@ [English](../README.md) | [العربية](README.ar.md) | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [Français](README.fr.md) | [Русский](README.ru.md) | [Español](README.es.md) | **日本語** -Vocat は、Quectel EC20/EC25 クラスのセルラーモデム向けのオープンソース Web コントロールパネル兼エンジニアリングツールキットです。モデムの検出、ライブの無線ステータス、AT / USSD ターミナル、SMS、WiFi Calling、eSIM 管理、ネットワーク選択、プロキシルーティング、通知、監査ログ、リリース自動化を、自己完結型の単一サービスに統合しています。 +Vocat は、Quectel EC20/EC25 クラスおよび China Mobile ML307 シリーズのセルラーモデム向けのオープンソース Web コントロールパネル兼エンジニアリングツールキットです。モデムの検出、リアルタイムの無線ステータス、AT / USSD ターミナル、SMS、WiFi Calling、eSIM 管理、ネットワーク選択、プロキシルーティング、通知、監査ログ、リリース自動化を、自己完結型の単一サービスに統合しています。 バックエンドは Go で書かれ、インターフェースは React と TypeScript で構築され、本番フロントエンドは Go バイナリに埋め込まれています。単一の実行ファイルに Web アプリケーション全体が含まれ、永続的な状態には SQLite を使用します。 @@ -38,7 +38,7 @@ Vocat は、Quectel EC20/EC25 クラスのセルラーモデム向けのオー | 領域 | Vocat が提供する機能 | | --- | --- | | デバイス管理 | シリアル/USB の自動検出、複数モデムのサポート、わかりやすいデバイス名、概要のライブ更新、モジュールの再起動、機内モード、USB ネットワークモードの制御。 | -| 無線とネットワーク | 登録ステータス、オペレーター、信号指標、RSRP/RSRQ/SINR、ネットワークモード、バンド、チャネル、オペレータースキャン、自動または手動のネットワーク選択。 | +| 無線とネットワーク | 登録ステータス、オペレーター、信号指標、RSRP/RSRQ/SINR、ネットワークモード、バンド、チャネル、オペレータースキャン、自動または手動のネットワーク選択、および LTE セルクエリと単一セルロック/アンロック(EARFCN/PCI)。 | | AT と USSD | 対話型 AT ターミナル、コマンド履歴、モデムの生出力、USSD の開始/継続/キャンセルフロー、明確なモデムエラーレポート。 | | SMS | セルラーおよび IMS SMS の直接送信、受信同期、マルチパート処理、配信レポート、会話履歴、未読状態、タイムスタンプ、メッセージごとの配信ステータス。 | | WiFi Calling | IKEv2/ePDG トンネルの確立、EAP-AKA 認証、IMS 登録、IMS SMS、再接続制御、ステータス診断、デバイスごとのルーティング。 | @@ -47,17 +47,18 @@ Vocat は、Quectel EC20/EC25 クラスのセルラーモデム向けのオー | プロキシルーティング | アップストリーム SOCKS ルーティング、デバイスバインディング、国別ルール、TCP 到達性チェック、WiFi Calling データパス向けの UDP Associate チェック。 | | 通知 | Telegram、Bark、メール、Pushplus、署名付き Webhook を介した新着 SMS の転送。各 SMS は個別の通知として配信されます。 | | Telegram ボット | デバイスステータス、インストール済みプロファイルの一覧と切り替え、WiFi Calling 制御、SMS 送信。機密性の高い操作には管理者の確認が必要です。 | -| 運用 | 認証、CSRF 保護、アクセスポリシー、監査イベント、ライブログ、ログ保持、ヘルスチェック、レスポンシブレイアウト、ダークモード、英語/中国語のアプリケーション UI。 | +| 運用 | パスワードおよび SSO 認証(Pocket ID、GitHub、Generic OAuth2)、CSRF 保護、アクセスポリシー、監査イベント、リアルタイムログ、ログ保持、ヘルスチェック、レスポンシブレイアウト、ダークモード、英語/中国語のアプリケーション UI。 | | 配布 | 静的 Linux バイナリ、systemd インストールスクリプト、SHA-256 検証付きの自己更新、Docker イメージ、GHCR 公開、GitHub Actions リリースビルド。 | ## 対応ハードウェア -Vocat は、互換性のある AT、QMI、シリアル、USB ネットワークインターフェースを公開する Qualcomm ベースの Quectel モジュールを対象としています。対象には以下が含まれます: +Vocat は、互換性のある AT、QMI、シリアル、USB ネットワークインターフェースを公開する Qualcomm ベースの Quectel モジュールおよび China Mobile ML307 シリーズモジュールを対象としています。対象には以下が含まれます: - Quectel EC20 - Quectel EC25 - Quectel EG25 ファミリー - 互換性のある EG600 および関連モジュール +- China Mobile ML307 シリーズ(例: ML307A-E5、各種 OEM) 利用可能な機能は、モジュールのファームウェア、USB 構成、SIM/eSIM の機能、ホストドライバー、無線ネットワーク、キャリア設定によって異なります。 @@ -65,51 +66,51 @@ Vocat は、互換性のある AT、QMI、シリアル、USB ネットワーク ### ワンクリック Linux インストール -root として(`sudo` が通常存在しない OpenWrt/Kwrt を含む): +root ユーザー(`sudo` が通常存在しない OpenWrt/Kwrt を含む)として実行: ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh | bash ``` -sudo を持つディストリビューションの一般ユーザーから: +sudo を備えたディストリビューションの通常ユーザーから実行: ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh | sudo bash ``` -VoCat をインストールせずに、ホストの VoWiFi/XFRM 前提条件を確認する: +VoCat をインストールせずにホストの VoWiFi/XFRM 前提条件を確認する: ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh | bash -s -- --check-env ``` -特定のバージョンをインストールする: +特定バージョンをインストールする: ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh -o install.sh sudo bash install.sh 0.0.2 ``` -VoWiFi IMS には Linux XFRM/IPsec が必要です。OpenWrt/Kwrt では、インストーラーはファームウェア自身のフィードから、一致する `ip-full`、`kmod-ipsec`、`kmod-ipsec4/6`、`kmod-crypto-authenc`、AES-CBC、SHA1 パッケージのインストールを試みます。一致するカーネルモジュールが利用できない場合は、それらを含むファームウェアを使用してください。別のカーネル向けにビルドされた kmod を強制的にインストールしてはいけません。 +VoWiFi IMS には Linux XFRM/IPsec が必要です。OpenWrt/Kwrt では、インストーラーがファームウェア自身のフィードから適合する `ip-full`、`kmod-ipsec`、`kmod-ipsec4/6`、`kmod-crypto-authenc`、AES-CBC および SHA1 パッケージのインストールを試みます。適合するカーネルモジュールが利用できない場合は、それらが含まれるファームウェアを使用してください。別のカーネル向けにビルドされた kmod を強制インストールしないでください。 -カーネルが XFRM/IPsec を提供できず、セルラー SMS やデータ通信など VoWiFi 以外の機能のみが必要な場合は、`--skip-vowifi-check` を指定してインストールしてください: +カーネルが XFRM/IPsec を提供できず、セルラー SMS やデータなどの非 VoWiFi 機能のみが必要な場合は、`--skip-vowifi-check` を指定してインストールしてください: ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh -o install.sh sudo bash install.sh --skip-vowifi-check ``` -インストーラーは次を行います: +インストーラーの処理内容: -- `amd64`、`386`、`arm64`、`aarch64`、`armv7` を検出します; -- 一致する GitHub Release バイナリをダウンロードします; -- `SHA256SUMS` と照合して検証します; -- Vocat を `/opt/vocat` にインストールします; -- Vocat が必要とするハードウェアおよびネットワークアクセスを持つ強化された systemd サービスを作成します; -- 実行時設定を `/etc/vocat/env` に保存します; +- `amd64`、`386`、`arm64`、`aarch64`、または `armv7` を検出します。 +- 一致する GitHub Release バイナリをダウンロードします。 +- `SHA256SUMS` と照合して検証します。 +- Vocat を `/opt/vocat` にインストールします。 +- Vocat に必要なハードウェアおよびネットワークアクセス権を備えた、セキュリティ強化された systemd サービスを作成します。 +- 実行時設定を `/etc/vocat/env` に保存します。 - 初回インストール時にランダムな初期管理者パスワードを生成します。 -インストール後、次を開きます: +インストール後、以下を開きます: ```text http://:7575 @@ -117,7 +118,7 @@ http://:7575 ### 手動バイナリインストール -一致するバイナリと `SHA256SUMS` を GitHub Releases からダウンロードします: +GitHub Releases から対応するバイナリと `SHA256SUMS` をダウンロードします: | プラットフォーム | リリースファイル | | --- | --- | @@ -141,11 +142,11 @@ sudo env \ /opt/vocat/bin/vocat serve ``` -この手動コマンドは Vocat をフォアグラウンドで実行します。プロセスがサーバーを直接起動するように `vocat serve` を使用してください。TTY で root として引数なしで `vocat` を実行すると、代わりに対話型管理メニューが開きます。管理対象の systemd サービスと自動再起動が必要な場合は、ワンクリックインストーラーを使用してください。 +この手動コマンドは Vocat をフォアグラウンドで実行します。プロセスがサーバーを直接起動するように `vocat serve` を使用してください。TTY 上で root として引数なしで `vocat` を実行すると、代わりに対話型管理メニューが開きます。管理された systemd サービスと自動再起動が必要な場合は、ワンクリックインストーラーを使用してください。 ### Docker -接続されているすべてのサポート対象 Quectel モデムを検出し、USB ホットプラグイベントを継続的に認識する必要がある Linux ホストでは、Vocat をハードウェアアクセスモードで実行します: +接続されているすべての対応 Quectel モデムを検出し、USB ホットプラグイベントを認識し続ける必要がある Linux ホストの場合は、ハードウェアアクセスモードで Vocat を実行します: ```bash docker pull ghcr.io/mengmengcode/vocat:latest @@ -172,7 +173,21 @@ docker run -d \ コンテナの起動後に `http://:7575` を開きます。QMI ネットワークインターフェースを Vocat から引き続き認識できるようにするにはホストネットワークが必要です。また、シリアルポート、QMI 制御ノード、TUN インターフェース、ネットワーク設定、コンテナ起動後に追加されたデバイスへのアクセスには特権が必要です。`/dev` のバインドマウントにより、コンテナを再作成せずに新しい `ttyUSB*`、`ttyACM*`、`cdc-wdm*` および MHI の `wwan*` ノードを認識できます。 -このモードは意図的に Vocat にホストのデバイスとネットワークスタックへの広範なアクセス権を与えます。信頼できる Linux ホストでのみ使用してください。自動検出は、サポート対象の Quectel USB モデム(USB ベンダー ID `2c7c`)と、Linux WWAN サブシステムを通じて公開される PCIe/MHI モデムを識別します。任意のモデム構成を識別するものではありません。`--device` で `/dev/ttyUSB2`、`/dev/cdc-wdm0`、`/dev/wwan0qmi0` などの個別ノードのみをマッピングすると、コンテナはそれらの固定ノードに限定され、複数デバイスやホットプラグを完全には検出できません。 +このモードは意図的に Vocat にホストのデバイスとネットワークスタックへの広範なアクセス権を与えます。信頼できる Linux ホストでのみ使用してください。自動検出は、サポート対象の Quectel USB モデム(USB ベンダー ID `2c7c`)および製品文字列による China Mobile ML307 シリーズモジュール、ならびに Linux WWAN サブシステムを通じて公開される PCIe/MHI モデムを識別します。任意のモデム構成を識別するものではありません。`--device` で `/dev/ttyUSB2`、`/dev/cdc-wdm0`、`/dev/wwan0qmi0` などの個別ノードのみをマッピングすると、コンテナはそれらの固定ノードに限定され、複数デバイスやホットプラグを完全には検出できません。 + +OpenWrt ホストでは、fw4 の input チェーンが管理外インターフェース(Vocat がセッションごとに作成する VoWiFi TUN を含む)からの新規接続を破棄します。そのため Vocat は、イメージ同梱の `nft` を使用して狭い範囲の `vocat-ims-*` 許可ルール(P-CSCF からネゴシエートされた内部アドレス宛て)を挿入します。これらがない場合、IMS 登録や発信 SMS は動作しますが、着信 SMS や通話は拒否されます。同梱の `nft` がホストのルールセットを読み取れない場合、Vocat は `nft could not inspect the OpenWrt fw4 input chain` をログ出力します。その場合は `VOCAT_NFT` をホスト自身の nft に指定してください: + +```bash +cat > /opt/vocat-host-nft <<'EOF' +#!/bin/sh +exec /host/lib/ld-musl-$(uname -m).so.1 --library-path /host/usr/lib:/host/lib /host/usr/sbin/nft "$@" +EOF +chmod 755 /opt/vocat-host-nft +# 上記の docker run コマンドに追加: +# -v /usr:/host/usr:ro -v /lib:/host/lib:ro \ +# -v /opt/vocat-host-nft:/usr/local/bin/host-nft:ro \ +# -e VOCAT_NFT=/usr/local/bin/host-nft +``` GHCR イメージは `linux/amd64` と `linux/arm64` 向けに公開されています。 @@ -191,6 +206,11 @@ VoCat は `qmicli` を使って QMI 制御チャネルの準備ができてい `vocat doctor --repair-dji-qmi` は、USB ドライバーの関連付けを変更したり、DTR 信号をアサートしたりする前に、`qmicli` の有無を確認します。ユーティリティが利用できない場合、コマンドはインストール方法を案内して停止し、デバイスの現在の状態を変更しません。 +### AT セルラーホストデータ + +AT ベースの ECM/RNDIS データには Linux、iproute2、および `udhcpc` を含む BusyBox が必要です。 +VoCat はモデムのネットワークインターフェースを排他的に管理する必要があります。そのインターフェースに対して競合する DHCP クライアントや NetworkManager プロファイルを無効化してください。VoCat はシステムのデフォルトルートやリゾルバ設定を変更することなく、IPv4 リースとマーク付きポリシールートを維持します。このパスは現在 IPv4 対応のサービスを必要とします。 + ## 設定 Vocat は `VOCAT_CONFIG` からオプションの JSON 設定ファイルを読み込み、次に `VOCAT_*` 環境変数を適用します。環境変数が優先されます。 @@ -203,6 +223,8 @@ Vocat は `VOCAT_CONFIG` からオプションの JSON 設定ファイルを読 | `VOCAT_SECURE_COOKIES` | `false` | HTTPS 使用時にセッション Cookie をセキュアとしてマークします。 | | `VOCAT_SHUTDOWN_TIMEOUT` | `10s` | グレースフルシャットダウンのタイムアウト。 | | `VOCAT_MAX_REQUEST_BODY_BYTES` | `1048576` | API リクエストボディの最大サイズ。 | +| `VOCAT_MCP_ENABLED` | `true` | AI エージェント向けに `/mcp` で MCP エンドポイントを提供します。無効にするには `false` を設定します。 | +| `VOCAT_PROBE_UNKNOWN_USB` | `false` | 未認識の USB シリアルデバイスを AT コマンドでプローブすることをオプトインします。プローブはシリアル設定を変更し入力をクリアするため、これらのポートがモデム専用である場合のみ有効にしてください。検証されたモデム ID は自動的に検出されます。JSON キー: `probe_unknown_usb`。 | | `VOCAT_REPO` | `MengMengCode/VoCat` | 自己更新機能が使用する信頼された GitHub リポジトリ(`owner/name` 形式)。 | | `GITHUB_TOKEN` | 空 | プライベートリポジトリやより高い API レート制限のためのオプションの GitHub トークン。 | @@ -212,6 +234,79 @@ Vocat は `VOCAT_CONFIG` からオプションの JSON 設定ファイルを読 Telegram トークン、SMTP パスワード、Webhook シークレット、SIM 認証情報、その他のプライベートデータをリポジトリに保存しないでください。アプリケーション設定または保護された環境ファイルを通じて設定してください。 +## シングルサインオン (SSO) + +VoCat は OpenID Connect による Pocket ID、GitHub.com OAuth App、および Generic OAuth2 プロバイダーをサポートしています。**設定 → シングルサインオン (SSO)** でプロバイダーを 1 つ選択します。SSO は初期状態では無効になっており、パスワードログインは引き続き利用可能です。ローカル管理者としてログインし、プロバイダーを保存して、外部アカウントを明示的にバインドしてから SSO を有効にしてください。バインドされていない外部アカウントは登録や管理者昇格を行うことができず、バインドされたアカウントと同じメールアドレスを共有していても自動マージされることはありません。 + +### Pocket ID + +1. `https://vocat.example.com` などの HTTPS オリジンを VoCat に使用し、`VOCAT_SECURE_COOKIES=true` を設定します。SSO 設定で **Pocket ID** を選択します。公開オリジンには最初現在のブラウザアドレスが入力されます。必要に応じて表示される **VoCat 公開オリジン** フィールドを編集してください。コンパクトなコールバック URL とコピーボタンがすぐに表示されるため、OIDC クライアントを作成したり認証情報を保存したりする前に URL をコピーできます: `https://vocat.example.com/api/auth/sso/callback`。 +2. Pocket ID で VoCat 用に個別の **Confidential** OIDC クライアントを作成します。正確なコールバック URL を登録し、対象のユーザーまたはグループがこのクライアントの使用を許可されていることを確認します。VoCat と Komari は別個のクライアントとローカルセッションが必要です。 +3. VoCat で Pocket ID の正確な公開 `APP_URL` を Issuer として入力し、ステップ 2 で作成したクライアントに発行された Client ID と Client Secret を入力します。Client ID フィールドにはこの値の確認場所のリマインダーが含まれています。スコープと PKCE S256 は VoCat が自動処理します。トークンクライアント認証は検証された Discovery メタデータから自動選択され、`client_secret_post` よりも `client_secret_basic` が優先されます。SSO を無効にしたまま保存します。オリジンを編集すると表示されるコールバックが即座に更新され、保存後に実際の認可に反映されます。 +4. 保存された設定をチェックします。成功すると **設定チェックに合格しました** と表示されます。次にアカウントをバインドします。Pocket ID の Passkey ログインと同意を完了し、VoCat に返されたバインディングを確認します。設定チェックはメタデータと接続性のみを検証し、このログインを行わずにクライアントシークレットやユーザー認可を検証することはできません。 +5. SSO を有効にして保存します。シークレットブラウザウィンドウを使用して、SSO ログイン、要求されたページへの復帰、およびログイン後最初の状態変更リクエストを検証します。 + +設定ページにはバインドされたアカウントとアンバインド操作が表示されます。外部アカウントを変更するには、まずアンバインドしてから対象のアカウントをバインドし、再度 SSO を有効にしてください。 + +Issuer はパスや末尾のスラッシュを含めて正確に照合されます。Pocket ID のログインは検証された ID トークンの `issuer + sub` を使用するため、UserInfo エンドポイントやメールクレームは不要です。Discovery およびその認可、Token、JWKS エンドポイントは設定された Issuer のオリジンと一致している必要があります。異なるバックエンドオリジンを持つ `INTERNAL_APP_URL` を使用したデプロイは現時点ではサポートされていません。公開 `APP_URL` を Issuer として保持してください。保存時には、明示的に設定された既存の認証方式やスコープが保持されます。 + +### GitHub および Generic OAuth2 + +GitHub の場合、GitHub.com でまったく同一のコールバック URL を持つ OAuth App を作成し、**GitHub** を選択して Client ID と Secret を入力し、上記の保存・チェック・バインド・有効化の手順に従います。エンドポイントは GitHub.com に固定され、デフォルトのスコープは `read:user` であり、PKCE S256 が必須です。バインディングは不変の GitHub 数値アカウント ID に基づくため、ユーザー名を変更してもアカウントは変わりません。GitHub Enterprise は本リリースの対象外です。 + +Generic OAuth2 の場合、クライアント認証情報に加えて認可、Token、および UserInfo の各 URL を指定します。Generic OAuth2 を選択すると、スコープ、トップレベルのユーザー ID フィールド名(デフォルトは `id`)、Token 認証および PKCE 互換性オプションが表示されます。UserInfo は Bearer アクセストークンでリクエストされます。ID は空でない文字列または整数である必要があり、大きな整数 ID でも正確な値が保持されます。デフォルトの Token 認証は `client_secret_post` で、PKCE は S256 です。PKCE をサポートしていないプロバイダーに対してのみ PKCE を無効にしてください。Generic OAuth2 は OIDC ID トークン検証を実行しません。 + +### ネットワーク、認証情報、セッション + +VoCat の既存の送信元ネットワークポリシーは SSO ルートにも適用されます。SSO を設定しても、インスタンスが自動的に一般公開されることはありません。ブラウザは VoCat および認可エンドポイントに到達できる必要があり、VoCat プロセスは Discovery、Token、JWKS/UserInfo エンドポイントに信頼できる TLS 証明書で到達できる必要があります。正確な Pocket ID HTTPS Issuer または Generic エンドポイント URL を保存すると、RFC1918/IPv6 ULA プライベートアドレスを含むこれらのアドレスへの HTTPS 接続が承認されます。GitHub の固定エンドポイントは引き続きパブリックアドレスに制限されます。 + +Generic プロバイダーの内部 HTTP サービスの場合、Token/UserInfo フィールドに完全な HTTP URL を明示的に入力してください。これらの HTTP 送信先に対するすべての DNS 候補および実際の接続先は、許可されたプライベートアドレスである必要があります。Pocket ID の Discovery は別のオリジンを自動承認することはできません。そのエンドポイントは設定された Issuer のオリジンと一致している必要があります。Issuer、Discovery 取得 URL、ブラウザ認可エンドポイントは常に HTTPS を必要とします。ループバック、リンクローカル、クラウドメタデータ、その他の特殊用途のアドレスは IdP 接続に対して引き続きブロックされます。プライベート CA 証明書はホスト/コンテナの信頼ストアにインストールしてください。TLS 検証をバイパスするオプションはありません。ブラウザが VoCat にアクセスするための localhost または HTTPS オリジンは、これらの IdP 接続許可とは独立しています。 + +付属の Compose デプロイはホストネットワークを使用します。ブリッジネットワークのサービス名が使用可能であると想定せず、実際のコンテナ内から DNS、ルーティング、CA 信頼を検証してください。リバースプロキシは設定された公開オリジンを維持する必要があります。VoCat は転送された Host ヘッダーからコールバック URL を推測しません。ルートパスへのデプロイが必要です。ローカルの Vite 開発では、公開オリジンおよびコールバック登録用に `http://127.0.0.1:5173` などの一貫したループバックオリジンを使用してください。IdP トランスポートの例外はテストフィクスチャ専用です。 + +セッションおよび CSRF Cookie は `SameSite=Strict` を維持します。短期間の認可 Cookie はトップレベルの GET コールバックをサポートするために `SameSite=Lax` を使用します。SSO の無効化やシークレットのローテーションを含む SSO 設定の保存を行うと、すべての SSO セッションが失効します。アカウントをアンバインドすると、そのバインディングの SSO セッションが失効します。パスワードセッションおよび API トークンはこれらの操作後も保持されます。ローカル管理者のパスワードを変更すると、すべてのセッションと API トークンが失効し、進行中の認可はキャンセルされますが、外部バインディングは保持されます。操作が成功して現在の SSO セッションが失効した場合、UI に再ログインが要求されます。 + +既存のシークレットを保持するにはシークレット入力を空のままにし、置き換える場合は新しいシークレットを入力します。設定ページにはシークレットをクリアするコントロールはありません。プロバイダー、Client ID、Issuer、または Generic の身元エンドポイント/ID フィールドを変更すると、身元名前空間が切り替わり、新しい認証情報と新しいバインディングが必要になります。既存のバインディングがある場合は明示的なリセット確認が必要です。バインドされたアカウントを変更するには、まずアンバインドしてから新しいバインディングを作成してください。Client Secret はサーバー上の保護された SQLite データベースにのみ保持されます。機密設定フラグはレスポンスのマスキング用であり、データベースを暗号化するものではありません。サードパーティトークンは永続化されず、ブラウザにも返されません。 + +通常のサインアウトは現在の VoCat セッションのみを終了します。IdP でアカウントを無効化したり Pocket ID からサインアウトしたりしても、既存の VoCat セッションは即座には失効しません。セッションは `VOCAT_SESSION_TTL` に従って期限切れになるか、VoCat で SSO を無効にするかアンバインドすることで即座に失効させることができます。公開機能エンドポイントは `GET /api/auth/providers` です。SSO 設定およびバインディング API の書き込みにはブラウザセッションと CSRF が必要であり、Bearer トークンではこれらを管理できません。設定のリビジョン競合は `409 sso_config_changed` を返し、身元元の変更確認が必要な場合は `409 sso_rebind_required` を返します。 + +### アップグレードとロールバック + +SSO により SQLite スキーマバージョン 29 が追加されます。アップグレードでは既存の管理者、セッション、API トークン、デバイスデータが保持され、SSO は設定されるまで無効のままです。アップグレードの前に、古いバイナリまたはイメージのダイジェストを記録し、VoCat の停止中に設定、CA データ、完全な永続データディレクトリをバックアップするか、SQLite の backup API または `.backup` を使用して一貫したライブスナップショットを取得してください。実行中の WAL モードのデータベースファイルのみをコピーすることは信頼できるバックアップではありません。 + +プロバイダーの問題から復旧しつつ、パスワードログインと現在の業務データを維持するには、新しいバージョンで SSO を無効にします。スキーマ 28 のバイナリに戻すには、VoCat を停止し、一致するアップグレード前のデータと設定スナップショットを復元する必要があります。単にコンテナイメージを元に戻したり、`PRAGMA user_version` を下げたりするだけでは、データベースをダウングレードできません。可能な限り別のディレクトリ/ボリュームに復元し、調査のために新しいデータベースを保持してください。スナップショットを復元すると、バックアップ以降に記録されたメッセージやタスクの変更が失われます。Docker の場合は、イメージをプルしてコンテナを再作成することで更新します。 + +本番リリース前に、実際の Pocket ID Passkey/アクセスポリシーのテストと、そのデプロイ環境でのアップグレード/ロールバックの予行演習を完了してください。 + +## API トークンと MCP + +ブラウザ以外のクライアントは、ブラウザのセッション Cookie や CSRF フローの代わりに Bearer トークンを使用して同じ `/api/...` エンドポイントに対して認証を行います: + +```bash +vocat token create --read-only --ttl 720h ci-runner # トークンを一度だけ出力 +curl -H "Authorization: Bearer vcat_..." http://127.0.0.1:7575/api/devices +``` + +`vocat token list` および `vocat token revoke ` でインベントリを管理します。トークンは管理者権限で動作します。`--read-only` トークンは `GET` リクエストのみを発行できます(文書化された例外が 1 つあります: SMS スレッドを読み取るとローカルで既読としてマークされます — ルールは「ハードウェアやネットワークの変更を行わない」です)。管理者のパスワードをローテーションすると、すべてのトークンが失効します。初回の `bootstrap-admin` 実行後にトークンを作成してください。リモートの Bearer クライアントは、その送信元アドレスを許可するネットワークアクセスポリシーも必要です。デフォルトの `internal` モードはプライベートネットワークのみを許可します。 + +デフォルトでは(無効にするには `VOCAT_MCP_ENABLED=false` を設定)、同じサービスが Streamable HTTP トランスポートと Bearer トークン認証を使用して、AI エージェント向けの MCP(Model Context Protocol)エンドポイントを `http://:7575/mcp` で公開します。ツールはデバイスステータス、SMS スレッドおよび保護された送信、eSIM プロファイルの一覧表示と切り替え、WiFi Calling ステータス/再接続をカバーします。機密性の高い操作(生 AT、eSIM のダウンロード/削除、SIM PIN、ネットワーク選択、システム更新)は公開されておらず、ツールの出力に IMSI、SIM PIN、アクティベーションコードが含まれることはありません。読み取り専用トークンは、書き込みツールが除外されたツールリストを受け取ります。SMS 送信は Web UI と同じブロック対象宛先ポリシーとグローバルな 1 時間あたりの制限に従い、セルラー AT パス経由で送信されます(WiFi Calling 有効時の Web UI の IMS 優先ルーティングは Web 側のままであり、MCP 経由で送信されたメッセージはまだ Web の会話履歴には記録されません)。 + +クライアント設定例(Claude Code の場合): + +```json +{ + "mcpServers": { + "vocat": { + "type": "http", + "url": "http://192.168.1.10:7575/mcp", + "headers": { "Authorization": "Bearer vcat_..." } + } + } +} +``` + +設計記録については [docs/API_MCP_DEVELOPMENT_PLAN.md](API_MCP_DEVELOPMENT_PLAN.md) を参照してください。 + ## Telegram ボット Telegram 通知が有効で、Chat ID と Admin ID の両方が設定されている場合、ボットは以下をサポートします: @@ -222,10 +317,17 @@ Telegram 通知が有効で、Chat ID と Admin ID の両方が設定されて /switch /wfc /sms +/ussd +/ussd_reply +/ussd_cancel ``` プロファイルの切り替えと SMS の送信には、ワンタイム確認ボタンが使用されます。ボットは eSIM のダウンロード、削除、名前変更コマンドを公開しません。 +Web ターミナルと Telegram は USSD ルーティングを共有します。WiFi Calling が有効な場合、リクエストは IMS/USSI を使用し、セルラー AT+CUSD にフォールバックすることなく、IMS が登録されるまで準備エラーを返します。WiFi Calling が無効な場合、リクエストはセルラー USSD を使用します。メニューの返信とキャンセルは常に元のデバイスとダイアログにバインドされます。キャンセルの失敗はエラーとして報告され、再試行のためにセッションが保持されます。結果が不確実な送信が別のチャネル経由で自動的に再送信されることはありません。 + +USSI ダイアログの処理は [3GPP TS 24.390](https://www.etsi.org/deliver/etsi_ts/124300_124399/124390/18.00.00_60/ts_124390v180000p.pdf) に準拠します。BYE に失敗した場合でもキャンセル記録が保持されますが、キャンセルが開始された後は以降のメニュー入力がブロックされます([RFC 3261 第 15.1.1 項](https://www.rfc-editor.org/rfc/rfc3261.html#section-15.1.1))。 + ## 更新 より新しい GitHub Release を確認する: @@ -240,15 +342,15 @@ vocat update --check --repo MengMengCode/VoCat sudo vocat update --repo MengMengCode/VoCat ``` -アップデーターは、現在の Linux アーキテクチャに一致するバイナリをダウンロードし、公開された `SHA256SUMS` で検証し、実行ファイルをアトミックに置き換え、利用可能な場合は `vocat` systemd サービスを再起動します。 +更新機能は現在の Linux アーキテクチャに一致するバイナリをダウンロードし、公開されている `SHA256SUMS` で検証し、実行ファイルをアトミックに置き換え、利用可能な場合は `vocat` systemd サービスを再起動します。 -Docker インストールの場合: +Docker インストールの場合の更新方法: ```bash docker pull ghcr.io/mengmengcode/vocat:latest ``` -新しいイメージをプルした後、コンテナを再作成します。 +新しいイメージをプルした後にコンテナを再作成します。 ## 開発 @@ -266,7 +368,7 @@ npm install npm run dev ``` -埋め込みフロントエンドをビルドしてバックエンドを起動する: +組み込みフロントエンドをビルドしてバックエンドを起動する: ```bash cd web @@ -289,10 +391,10 @@ go build -trimpath -ldflags "-s -w" -o vocat ./cmd/vocat ## リリース自動化 -バージョンタグをプッシュすると、2 つの GitHub Actions ワークフローが開始されます: +バージョンダグをプッシュすると、2 つの GitHub Actions ワークフローが開始されます: -- `release-binaries` は `amd64`、`386`、`arm64`、`aarch64`、`armv7` バイナリと `SHA256SUMS` をビルドして公開します。 -- `docker` はマルチアーキテクチャイメージをビルドして GitHub Container Registry に公開します。 +- `release-binaries`: `amd64`、`386`、`arm64`、`aarch64`、`armv7` バイナリと `SHA256SUMS` をビルドして公開します。 +- `docker`: マルチアーキテクチャイメージをビルドし、GitHub Container Registry に公開します。 ```bash git tag v0.2.0 @@ -302,29 +404,29 @@ git push origin v0.2.0 ## プロジェクト構成 ```text -cmd/vocat/ アプリケーションのエントリポイントと CLI -internal/device/ モデム検出とデバイス制御 +cmd/vocat/ アプリケーションのエントリーポイントと CLI +internal/device/ モデムの検出とデバイス制御 internal/modem/ AT セッションと応答処理 -internal/server/ HTTP API、通知、埋め込み Web サーバー +internal/server/ HTTP API、通知、組み込み Web サーバー internal/store/ SQLite 永続化 -internal/update/ GitHub Release 自己更新機能 +internal/update/ GitHub Release による自己更新 internal/vowifi/ IKE、EAP-AKA、IMS、WiFi Calling ランタイム -scripts/install.sh Linux インストーラーとアップデーター -web/src/ React と TypeScript のフロントエンド -.github/workflows/ バイナリと Docker のリリース自動化 +scripts/install.sh Linux インストーラーおよびアップデーター +web/src/ React と TypeScript フロントエンド +.github/workflows/ バイナリおよび Docker のリリース自動化 ``` ## 責任ある使用 -セルラーモデムおよび eSIM の操作は、加入者サービス、保存されたプロファイル、ネットワーク登録、ハードウェア状態に影響を与える可能性があります。バックアップを保持し、破壊的な操作を慎重に確認し、接続されたハードウェアとネットワークリソースを操作することが許可されている合法的な環境でのみソフトウェアを使用してください。 +セルラーモデムおよび eSIM の操作は、加入者サービス、保存されているプロファイル、ネットワーク登録、ハードウェアの状態に影響を与える可能性があります。バックアップを保持し、破壊的な操作を慎重に確認し、接続されたハードウェアおよびネットワークリソースの運用が許可されている合法的な環境でのみ本ソフトウェアを使用してください。 -Vocat は、キャリア認証、ネットワークポリシー、ハードウェアセキュリティ、eSIM の信頼要件をバイパスしません。操作のサポートは、Vocat がモデムまたは eUICC にそれを要求できることを意味します。デバイス、プロファイル、ネットワーク、またはキャリアがそれを拒否する場合があります。 +Vocat は、キャリア認証、ネットワークポリシー、ハードウェアセキュリティ、または eSIM の信頼要件をバイパスしません。操作への対応は、Vocat がモデムまたは eUICC にそれを要求できることを意味します。デバイス、プロファイル、ネットワーク、またはキャリアによって拒否される場合があります。 ## コントリビューション -Issue や Pull Request を歓迎します。変更は焦点を絞り、可能な場合はテストを含め、認証情報や加入者データをコミットしないようにし、ハードウェア固有の動作を明確に文書化してください。 +Issue や Pull Request を歓迎します。変更点を絞り込み、可能な限りテストを含め、認証情報や加入者データのコミットを避け、ハードウェア固有の動作を明確に文書化してください。 -変更を提出する前に: +変更を送信する前に: ```bash go test ./... @@ -348,10 +450,12 @@ cd web && npm run build [LICENSE](../LICENSE) を参照してください。 - +## Star History + + - - - Star History Chart + + + Star History Chart - +\n \ No newline at end of file diff --git a/docs/README.ru.md b/docs/README.ru.md index 045dda19..e816c094 100644 --- a/docs/README.ru.md +++ b/docs/README.ru.md @@ -24,7 +24,7 @@ [English](../README.md) | [العربية](README.ar.md) | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [Français](README.fr.md) | **Русский** | [Español](README.es.md) | [日本語](README.ja.md) -Vocat — это веб-панель управления с открытым исходным кодом и набор инженерных инструментов для сотовых модемов Quectel класса EC20/EC25. Она объединяет в одном автономном сервисе обнаружение модемов, состояние радиосвязи в реальном времени, терминалы AT и USSD, SMS, WiFi Calling, управление eSIM, выбор сети, маршрутизацию через прокси, уведомления, журналы аудита и автоматизацию релизов. +Vocat — это веб-панель управления с открытым исходным кодом и набор инженерных инструментов для сотовых модемов Quectel класса EC20/EC25 и China Mobile серии ML307. Она объединяет в одном автономном сервисе обнаружение модемов, состояние радиосвязи в реальном времени, терминалы AT и USSD, SMS, WiFi Calling, управление eSIM, выбор сети, маршрутизацию через прокси, уведомления, журналы аудита и автоматизацию релизов. Бэкенд написан на Go, интерфейс построен на React и TypeScript, а производственный фронтенд встроен в бинарный файл Go. Один исполняемый файл содержит веб-приложение и использует SQLite для постоянного хранения состояния. @@ -38,7 +38,7 @@ Vocat — это веб-панель управления с открытым и | Область | Что предоставляет Vocat | | --- | --- | | Управление устройствами | Автоматическое обнаружение по последовательному порту/USB, поддержка нескольких модемов, понятные имена устройств, обновление обзора в реальном времени, перезапуск модуля, авиарежим и управление режимом USB-сети. | -| Радио и сеть | Статус регистрации, оператор, метрики сигнала, RSRP/RSRQ/SINR, режим сети, диапазон, канал, сканирование операторов и автоматический или ручной выбор сети. | +| Радио и сеть | Статус регистрации, оператор, метрики сигнала, RSRP/RSRQ/SINR, режим сети, диапазон, канал, сканирование операторов, автоматический или ручной выбор сети, а также опрос сот LTE и блокировка/разблокировка одной соты (EARFCN/PCI). | | AT и USSD | Интерактивный AT-терминал, история команд, необработанные ответы модема, потоки запуска/продолжения/отмены USSD и понятные сообщения об ошибках модема. | | SMS | Прямая отправка сотовых и IMS SMS, входящая синхронизация, обработка составных сообщений, отчёты о доставке, история диалогов, статус непрочитанных, метки времени и статус доставки каждого сообщения. | | WiFi Calling | Установка туннеля IKEv2/ePDG, аутентификация EAP-AKA, регистрация IMS, IMS SMS, управление переподключением, диагностика состояния и маршрутизация по устройствам. | @@ -47,17 +47,18 @@ Vocat — это веб-панель управления с открытым и | Маршрутизация через прокси | Восходящая маршрутизация SOCKS, привязки устройств, правила по странам, проверки доступности TCP и проверки UDP Associate для путей передачи данных WiFi Calling. | | Уведомления | Пересылка новых входящих SMS через Telegram, Bark, электронную почту, Pushplus и подписанные вебхуки. Каждое SMS доставляется как отдельное уведомление. | | Telegram-бот | Статус устройства, список и переключение установленных профилей, управление WiFi Calling и отправка SMS. Чувствительные действия требуют подтверждения администратора. | -| Эксплуатация | Аутентификация, защита CSRF, политики доступа, события аудита, журналы в реальном времени, хранение журналов, проверки работоспособности, адаптивная вёрстка, тёмный режим и интерфейс на английском/китайском. | +| Эксплуатация | Аутентификация по паролю и SSO (Pocket ID, GitHub, Generic OAuth2), защита CSRF, политики доступа, события аудита, журналы в реальном времени, хранение журналов, проверки работоспособности, адаптивная вёрстка, тёмный режим и интерфейс на английском/китайском. | | Дистрибуция | Статические бинарные файлы Linux, скрипт установки systemd, самообновление с проверкой SHA-256, образ Docker, публикация в GHCR и сборки релизов GitHub Actions. | ## Поддерживаемое оборудование -Vocat ориентирован на модули Quectel на базе Qualcomm, которые предоставляют совместимые интерфейсы AT, QMI, последовательный порт и USB-сеть, включая: +Vocat ориентирован на модули Quectel на базе Qualcomm и модули China Mobile серии ML307, которые предоставляют совместимые интерфейсы AT, QMI, последовательный порт и USB-сеть, включая: - Quectel EC20 - Quectel EC25 - Семейство Quectel EG25 - Совместимые модули EG600 и родственные +- Серия China Mobile ML307 (например, ML307A-E5, различные OEM-варианты) Доступные функции зависят от прошивки модуля, конфигурации USB, возможностей SIM/eSIM, драйверов хоста, радиосети и настроек оператора. @@ -71,13 +72,13 @@ Vocat ориентирован на модули Quectel на базе Qualcomm, curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh | bash ``` -От обычного пользователя в дистрибутиве с sudo: +Обычным пользователем в дистрибутиве с sudo: ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh | sudo bash ``` -Проверить предварительные требования VoWiFi/XFRM на хосте без установки VoCat: +Проверить предварительные требования к VoWiFi/XFRM на хосте без установки VoCat: ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh | bash -s -- --check-env @@ -90,13 +91,9 @@ curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/i sudo bash install.sh 0.0.2 ``` -VoWiFi IMS требует Linux XFRM/IPsec. В OpenWrt/Kwrt установщик пытается -установить соответствующие пакеты `ip-full`, `kmod-ipsec`, `kmod-ipsec4/6`, -`kmod-crypto-authenc`, AES-CBC и SHA1 из собственного репозитория прошивки. -Если соответствующие модули ядра недоступны, используйте прошивку, которая их включает; -никогда не устанавливайте принудительно kmod, собранные для другого ядра. +Для VoWiFi IMS требуется Linux XFRM/IPsec. В OpenWrt/Kwrt установщик пытается установить из собственного репозитория прошивки точно соответствующие пакеты `ip-full`, `kmod-ipsec`, `kmod-ipsec4/6`, `kmod-crypto-authenc`, AES-CBC и SHA1. Если подходящие модули ядра недоступны, используйте прошивку, которая их содержит; ни в коем случае не устанавливайте принудительно модули kmod, собранные для другого ядра. -Если ваше ядро не поддерживает XFRM/IPsec и вам нужны только функции без VoWiFi, например сотовые SMS или передача данных, выполните установку с `--skip-vowifi-check`: +Если ваше ядро не поддерживает XFRM/IPsec и вам нужны только функции без VoWiFi (например, сотовые SMS или передача данных), установите с параметром `--skip-vowifi-check`: ```bash curl -fsSL https://raw.githubusercontent.com/MengMengCode/VoCat/master/scripts/install.sh -o install.sh @@ -105,15 +102,15 @@ sudo bash install.sh --skip-vowifi-check Установщик: -- определяет `amd64`, `386`, `arm64`, `aarch64` или `armv7`; -- загружает соответствующий бинарный файл GitHub Release; -- проверяет его по `SHA256SUMS`; -- устанавливает Vocat в `/opt/vocat`; -- создаёт усиленный сервис systemd с доступом к оборудованию и сети, необходимым Vocat; -- хранит конфигурацию времени выполнения в `/etc/vocat/env`; +- определяет архитектуру `amd64`, `386`, `arm64`, `aarch64` или `armv7`; +- загружает соответствующий бинарный файл из GitHub Releases; +- проверяет его по файлу `SHA256SUMS`; +- устанавливает Vocat в каталог `/opt/vocat`; +- создаёт защищённый сервис systemd с правами доступа к оборудованию и сети, необходимыми для работы Vocat; +- сохраняет конфигурацию времени выполнения в `/etc/vocat/env`; - генерирует случайный начальный пароль администратора при первой установке. -После установки откройте: +После установки откройте в браузере: ```text http://:7575 @@ -121,17 +118,17 @@ http://:7575 ### Ручная установка бинарного файла -Загрузите соответствующий бинарный файл и `SHA256SUMS` из GitHub Releases: +Загрузите нужный бинарный файл и `SHA256SUMS` из GitHub Releases: | Платформа | Файл релиза | | --- | --- | | Linux x86-64 | `vocat-linux-amd64` | -| Linux x86 32-бит | `vocat-linux-386` | +| Linux x86 32-bit | `vocat-linux-386` | | Linux ARM64 | `vocat-linux-arm64` | | Linux AArch64 | `vocat-linux-aarch64` | | Linux ARMv7 | `vocat-linux-armv7` | -Проверьте и установите его: +Проверьте и установите: ```bash sha256sum -c SHA256SUMS --ignore-missing @@ -145,15 +142,11 @@ sudo env \ /opt/vocat/bin/vocat serve ``` -Эта ручная команда запускает Vocat в переднем плане. Используйте `vocat serve`, чтобы -процесс сразу запустил сервер; запуск `vocat` без аргументов от имени root -в TTY вместо этого открывает интерактивное меню управления. Используйте установку -одной командой, когда требуется управляемый сервис systemd и автоматический перезапуск. +Эта ручная команда запускает Vocat на переднем плане. Используйте команду `vocat serve`, чтобы процесс сразу запустил сервер; запуск `vocat` без аргументов от имени root в TTY вместо этого открывает интерактивное меню управления. Используйте установку одной командой, когда требуется управляемый сервис systemd и автоматический перезапуск. ### Docker -Для хоста Linux, который должен обнаруживать каждый подключённый поддерживаемый модем Quectel и -продолжать видеть события горячего подключения USB, запустите Vocat в режиме доступа к оборудованию: +Для хоста Linux, который должен обнаруживать каждый подключённый поддерживаемый модем Quectel и продолжать видеть события горячего подключения USB, запустите Vocat в режиме доступа к оборудованию: ```bash docker pull ghcr.io/mengmengcode/vocat:latest @@ -180,7 +173,21 @@ docker run -d \ После запуска контейнера откройте `http://:7575`. Сеть хоста необходима, чтобы сетевые интерфейсы QMI оставались видимыми для Vocat, а привилегированный доступ к устройствам — для последовательных портов, узлов управления QMI, интерфейсов TUN, настройки сети и устройств, добавленных после запуска контейнера. Привязка `/dev` делает новые узлы `ttyUSB*`, `ttyACM*`, `cdc-wdm*` и MHI `wwan*` видимыми без пересоздания контейнера. -Этот режим намеренно предоставляет Vocat широкий доступ к устройствам и сетевому стеку хоста. Используйте его только на доверенном хосте Linux. Автоматическое обнаружение распознаёт поддерживаемые USB-модемы Quectel (идентификатор производителя USB `2c7c`) и модемы PCIe/MHI, представленные через подсистему Linux WWAN; произвольные конфигурации модемов не распознаются. Проброс только отдельных узлов с помощью `--device`, например `/dev/ttyUSB2`, `/dev/cdc-wdm0` или `/dev/wwan0qmi0`, ограничивает контейнер этими фиксированными узлами и не обеспечивает полноценного обнаружения нескольких устройств или горячего подключения. +Этот режим намеренно предоставляет Vocat широкий доступ к устройствам и сетевому стеку хоста. Используйте его только на доверенном хосте Linux. Автоматическое обнаружение распознаёт поддерживаемые USB-модемы Quectel (идентификатор производителя USB `2c7c`), модули China Mobile серии ML307 по строке продукта, а также модемы PCIe/MHI, представленные через подсистему Linux WWAN; произвольные конфигурации модемов не распознаются. Проброс только отдельных узлов с помощью `--device`, например `/dev/ttyUSB2`, `/dev/cdc-wdm0` или `/dev/wwan0qmi0`, ограничивает контейнер этими фиксированными узлами и не обеспечивает полноценного обнаружения нескольких устройств или горячего подключения. + +На хостах OpenWrt цепочка input fw4 отбрасывает новые подключения от интерфейсов, которыми она не управляет, включая создаваемый Vocat интерфейс TUN для каждого сеанса VoWiFi. Поэтому Vocat вставляет узкие разрешающие правила `vocat-ims-*` (от P-CSCF к согласованному внутреннему адресу) с помощью `nft`, входящего в образ. Без них регистрация IMS и исходящие SMS работают, но входящие SMS и вызовы отклоняются. Если встроенный `nft` не может прочитать набор правил хоста, Vocat регистрирует сообщение `nft could not inspect the OpenWrt fw4 input chain`; в этом случае укажите в `VOCAT_NFT` собственный nft хоста: + +```bash +cat > /opt/vocat-host-nft <<'EOF' +#!/bin/sh +exec /host/lib/ld-musl-$(uname -m).so.1 --library-path /host/usr/lib:/host/lib /host/usr/sbin/nft "$@" +EOF +chmod 755 /opt/vocat-host-nft +# Добавьте в приведённую выше команду docker run: +# -v /usr:/host/usr:ro -v /lib:/host/lib:ro \ +# -v /opt/vocat-host-nft:/usr/local/bin/host-nft:ro \ +# -e VOCAT_NFT=/usr/local/bin/host-nft +``` Образ GHCR публикуется для `linux/amd64` и `linux/arm64`. @@ -199,6 +206,11 @@ VoCat использует `qmicli` для проверки готовности `vocat doctor --repair-dji-qmi` проверяет наличие `qmicli` перед изменением любой привязки USB-драйвера или активацией сигнала DTR. Если утилита недоступна, команда останавливается с подсказкой по установке и оставляет текущее состояние устройства без изменений. +### Хостовые сотовые данные по AT + +Передача данных ECM/RNDIS на основе AT требует Linux, iproute2 и BusyBox с `udhcpc`. +VoCat должен монопольно управлять сетевым интерфейсом модема; отключите конкурирующие клиенты DHCP или профили NetworkManager для этого интерфейса. VoCat поддерживает аренду IPv4 и маркированные маршруты политик, не изменяя системный маршрут по умолчанию и настройки DNS-распознавателя. Для этого пути в настоящее время требуется услуга с поддержкой IPv4. + ## Конфигурация Vocat читает необязательный JSON-файл конфигурации из `VOCAT_CONFIG`, затем применяет переменные окружения `VOCAT_*`. Переменные окружения имеют приоритет. @@ -211,6 +223,8 @@ Vocat читает необязательный JSON-файл конфигура | `VOCAT_SECURE_COOKIES` | `false` | Помечает cookie сессии как безопасные при использовании HTTPS. | | `VOCAT_SHUTDOWN_TIMEOUT` | `10s` | Тайм-аут корректного завершения работы. | | `VOCAT_MAX_REQUEST_BODY_BYTES` | `1048576` | Максимальный размер тела запроса API. | +| `VOCAT_MCP_ENABLED` | `true` | Предоставлять конечную точку MCP по адресу `/mcp` для ИИ-агентов. Установите `false` для отключения. | +| `VOCAT_PROBE_UNKNOWN_USB` | `false` | Включить опрос неопознанных последовательных USB-устройств AT-командами. Опрос изменяет настройки порта и очищает входной буфер; включайте только тогда, когда эти порты выделены для модемов. Проверенные идентификаторы модемов определяются автоматически. Ключ JSON: `probe_unknown_usb`. | | `VOCAT_REPO` | `MengMengCode/VoCat` | Доверенный репозиторий GitHub, используемый самообновлятором, в формате `owner/name`. | | `GITHUB_TOKEN` | пусто | Необязательный токен GitHub для приватных репозиториев или более высоких лимитов API. | @@ -220,6 +234,79 @@ Vocat читает необязательный JSON-файл конфигура Не храните токены Telegram, пароли SMTP, секреты вебхуков, учётные данные SIM или другие приватные данные в репозитории. Настраивайте их через параметры приложения или защищённые файлы окружения. +## Единый вход (SSO) + +VoCat поддерживает Pocket ID через OpenID Connect, приложения OAuth на GitHub.com и универсальные поставщики Generic OAuth2. Выберите одного поставщика в разделе **Настройки → Единый вход (SSO)**. По умолчанию SSO отключён, а вход по паролю остаётся доступным. Сначала войдите под локальным администратором, сохраните поставщика и явно привяжите внешнюю учётную запись перед включением SSO. Другие внешние учётные записи не могут регистрироваться или становиться администраторами, включая учётные записи с тем же адресом электронной почты, что и у привязанной. + +### Pocket ID + +1. Используйте для VoCat HTTPS-источник, например `https://vocat.example.com`, и установите `VOCAT_SECURE_COOKIES=true`. В настройках SSO выберите **Pocket ID**. В поле публичного источника изначально подставляется текущий адрес браузера; при необходимости отредактируйте поле **Публичный адрес VoCat**. Компактный URL-адрес обратного вызова и кнопка копирования отображаются сразу, поэтому URL можно скопировать до создания клиента OIDC или сохранения каких-либо учётных данных: `https://vocat.example.com/api/auth/sso/callback`. +2. В Pocket ID создайте отдельный конфиденциальный (**Confidential**) OIDC-клиент для VoCat. Зарегистрируйте точный URL обратного вызова и убедитесь, что целевой пользователь или группа имеют право использовать этот клиент. Для VoCat и Komari требуются отдельные клиенты и локальные сессии. +3. В VoCat введите точный публичный `APP_URL` Pocket ID в качестве Issuer, а также введите Client ID и Client Secret, выпущенные для клиента, созданного на шаге 2. Поле Client ID содержит подсказку, где найти это значение. Области видимости (Scopes) и PKCE S256 обрабатываются VoCat автоматически. Аутентификация клиента для токена выбирается из проверенных метаданных Discovery с приоритетом `client_secret_basic` перед `client_secret_post`. Сохраните настройки, пока SSO остаётся отключённым. Изменение адреса сразу обновляет отображаемый URL обратного вызова; эти изменения вступают в силу для авторизации после сохранения. +4. Проверьте сохранённую конфигурацию; при успешной проверке отображается сообщение **Проверка конфигурации пройдена**. Затем привяжите учётную запись. Выполните вход через Passkey в Pocket ID, предоставьте согласие и подтвердите возвращённую привязку в VoCat. Проверка конфигурации валидирует только метаданные и доступность сети; она не может проверить секрет клиента или авторизацию пользователя без фактического входа. +5. Включите SSO и сохраните. Используйте окно инкогнито для проверки входа через SSO, возврата на запрошенную страницу и первого запроса, изменяющего состояние. + +На странице настроек отображается привязанная учётная запись и действие отвязки. Чтобы сменить внешнюю учётную запись, сначала отвяжите её, затем привяжите нужную учётную запись и снова включите SSO. + +Issuer сопоставляется строго точно, включая путь и завершающий слэш. Вход в Pocket ID использует проверенные `issuer + sub` из ID Token; эндпоинт UserInfo или утверждение email не требуются. Эндпоинты Discovery, авторизации, Token и JWKS должны использовать тот же origin, что и настроенный Issuer. Развёртывания с использованием `INTERNAL_APP_URL` с другим внутренним origin в этом выпуске не поддерживаются. Оставляйте публичный `APP_URL` в качестве Issuer. Ранее явно настроенные методы аутентификации и scopes сохраняются при обновлении настроек. + +### GitHub и Generic OAuth2 + +Для GitHub создайте OAuth App на GitHub.com с точно таким же URL обратного вызова, выберите **GitHub**, введите его Client ID и Secret и выполните описанную выше последовательность: сохранение, проверка, привязка, включение. Конечные точки фиксированы для GitHub.com; областью действия по умолчанию является `read:user`, и требуется PKCE S256. Привязка осуществляется по стабильному числовому идентификатору учётной записи GitHub, поэтому смена логина не изменяет привязку. Поддержка GitHub Enterprise выходит за рамки этого релиза. + +Для Generic OAuth2 в дополнение к учетным данным клиента укажите URL авторизации, Token и UserInfo. При выборе Generic OAuth2 отображаются scopes, имя поля ID пользователя верхнего уровня (по умолчанию `id`), параметры аутентификации токена и совместимости с PKCE. UserInfo запрашивается с токеном доступа Bearer. Идентификатор должен быть непустой строкой или целым числом; большие целочисленные ID сохраняют свое точное значение. Аутентификация токена по умолчанию — `client_secret_post`, а PKCE — S256. Отключайте PKCE только для поставщиков, у которых отсутствует поддержка PKCE. Generic OAuth2 не выполняет проверку ID Token OIDC. + +### Сеть, учётные данные и сессии + +Существующая политика сети источника VoCat также применяется к маршрутам SSO. Настройка SSO не делает установку общедоступной в интернете. Браузер должен иметь доступ к VoCat и конечной точке авторизации; процесс VoCat должен иметь доступ к конечным точкам Discovery, Token и JWKS/UserInfo с доверенными сертификатами TLS. Сохранение точного URL-адреса HTTPS Issuer Pocket ID или Generic утверждает эти адреса для подключений HTTPS, включая частные адреса RFC1918/IPv6 ULA. Фиксированные конечные точки GitHub остаются ограниченными общедоступными адресами. + +Для внутренней службы HTTP Generic-поставщика явно укажите ее полный URL HTTP в поле Token/UserInfo. Каждый кандидат DNS и фактическое подключение для этих HTTP-назначений должны быть разрешенным частным адресом. Discovery Pocket ID не может утвердить другой origin; его конечные точки должны использовать origin настроенного Issuer. Сам Issuer, URL загрузки Discovery и конечная точка авторизации браузера всегда требуют HTTPS. Loopback, link-local, метаданные облака и другие адреса специального назначения остаются заблокированными для соединений с IdP. Установите частные сертификаты CA в доверенное хранилище хоста/контейнера; возможность обхода проверки TLS отсутствует. Использование localhost или HTTPS-источника для доступа к VoCat в браузере не зависит от этих разрешений соединений с IdP. + +Прилагаемое развёртывание Compose использует сеть хоста. Проверяйте DNS, маршрутизацию и доверие CA из самого контейнера; не полагайтесь на доступность имен служб мостовой сети. Обратные прокси должны сохранять настроенный публичный origin. VoCat не выводит URL-адреса обратного вызова из пересылаемых заголовков host. Требуется развёртывание в корневом пути. Для локальной разработки с Vite используйте один согласованный loopback-origin (например, `http://127.0.0.1:5173`) для публичного адреса и регистрации обратного вызова; исключения сетевого транспорта IdP допустимы только в тестовых фикстурах. + +Файлы cookie сессии и CSRF сохраняют `SameSite=Strict`; кратковременные файлы cookie авторизации используют `SameSite=Lax` для обработки межсайтовых GET-обратных вызовов верхнего уровня. Сохранение любой конфигурации SSO (включая отключение или ротацию секрета) аннулирует все сессии SSO. Отвязка учётной записи аннулирует сессии SSO этой привязки. Сессии по паролю и API-токены сохраняются после этих действий. Смена пароля локального администратора аннулирует все сессии и API-токены, отменяет незавершённые авторизации и сохраняет внешнюю привязку. Пользовательский интерфейс запрашивает повторный вход, когда успешная операция аннулирует текущую сессию SSO. + +Оставьте поле ввода секрета пустым, чтобы сохранить текущий секрет, или введите новый секрет для его замены. На странице настроек нет элемента для полной очистки сохранённого секрета. Изменение поставщика, Client ID, Issuer или эндпоинтов/поля ID Generic переключает пространство имён идентификации, требует ввода новых учётных данных и повторной привязки; существующие привязки требуют явного подтверждения сброса. Чтобы сменить привязанную учётную запись, сначала отвяжите её, а затем создайте новую привязку. Секреты клиентов хранятся только на сервере в защищённой базе данных SQLite; флаг конфиденциальности маскирует ответы API и не шифрует базу данных. Сторонние токены не сохраняются и не возвращаются в браузер. + +Обычный выход завершает только текущую сессию VoCat. Отключение учётной записи в IdP или выход из Pocket ID не аннулирует немедленно существующую сессию VoCat; она истекает согласно `VOCAT_SESSION_TTL` или может быть аннулирована отключением SSO либо отвязкой в VoCat. Общедоступный эндпоинт возможностей — `GET /api/auth/providers`. API настроек SSO и привязки требуют сессии браузера и CSRF для записи; токены Bearer не могут управлять ими. Конфликты ревизий конфигурации возвращают `409 sso_config_changed`; изменение источников идентификации возвращает `409 sso_rebind_required`. + +### Обновление и откат + +SSO добавляет версию схемы SQLite 29. Обновление сохраняет существующего администратора, сессии, API-токены и данные устройств; SSO остаётся отключённым до настройки. Перед обновлением запишите дайджест старого бинарного файла или образа и создайте резервную копию конфигурации, CA и полного каталога постоянных данных при остановленном VoCat либо используйте API резервного копирования SQLite / команду `.backup` для получения согласованного снимка. Простое копирование работающего файла базы данных в режиме WAL не является надёжной резервной копией. + +При возникновении проблем с поставщиком отключите SSO в новой версии, чтобы восстановить доступ по паролю и сохранить текущие рабочие данные. Для возврата к бинарному файлу со схемой 28 требуется остановить VoCat и восстановить соответствующий снимок данных и конфигурации до обновления. Простой откат образа контейнера или ручное понижение `PRAGMA user_version` не могут понизить версию базы данных. По возможности восстанавливайте данные в отдельный каталог/том и сохраняйте новую базу данных для анализа. Восстановление снимка приводит к потере сообщений и изменений задач, зафиксированных после создания резервной копии. Для Docker обновление выполняется получением нового образа и пересозданием контейнера. + +Перед релизом в рабочей среде выполните реальное тестирование Passkey/политики доступа в Pocket ID и отработайте сценарии обновления и отката. + +## API-токены и MCP + +Клиенты, отличные от браузера, проходят аутентификацию для тех же эндпоинтов `/api/...` с помощью токена Bearer вместо сессионных cookie и механизма CSRF браузера: + +```bash +vocat token create --read-only --ttl 720h ci-runner # выводит токен один раз +curl -H "Authorization: Bearer vcat_..." http://127.0.0.1:7575/api/devices +``` + +`vocat token list` и `vocat token revoke ` управляют списком токенов. Токены действуют с правами администратора; токен с флагом `--read-only` может отправлять только запросы `GET` (одно документированное исключение: чтение цепочки SMS помечает её локально как прочитанную — правило гласит: «никаких изменений оборудования или сети»). Ротация пароля администратора аннулирует все токены. Создавайте токены после первого выполнения `bootstrap-admin`. Удалённым клиентам Bearer также требуется политика сетевого доступа, разрешающая их IP-адрес; режим по умолчанию `internal` обслуживает только частные сети. + +По умолчанию (для отключения установите `VOCAT_MCP_ENABLED=false`) тот же сервис предоставляет конечную точку MCP (Model Context Protocol) для ИИ-агентов по адресу `http://:7575/mcp` с использованием транспорта Streamable HTTP и аутентификации по токену Bearer. Инструменты охватывают состояние устройств, цепочки SMS и защищённую отправку, просмотр и переключение профилей eSIM, а также состояние и переподключение WiFi Calling; конфиденциальные операции (прямые AT-команды, загрузка/удаление eSIM, SIM PIN, выбор сети, обновление системы) не предоставляются, а вывод инструментов никогда не содержит IMSI, SIM PIN или коды активации. Токен только для чтения получает список инструментов без инструментов записи. Отправка SMS подчиняется той же политике заблокированных номеров и общему часовому лимиту, что и веб-интерфейс, и выполняется через сотовый AT-канал (маршрутизация с приоритетом IMS при включённом WiFi Calling остаётся на стороне веб-интерфейса, а сообщения, отправленные через MCP, пока не записываются в историю диалогов веб-интерфейса). + +Пример конфигурации клиента (на примере Claude Code): + +```json +{ + "mcpServers": { + "vocat": { + "type": "http", + "url": "http://192.168.1.10:7575/mcp", + "headers": { "Authorization": "Bearer vcat_..." } + } + } +} +``` + +Документ по архитектуре и дизайну см. в [docs/API_MCP_DEVELOPMENT_PLAN.md](API_MCP_DEVELOPMENT_PLAN.md). + ## Telegram-бот Когда уведомления Telegram включены и настроены Chat ID и Admin ID, бот поддерживает: @@ -230,10 +317,17 @@ Vocat читает необязательный JSON-файл конфигура /switch /wfc /sms +/ussd +/ussd_reply +/ussd_cancel ``` Переключение профилей и отправка SMS используют одноразовые кнопки подтверждения. Бот не предоставляет команды загрузки, удаления или переименования eSIM. +Веб-терминал и Telegram совместно используют маршрутизацию USSD. При включённом WiFi Calling запросы используют IMS/USSI и возвращают ошибку готовности до завершения регистрации в IMS без отката на сотовую команду AT+CUSD. При отключённом WiFi Calling запросы используют сотовый USSD. Ответы на пункты меню и отмена всегда привязаны к исходному устройству и диалогу. Ошибки отмены фиксируются и сохраняют сессию для повторной попытки; запрос с неопределённым результатом отправки никогда не отправляется повторно автоматически по другому каналу. + +Обработка диалогов USSI соответствует [3GPP TS 24.390](https://www.etsi.org/deliver/etsi_ts/124300_124399/124390/18.00.00_60/ts_124390v180000p.pdf). Неудачный запрос BYE сохраняет запись об отмене, но дальнейший ввод меню блокируется сразу после начала отмены ([RFC 3261, раздел 15.1.1](https://www.rfc-editor.org/rfc/rfc3261.html#section-15.1.1)). + ## Обновление Проверить наличие более нового GitHub Release: @@ -248,7 +342,7 @@ vocat update --check --repo MengMengCode/VoCat sudo vocat update --repo MengMengCode/VoCat ``` -Обновлятор загружает бинарный файл, соответствующий текущей архитектуре Linux, проверяет его по опубликованному `SHA256SUMS`, атомарно заменяет исполняемый файл и перезапускает сервис systemd `vocat`, когда он доступен. +Обновлятор загружает бинарный файл, соответствующий текущей архитектуре Linux, проверяет его с помощью опубликованного файла `SHA256SUMS`, атомарно заменяет исполняемый файл и перезапускает службу systemd `vocat`, если она доступна. Для установок Docker: @@ -256,7 +350,7 @@ sudo vocat update --repo MengMengCode/VoCat docker pull ghcr.io/mengmengcode/vocat:latest ``` -Пересоздайте контейнер после загрузки нового образа. +Пересоздайте контейнер после получения нового образа. ## Разработка @@ -289,7 +383,7 @@ go run ./cmd/vocat go test ./... ``` -Собрать производственный бинарный файл: +Собрать рабочий бинарный файл: ```bash go build -trimpath -ldflags "-s -w" -o vocat ./cmd/vocat @@ -299,8 +393,8 @@ go build -trimpath -ldflags "-s -w" -o vocat ./cmd/vocat Отправка тега версии запускает два рабочих процесса GitHub Actions: -- `release-binaries` собирает и публикует бинарные файлы `amd64`, `386`, `arm64`, `aarch64` и `armv7` вместе с `SHA256SUMS`. -- `docker` собирает и публикует мультиархитектурный образ в GitHub Container Registry. +- `release-binaries` собирает и публикует бинарные файлы `amd64`, `386`, `arm64`, `aarch64` и `armv7`, а также `SHA256SUMS`. +- `docker` собирает и публикует многоархитектурный образ в GitHub Container Registry. ```bash git tag v0.2.0 @@ -312,27 +406,27 @@ git push origin v0.2.0 ```text cmd/vocat/ Точка входа приложения и CLI internal/device/ Обнаружение модемов и управление устройствами -internal/modem/ Сессия AT и обработка ответов +internal/modem/ Сеансы AT и обработка ответов internal/server/ HTTP API, уведомления и встроенный веб-сервер -internal/store/ Постоянное хранение SQLite -internal/update/ Самообновлятор GitHub Release +internal/store/ Хранилище SQLite +internal/update/ Самообновлятор из GitHub Release internal/vowifi/ Среда выполнения IKE, EAP-AKA, IMS и WiFi Calling -scripts/install.sh Установщик и обновлятор Linux +scripts/install.sh Скрипт установки и обновления для Linux web/src/ Фронтенд на React и TypeScript .github/workflows/ Автоматизация релизов бинарных файлов и Docker ``` ## Ответственное использование -Операции с сотовыми модемами и eSIM могут влиять на обслуживание абонента, сохранённые профили, регистрацию в сети и состояние оборудования. Делайте резервные копии, внимательно проверяйте деструктивные действия и используйте программное обеспечение только в законных средах, где вам разрешено работать с подключённым оборудованием и сетевыми ресурсами. +Операции с сотовыми модемами и eSIM могут влиять на обслуживание абонентов, сохранённые профили, сетевую регистрацию и состояние оборудования. Создавайте резервные копии, внимательно проверяйте разрушительные действия и используйте программное обеспечение только в законных средах, где вам разрешено эксплуатировать подключённое оборудование и сетевые ресурсы. Vocat не обходит аутентификацию оператора, сетевую политику, аппаратную безопасность или требования доверия eSIM. Поддержка операции означает, что Vocat может запросить её у модема или eUICC; устройство, профиль, сеть или оператор всё равно могут её отклонить. ## Участие в разработке -Мы приветствуем issues и pull request'ы. Делайте изменения сфокусированными, по возможности добавляйте тесты, избегайте коммита учётных данных или данных абонентов и чётко документируйте поведение, специфичное для оборудования. +Приветствуются issues и pull requests. Сохраняйте изменения сфокусированными, по возможности добавляйте тесты, избегайте фиксации учётных данных или абонентских данных и чётко документируйте поведение, специфичное для оборудования. -Перед отправкой изменения: +Перед отправкой изменений: ```bash go test ./... @@ -341,8 +435,8 @@ cd web && npm run build ## Благодарности - [Nodeseek.com](https://www.nodeseek.com) — Сообщество, посвящённое серверам -- [Linux.do](https://linux.do) — Вдохновляющее технологическое сообщество -- [iniwex5](https://github.com/iniwex5) — Руководства по стилю и функциональности +- [Linux.do](https://linux.do) — Вдохновляющее техническое сообщество +- [iniwex5](https://github.com/iniwex5) — Рекомендации по стилю и функциональности ## Угостите меня кофе @@ -356,10 +450,12 @@ cd web && npm run build См. [LICENSE](../LICENSE). - +## Star History + + - - - Star History Chart + + + Star History Chart - +\n \ No newline at end of file diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md index 4359323a..29aada73 100644 --- a/docs/README.zh-CN.md +++ b/docs/README.zh-CN.md @@ -24,7 +24,7 @@ [English](../README.md) | [العربية](README.ar.md) | **简体中文** | [繁體中文](README.zh-TW.md) | [Français](README.fr.md) | [Русский](README.ru.md) | [Español](README.es.md) | [日本語](README.ja.md) -Vocat 是一款面向 Quectel EC20/EC25 系列蜂窝模组的开源 Web 控制面板与工程工具套件。它在一个自包含的服务中整合了模组发现、实时射频状态、AT 与 USSD 终端、短信、WiFi Calling(WiFi 通话)、eSIM 管理、网络选择、代理路由、通知、审计日志以及发布自动化。 +Vocat 是一款面向 Quectel EC20/EC25 系列与中移物联 ML307 系列蜂窝模组的开源 Web 控制面板与工程工具套件。它在一个自包含的服务中整合了模组发现、实时射频状态、AT 与 USSD 终端、短信、WiFi Calling(WiFi 通话)、eSIM 管理、网络选择、代理路由、通知、审计日志以及发布自动化。 后端使用 Go 编写,界面采用 React 与 TypeScript 构建,生产环境前端被嵌入进 Go 二进制中。单个可执行文件即包含完整的 Web 应用,并使用 SQLite 进行持久化存储。 @@ -38,7 +38,7 @@ Vocat 是一款面向 Quectel EC20/EC25 系列蜂窝模组的开源 Web 控制 | 领域 | Vocat 提供的能力 | | --- | --- | | 设备管理 | 自动串口/USB 发现、多模组支持、设备友好名称、概览实时刷新、模组重启、飞行模式以及 USB 网卡模式控制。 | -| 射频与网络 | 注册状态、运营商、信号指标、RSRP/RSRQ/SINR、网络模式、频段、信道、运营商扫描以及自动/手动选网。 | +| 射频与网络 | 注册状态、运营商、信号指标、RSRP/RSRQ/SINR、网络模式、频段、信道、运营商扫描、自动/手动选网,以及 LTE 小区查询与单小区锁定/解锁(EARFCN/PCI)。 | | AT 与 USSD | 交互式 AT 终端、命令历史、原始模组响应、USSD 发起/继续/取消流程以及清晰的模组错误上报。 | | 短信 | 蜂窝与 IMS 短信直接发送、入站同步、分段短信处理、送达报告、会话历史、未读状态、时间戳以及逐条消息的送达状态。 | | WiFi Calling | IKEv2/ePDG 隧道建立、EAP-AKA 鉴权、IMS 注册、IMS 短信、重连控制、状态诊断以及按设备路由。 | @@ -52,12 +52,13 @@ Vocat 是一款面向 Quectel EC20/EC25 系列蜂窝模组的开源 Web 控制 ## 支持的硬件 -Vocat 面向基于高通芯片、并暴露兼容 AT、QMI、串口与 USB 网络接口的 Quectel 模组,包括: +Vocat 面向基于高通芯片的 Quectel 模组与中移物联 ML307 系列模组,这些模组需暴露兼容的 AT、QMI、串口与 USB 网络接口,包括: - Quectel EC20 - Quectel EC25 - Quectel EG25 系列 - 兼容的 EG600 及相关模组 +- 中移物联 ML307 系列(例如 ML307A-E5,及各 OEM 变体) 可用功能取决于模组固件、USB 复合设备配置、SIM/eSIM 能力、主机驱动、无线网络以及运营商配置。 @@ -172,7 +173,7 @@ docker run -d \ 容器启动后打开 `http://:7575`。必须使用主机网络,才能让 QMI 网络接口保持对 Vocat 可见;串口、QMI 控制节点、TUN 接口、网络配置以及容器启动后新增的设备则需要特权设备访问。`/dev` 绑定挂载使新的 `ttyUSB*`、`ttyACM*`、`cdc-wdm*` 和 MHI `wwan*` 节点无需重建容器即可见。 -该模式有意赋予 Vocat 对主机设备和网络栈的广泛访问权限,请仅在受信任的 Linux 主机上使用。自动发现会识别受支持的 Quectel USB 模组(USB 厂商 ID `2c7c`),以及通过 Linux WWAN 子系统暴露的 PCIe/MHI 模组;它不能识别任意模组布局。仅用 `--device` 映射单个节点,例如 `/dev/ttyUSB2`、`/dev/cdc-wdm0` 或 `/dev/wwan0qmi0`,会将容器限定在这些固定节点上,无法提供完整的多设备或热插拔发现。 +该模式有意赋予 Vocat 对主机设备和网络栈的广泛访问权限,请仅在受信任的 Linux 主机上使用。自动发现会识别受支持的 Quectel USB 模组(USB 厂商 ID `2c7c`)、中移物联 ML307 系列模组(按产品字符串识别),以及通过 Linux WWAN 子系统暴露的 PCIe/MHI 模组;它不能识别任意模组布局。仅用 `--device` 映射单个节点,例如 `/dev/ttyUSB2`、`/dev/cdc-wdm0` 或 `/dev/wwan0qmi0`,会将容器限定在这些固定节点上,无法提供完整的多设备或热插拔发现。 在 OpenWrt 主机上,fw4 的 input 链会丢弃来自其未管理接口的新连接,其中包括 Vocat 为每个 VoWiFi 会话创建的 TUN 接口。因此 Vocat 会用镜像自带的 `nft` 插入范围很窄的 `vocat-ims-*` 放行规则(仅 P-CSCF 到协商得到的内层地址)。缺少这些规则时 IMS 注册和发短信仍然正常,但收短信和来电会被拒绝。如果镜像自带的 `nft` 无法读取主机规则集,Vocat 会记录 `nft could not inspect the OpenWrt fw4 input chain`,此时可让 `VOCAT_NFT` 指向主机自己的 nft: @@ -205,6 +206,11 @@ VoCat 使用 `qmicli` 验证 QMI 控制通道是否就绪,并使用 `qmi-proxy `vocat doctor --repair-dji-qmi` 会在更改任何 USB 驱动绑定或置位 DTR 之前检查 `qmicli`。如果该工具不可用,命令会停止并给出安装提示,保持设备当前状态不变。 +### AT 蜂窝主机数据 + +基于 AT 的 ECM/RNDIS 数据需要 Linux、iproute2 以及带有 `udhcpc` 的 BusyBox。 +VoCat 必须独占管理模组网络接口;请禁用该接口上竞争的 DHCP 客户端或网络管理配置。VoCat 会维护 IPv4 租约和打标策略路由,而不会更改系统默认路由或解析器配置。该路径目前需要支持 IPv4 的蜂窝服务。 + ## 配置 Vocat 先从 `VOCAT_CONFIG` 读取可选的 JSON 配置文件,再应用 `VOCAT_*` 环境变量。环境变量优先级更高。 @@ -217,6 +223,8 @@ Vocat 先从 `VOCAT_CONFIG` 读取可选的 JSON 配置文件,再应用 `VOCAT | `VOCAT_SECURE_COOKIES` | `false` | 可选:强制所有入口使用安全 Cookie;默认按 HTTP/HTTPS 入口自动适配。 | | `VOCAT_SHUTDOWN_TIMEOUT` | `10s` | 优雅关闭超时时间。 | | `VOCAT_MAX_REQUEST_BODY_BYTES` | `1048576` | API 请求体最大字节数。 | +| `VOCAT_MCP_ENABLED` | `true` | 在 `/mcp` 提供面向 AI Agent 的 MCP 端点。设为 `false` 可禁用。 | +| `VOCAT_PROBE_UNKNOWN_USB` | `false` | 允许使用 AT 命令探测未识别的 USB 串口设备。探测会更改串口设置并清空输入缓存;仅当这些端口专用于模组时再启用。已验证的模组身份会自动被发现。JSON 键名:`probe_unknown_usb`。 | | `VOCAT_REPO` | `MengMengCode/VoCat` | 自更新器使用的受信任 GitHub 仓库,格式为 `owner/name`。 | | `GITHUB_TOKEN` | 空 | 可选的 GitHub token,用于私有仓库或更高的 API 限额。 | @@ -340,6 +348,35 @@ Docker 通过拉取镜像并重建容器升级。 生产发布前,应在实际部署完成 Pocket ID Passkey/访问策略联调和升级、回滚演练。 +## API Token 与 MCP + +非浏览器客户端可以使用 Bearer Token 向相同的 `/api/...` 端点进行身份验证,无需使用浏览器的会话 Cookie 和 CSRF 流程: + +```bash +vocat token create --read-only --ttl 720h ci-runner # 仅打印一次 Token +curl -H "Authorization: Bearer vcat_..." http://127.0.0.1:7575/api/devices +``` + +`vocat token list` 和 `vocat token revoke ` 用于管理 Token 清单。Token 拥有管理员权限;`--read-only` Token 只能发起 `GET` 请求(一个经文档记录的特例:读取短信会话会将其在本地标记为已读——规则是“不产生硬件或网络变更”)。轮换管理员密码会撤销所有 Token。请在首次执行 `bootstrap-admin` 后创建 Token。远程 Bearer 客户端还需要其来源地址在网络访问策略许可范围内;默认的 `internal` 模式仅允许私有网络。 + +默认情况下(设置 `VOCAT_MCP_ENABLED=false` 可禁用),同一服务会在 `http://:7575/mcp` 为 AI Agent 暴露一个基于 Streamable HTTP 传输和 Bearer Token 认证的 MCP(Model Context Protocol)端点。提供的工具涵盖设备状态、短信会话及受保护的发送、eSIM 配置文件列表与切换,以及 WiFi Calling 状态/重连;敏感操作(原始 AT、eSIM 下载/删除、SIM PIN、网络选择、系统更新)未暴露,且工具输出中绝不包含 IMSI、SIM PIN 或激活码。只读 Token 获取的工具列表中不包含写操作工具。短信发送遵循与 Web 界面相同的受阻目的地策略和全局每小时限额,并走蜂窝 AT 路径发送(Web 界面在启用 WiFi Calling 时的 IMS 优先路由仍保留在 Web 端,且通过 MCP 发送的短信暂未写入 Web 会话历史)。 + +客户端配置示例(以 Claude Code 为例): + +```json +{ + "mcpServers": { + "vocat": { + "type": "http", + "url": "http://192.168.1.10:7575/mcp", + "headers": { "Authorization": "Bearer vcat_..." } + } + } +} +``` + +设计记录请参阅 [docs/API_MCP_DEVELOPMENT_PLAN.md](API_MCP_DEVELOPMENT_PLAN.md)。 + ## Telegram 机器人 启用 Telegram 通知并配置好 Chat ID 与 Admin ID 后,机器人支持: @@ -483,10 +520,12 @@ cd web && npm run build 参见 [LICENSE](../LICENSE)。 - +## Star History + + - - - Star History Chart + + + Star History Chart diff --git a/docs/README.zh-TW.md b/docs/README.zh-TW.md index e02c6386..65f0e42b 100644 --- a/docs/README.zh-TW.md +++ b/docs/README.zh-TW.md @@ -24,7 +24,7 @@ [English](../README.md) | [العربية](README.ar.md) | [简体中文](README.zh-CN.md) | **繁體中文** | [Français](README.fr.md) | [Русский](README.ru.md) | [Español](README.es.md) | [日本語](README.ja.md) -Vocat 是一款面向 Quectel EC20/EC25 系列行動通訊模組的開源 Web 控制面板與工程工具套件。它在單一自包含的服務中整合了模組探索、即時射頻狀態、AT 與 USSD 終端、簡訊、WiFi Calling(WiFi 通話)、eSIM 管理、網路選擇、代理路由、通知、稽核日誌以及發佈自動化。 +Vocat 是一款面向 Quectel EC20/EC25 系列與中移物聯 ML307 系列行動通訊模組的開源 Web 控制面板與工程工具套件。它在單一自包含的服務中整合了模組探索、即時射頻狀態、AT 與 USSD 終端、簡訊、WiFi Calling(WiFi 通話)、eSIM 管理、網路選擇、代理路由、通知、稽核日誌以及發佈自動化。 後端使用 Go 撰寫,介面採用 React 與 TypeScript 建構,生產環境前端被嵌入進 Go 二進位檔中。單一可執行檔即包含完整的 Web 應用,並使用 SQLite 進行持久化儲存。 @@ -38,7 +38,7 @@ Vocat 是一款面向 Quectel EC20/EC25 系列行動通訊模組的開源 Web | 領域 | Vocat 提供的能力 | | --- | --- | | 裝置管理 | 自動序列埠/USB 探索、多模組支援、裝置友善名稱、概覽即時更新、模組重新啟動、飛航模式以及 USB 網路卡模式控制。 | -| 射頻與網路 | 註冊狀態、電信業者、訊號指標、RSRP/RSRQ/SINR、網路模式、頻段、通道、電信業者掃描以及自動/手動選網。 | +| 射頻與網路 | 註冊狀態、電信業者、訊號指標、RSRP/RSRQ/SINR、網路模式、頻段、通道、電信業者掃描、自動/手動選網,以及 LTE 小區查詢與單一小區鎖定/解除鎖定(EARFCN/PCI)。 | | AT 與 USSD | 互動式 AT 終端、指令歷史、原始模組回應、USSD 發起/繼續/取消流程以及清晰的模組錯誤回報。 | | 簡訊 | 行動通訊與 IMS 簡訊直接傳送、接收同步、分段簡訊處理、送達報告、對話歷史、未讀狀態、時間戳以及逐則訊息的送達狀態。 | | WiFi Calling | IKEv2/ePDG 隧道建立、EAP-AKA 驗證、IMS 註冊、IMS 簡訊、重新連線控制、狀態診斷以及依裝置路由。 | @@ -47,19 +47,20 @@ Vocat 是一款面向 Quectel EC20/EC25 系列行動通訊模組的開源 Web | 代理路由 | 上游 SOCKS 路由、裝置綁定、國家規則、TCP 可達性檢查以及面向 WiFi Calling 資料路徑的 UDP Associate 檢查。 | | 通知 | 透過 Telegram、Bark、電子郵件、Pushplus 以及簽章 Webhook 轉發新接收簡訊,每則簡訊個別推送。 | | Telegram 機器人 | 裝置狀態、已安裝設定檔列表與切換、WiFi Calling 控制以及簡訊傳送。敏感操作需要管理員確認。 | -| 維運 | 驗證、CSRF 防護、存取策略、稽核事件、即時日誌、日誌保留、健康檢查、響應式版面、深色模式以及中英文應用介面。 | +| 維運 | 密碼與 SSO 驗證(Pocket ID、GitHub、Generic OAuth2)、CSRF 防護、存取策略、稽核事件、即時日誌、日誌保留、健康檢查、響應式版面、深色模式以及中英文應用介面。 | | 發佈 | 靜態 Linux 二進位檔、systemd 安裝腳本、具 SHA-256 校驗的自我更新、Docker 映像、GHCR 發佈以及 GitHub Actions 發佈建置。 | ## 支援的硬體 -Vocat 面向基於高通晶片、並暴露相容 AT、QMI、序列埠與 USB 網路介面的 Quectel 模組,包括: +Vocat 面向基於高通晶片的 Quectel 模組與中移物聯 ML307 系列模組,這些模組需暴露相容的 AT、QMI、序列埠與 USB 網路介面,包括: - Quectel EC20 - Quectel EC25 - Quectel EG25 系列 - 相容的 EG600 及相關模組 +- 中移物聯 ML307 系列(例如 ML307A-E5,及各 OEM 變體) -可用功能取決於模組韌體、USB 複合裝置配置、SIM/eSIM 能力、主機驅動、無線網路以及電信業者配置。 +可用功能取決於模組韌體、USB 複合裝置配置、SIM/eSIM 能力、主機驅動程式、無線網路以及電信業者配置。 ## 安裝 @@ -172,7 +173,21 @@ docker run -d \ 容器啟動後開啟 `http://:7575`。必須使用主機網路,才能讓 QMI 網路介面持續對 Vocat 可見;序列埠、QMI 控制節點、TUN 介面、網路設定以及容器啟動後新增的裝置則需要特權裝置存取。`/dev` 繫結掛載使新的 `ttyUSB*`、`ttyACM*`、`cdc-wdm*` 和 MHI `wwan*` 節點無需重建容器即可見。 -此模式刻意賦予 Vocat 對主機裝置和網路堆疊的廣泛存取權限,請僅在受信任的 Linux 主機上使用。自動探索會辨識受支援的 Quectel USB 模組(USB 廠商 ID `2c7c`),以及透過 Linux WWAN 子系統提供的 PCIe/MHI 模組;它無法辨識任意模組配置。僅以 `--device` 對應個別節點,例如 `/dev/ttyUSB2`、`/dev/cdc-wdm0` 或 `/dev/wwan0qmi0`,會將容器限制在這些固定節點上,無法提供完整的多裝置或熱插拔探索。 +此模式刻意賦予 Vocat 對主機裝置和網路堆疊的廣泛存取權限,請僅在受信任的 Linux 主機上使用。自動探索會辨識受支援的 Quectel USB 模組(USB 廠商 ID `2c7c`)、中移物聯 ML307 系列模組(依產品字串辨識),以及透過 Linux WWAN 子系統提供的 PCIe/MHI 模組;它無法辨識任意模組配置。僅以 `--device` 對應個別節點,例如 `/dev/ttyUSB2`、`/dev/cdc-wdm0` 或 `/dev/wwan0qmi0`,會將容器限制在這些固定節點上,無法提供完整的多裝置或熱插拔探索。 + +在 OpenWrt 主機上,fw4 的 input 鏈會丟棄來自其未管理介面的新連線,其中包括 Vocat 為每個 VoWiFi 工作階段建立的 TUN 介面。因此 Vocat 會使用映像內建的 `nft` 插入範圍很窄的 `vocat-ims-*` 放行規則(僅 P-CSCF 到協商得到的內部 IP 位址)。缺少這些規則時 IMS 註冊與傳送簡訊仍然正常,但接收簡訊與來電會被拒絕。若映像內建的 `nft` 無法讀取主機規則集,Vocat 會記錄 `nft could not inspect the OpenWrt fw4 input chain`;此時可讓 `VOCAT_NFT` 指向主機自帶的 nft: + +```bash +cat > /opt/vocat-host-nft <<'EOF' +#!/bin/sh +exec /host/lib/ld-musl-$(uname -m).so.1 --library-path /host/usr/lib:/host/lib /host/usr/sbin/nft "$@" +EOF +chmod 755 /opt/vocat-host-nft +# 在上面的 docker run 指令中加入: +# -v /usr:/host/usr:ro -v /lib:/host/lib:ro \ +# -v /opt/vocat-host-nft:/usr/local/bin/host-nft:ro \ +# -e VOCAT_NFT=/usr/local/bin/host-nft +``` GHCR 映像發佈為 `linux/amd64` 與 `linux/arm64`。 @@ -191,6 +206,11 @@ VoCat 使用 `qmicli` 驗證 QMI 控制通道是否就緒,並使用 `qmi-proxy `vocat doctor --repair-dji-qmi` 會在變更任何 USB 驅動程式繫結或將 DTR 設為有效狀態之前檢查 `qmicli`。如果該工具無法使用,指令會停止並提供安裝提示,保持裝置目前狀態不變。 +### AT 行動通訊主機資料 + +基於 AT 的 ECM/RNDIS 資料需要 Linux、iproute2 以及帶有 `udhcpc` 的 BusyBox。 +VoCat 必須獨佔管理模組網路介面;請停用該介面上競爭的 DHCP 用戶端或網路管理設定檔。VoCat 會維護 IPv4 租約與標記策略路由,而不會變更系統預設路由或 DNS 解析設定。該路徑目前需要支援 IPv4 的行動通訊服務。 + ## 配置 Vocat 先從 `VOCAT_CONFIG` 讀取可選的 JSON 配置檔,再套用 `VOCAT_*` 環境變數。環境變數優先級更高。 @@ -203,6 +223,8 @@ Vocat 先從 `VOCAT_CONFIG` 讀取可選的 JSON 配置檔,再套用 `VOCAT_*` | `VOCAT_SECURE_COOKIES` | `false` | 在使用 HTTPS 時將工作階段 Cookie 標記為安全。 | | `VOCAT_SHUTDOWN_TIMEOUT` | `10s` | 優雅關閉逾時時間。 | | `VOCAT_MAX_REQUEST_BODY_BYTES` | `1048576` | API 請求主體最大位元組數。 | +| `VOCAT_MCP_ENABLED` | `true` | 在 `/mcp` 提供面向 AI Agent 的 MCP 端點。設為 `false` 可停用。 | +| `VOCAT_PROBE_UNKNOWN_USB` | `false` | 允許使用 AT 指令探測未識別的 USB 序列埠裝置。探測會變更序列埠設定並清空輸入快取;僅當這些連接埠專用於模組時才啟用。已驗證的模組身分會自動被探索。JSON 鍵名:`probe_unknown_usb`。 | | `VOCAT_REPO` | `MengMengCode/VoCat` | 自我更新器使用的受信任 GitHub 倉庫,格式為 `owner/name`。 | | `GITHUB_TOKEN` | 空 | 可選的 GitHub token,用於私有倉庫或更高的 API 限額。 | @@ -212,6 +234,79 @@ Vocat 先從 `VOCAT_CONFIG` 讀取可選的 JSON 配置檔,再套用 `VOCAT_*` 請勿將 Telegram token、SMTP 密碼、Webhook 金鑰、SIM 認證資訊或其他私密資料存放在倉庫中。請透過應用設定或受保護的環境檔來配置它們。 +## 單一登入(SSO) + +VoCat 支援 Pocket ID(OpenID Connect)、GitHub.com OAuth App 和 Generic OAuth2,可在**設定 → 單一登入(SSO)**中選擇其中一個識別提供者。SSO 預設停用,密碼登入始終可用。請先使用本地管理員登入、儲存設定並明確綁定外部帳號,再啟用 SSO。未綁定的外部帳號無法註冊或成為管理員;即使共用已綁定帳號的電子郵件地址也不會自動合併。 + +### Pocket ID + +1. 為 VoCat 設定 HTTPS 根路徑公開來源,例如 `https://vocat.example.com`,並設定 `VOCAT_SECURE_COOKIES=true`。在 SSO 設定中選擇 **Pocket ID**。公開來源初始使用目前瀏覽器存取位址,需要調整時直接編輯**VoCat 公開來源**。頁面會立即顯示簡潔的回呼 URL 與複製按鈕,無需先建立 OIDC 用戶端或儲存憑據即可複製:`https://vocat.example.com/api/auth/sso/callback`。 +2. 在 Pocket ID 為 VoCat 個別建立 **Confidential** OIDC 用戶端。登記精確的回呼 URL,並確認目標使用者或群組獲准使用該用戶端。VoCat 與 Komari 應分別建立用戶端並簽發本地工作階段。 +3. 在 VoCat 輸入 Pocket ID 精確的公開 `APP_URL` 作為簽發者(Issuer),並填入步驟 2 建立的用戶端之 Client ID 與 Client Secret。Client ID 欄位包含何處尋找該數值的提示。Scopes 與 PKCE S256 由 VoCat 自動處理。權杖客戶端驗證根據已驗證的 Discovery 中繼資料自動選擇,優先使用 `client_secret_basic`,其次為 `client_secret_post`。在 SSO 保持停用的狀態下儲存。修改公開來源會立即更新顯示的回呼位址,儲存後方用於實際授權。 +4. 檢查已儲存的設定;成功時顯示**設定檢查通過**。接著發起帳號綁定,完成 Pocket ID 的 Passkey 登入與授權同意,並在 VoCat 中確認傳回的綁定。設定檢查僅驗證中繼資料與連線能力;在實際登入前無法確認 Client Secret 或使用者授權。 +5. 啟用 SSO 並儲存。使用無痕視窗驗證 SSO 登入、返回請求頁面以及登入後的第一次狀態變更操作。 + +設定頁面顯示已綁定帳號與解除綁定操作。若要更換外部帳號,請先解除綁定,再綁定目標帳號並重新啟用 SSO。 + +Issuer 會精確比對,包含其路徑與結尾斜線。Pocket ID 登入使用經過驗證的 ID Token 之 `issuer + sub` 關聯帳號,不依賴 UserInfo 端點或電子郵件聲明。Discovery 及其授權、Token 與 JWKS 端點必須與設定的 Issuer 同來源(Origin)。首期不支援使用 `INTERNAL_APP_URL` 搭配不同後端來源的部署方式。Issuer 請維持填寫公開 `APP_URL`。儲存時會保留現有已明確設定的驗證方法與 Scopes。 + +### GitHub 與 Generic OAuth2 + +GitHub:在 GitHub.com 建立 OAuth App 並登記完全相同的回呼 URL,在 VoCat 選擇 **GitHub**,填入 Client ID 與 Secret,並依序執行上述的儲存、檢查、綁定、啟用步驟。端點固定為 GitHub.com,預設 scope 為 `read:user`,且強制要求 PKCE S256。綁定採用穩定的 GitHub 數值帳號 ID,因此修改使用者名稱不會變更綁定身份。GitHub Enterprise 不在此版本支援範圍內。 + +Generic OAuth2:除了用戶端憑據外,還需提供授權、Token 與 UserInfo URL。選取 Generic OAuth2 時會直接顯示 Scopes、頂層使用者 ID 欄位名稱(預設 `id`)、Token 驗證與 PKCE 相容性選項。UserInfo 透過 Bearer 存取權杖請求;使用者 ID 必須為非空字串或整數,大整數會保留精確數值。預設 Token 驗證為 `client_secret_post`,預設 PKCE 為 S256。僅針對不支援 PKCE 的提供者才關閉 PKCE。Generic OAuth2 不執行 OIDC ID Token 驗證。 + +### 網路、憑證與工作階段 + +VoCat 現有的來源網路策略同樣適用於 SSO 路由。設定 SSO 並不會使安裝實例公開對外開放。瀏覽器必須能連線至 VoCat 與授權端點;VoCat 處理程序必須能連線至 Discovery、Token 與 JWKS/UserInfo 端點並信任其 TLS 憑證。儲存精確的 Pocket ID HTTPS Issuer 或 Generic 各端點 URL 即代表核准這些位址進行 HTTPS 連線,包括 RFC1918/IPv6 ULA 私有網路位址。GitHub 的固定端點仍僅限於公開位址。 + +針對 Generic 提供者的內部 HTTP 服務,請在 Token/UserInfo 欄位明確填寫完整的 HTTP URL。這些 HTTP 目的地的每次 DNS 解析候選與實際連線都必須為獲准的私有位址。Pocket ID 的 Discovery 無法自動核准其他來源;其端點必須使用所設定的 Issuer 來源。Issuer、Discovery 擷取 URL 與瀏覽器授權端點始終需要 HTTPS。IdP 連線仍拒絕 loopback、link-local、雲端中繼資料與其他特殊用途位址。私有 CA 憑證應安裝至主機/容器的信任庫中;本系統不提供略過 TLS 驗證的選項。瀏覽器存取 VoCat 使用的 localhost 或 HTTPS 來源與這些 IdP 連線權限相互獨立。 + +提供的 Compose 部署使用 host 網路。請在實際容器內驗證 DNS、路由與 CA 信任;請勿預設橋接網路的服務名稱可用。反向代理必須保留所設定的公開來源。VoCat 不會從轉發的 Host 標頭推導回呼 URL。必須採用根路徑部署。本機 Vite 開發時請統一使用一致的 loopback 來源(例如 `http://127.0.0.1:5173`)作為公開來源與回呼登記位址;IdP 傳輸例外僅限於測試 fixture 中使用。 + +工作階段與 CSRF Cookie 維持 `SameSite=Strict`;短期授權 Cookie 使用 `SameSite=Lax` 以支援最頂層 GET 跨站回呼。儲存任何 SSO 設定(包括停用 SSO 或輪換 Secret)均會撤銷所有現有 SSO 工作階段。解除綁定帳號會撤銷該綁定的 SSO 工作階段。密碼工作階段與 API 權杖在上述操作後仍會保留。變更本機管理員密碼會撤銷所有工作階段與 API 權杖、取消進行中的授權,並保留外部綁定。當操作成功並撤銷目前 SSO 工作階段時,介面會提示重新登入。 + +Secret 輸入欄位留空代表保留原值,輸入新 Secret 則會替換原值。設定頁面不提供清除已儲存 Secret 的控制項。變更提供者、Client ID、Issuer 或 Generic 身分端點/ID 欄位時,會切換身分識別命名空間,需要填寫新憑據並重新綁定;既有綁定需要明確確認重設。若要更換綁定帳號,請先解除綁定再建立新綁定。Client Secret 僅儲存在伺服器受保護的 SQLite 資料庫中;敏感設定標誌僅用於介面回應脫敏,不代表資料庫加密。第三方權杖不會被持久化儲存,也不會傳回瀏覽器。 + +一般登出僅結束目前的 VoCat 工作階段。在 IdP 停用帳號或登出 Pocket ID 不會立即撤銷既有的 VoCat 工作階段;它會依據 `VOCAT_SESSION_TTL` 到期,亦可在 VoCat 中停用 SSO 或解除綁定以立即撤銷。公開功能端點為 `GET /api/auth/providers`。SSO 設定與綁定 API 寫入操作需要瀏覽器工作階段與 CSRF;Bearer 權杖無法管理這些憑據。設定版本衝突會傳回 `409 sso_config_changed`;身分來源變更需要重新綁定時傳回 `409 sso_rebind_required`。 + +### 升級與復原 + +SSO 新增 SQLite schema 版本 29。升級會保留原管理員、工作階段、API 權杖與裝置資料;SSO 在設定前仍保持停用。升級前請記錄舊二進位檔或映像摘要,並在 VoCat 停止時備份設定、CA 資料與完整的持久化資料目錄;線上備份應使用 SQLite 的 backup API 或 `.backup`。在 WAL 模式下,僅複製執行中的 `.db` 檔案並非可靠的備份方式。 + +若新版本設定發生問題,可先停用 SSO,以繼續使用密碼登入並保留目前業務資料。若要退回僅支援 schema 28 的舊二進位檔,必須停止 VoCat 並還原對應的升級前資料與設定快照。僅復原容器映像或手動降低 `PRAGMA user_version` 無法降級資料庫。建議在可行情況下還原至獨立目錄/磁碟區,並保留新資料庫以供調查。還原快照會遺失備份後收到的簡訊與任務變更。若使用 Docker,請透過拉取映像並重建容器來更新。 + +在生產發佈前,應在實際部署環境中完成 Pocket ID Passkey/存取策略整合測試,以及升級與復原演練。 + +## API 權杖與 MCP + +非瀏覽器用戶端可以使用 Bearer 權杖向相同的 `/api/...` 端點進行驗證,無需使用瀏覽器的工作階段 Cookie 與 CSRF 流程: + +```bash +vocat token create --read-only --ttl 720h ci-runner # 僅列印一次權杖 +curl -H "Authorization: Bearer vcat_..." http://127.0.0.1:7575/api/devices +``` + +`vocat token list` 與 `vocat token revoke ` 用於管理權杖清單。權杖具有管理員權限;`--read-only` 權杖僅能發起 `GET` 請求(一個經文件記錄的例外:讀取簡訊對話會在本地將其標記為已讀——規則是「不產生硬體或網路變更」)。輪換管理員密碼會撤銷所有權杖。請在首次執行 `bootstrap-admin` 後建立權杖。遠端 Bearer 用戶端還需要其來源位址在網路存取策略許可範圍內;預設的 `internal` 模式僅允許私有網路。 + +預設情況下(設定 `VOCAT_MCP_ENABLED=false` 可停用),同一服務會在 `http://:7575/mcp` 為 AI Agent 提供一個基於 Streamable HTTP 傳輸與 Bearer 權杖驗證的 MCP(Model Context Protocol)端點。提供的工具涵蓋裝置狀態、簡訊對話及受保護的傳送、eSIM 設定檔列表與切換,以及 WiFi Calling 狀態/重新連線;敏感操作(原始 AT、eSIM 下載/刪除、SIM PIN、網路選擇、系統更新)未暴露,且工具輸出中絕不包含 IMSI、SIM PIN 或啟用碼。唯讀權杖取得的工具列表中不包含寫入操作工具。簡訊傳送遵循與 Web 介面相同的受阻目的地策略與全域每小時上限,並經由行動通訊 AT 路徑傳送(Web 介面在啟用 WiFi Calling 時的 IMS 優先路由仍保留在 Web 端,且透過 MCP 傳送的簡訊暫未寫入 Web 對話歷史)。 + +用戶端設定範例(以 Claude Code 為例): + +```json +{ + "mcpServers": { + "vocat": { + "type": "http", + "url": "http://192.168.1.10:7575/mcp", + "headers": { "Authorization": "Bearer vcat_..." } + } + } +} +``` + +設計記錄請參閱 [docs/API_MCP_DEVELOPMENT_PLAN.md](API_MCP_DEVELOPMENT_PLAN.md)。 + ## Telegram 機器人 啟用 Telegram 通知並配置好 Chat ID 與 Admin ID 後,機器人支援: @@ -222,10 +317,17 @@ Vocat 先從 `VOCAT_CONFIG` 讀取可選的 JSON 配置檔,再套用 `VOCAT_*` /switch /wfc /sms +/ussd +/ussd_reply +/ussd_cancel ``` 設定檔切換與簡訊提交使用一次性確認按鈕。機器人不暴露 eSIM 下載、刪除或重新命名命令。 +網頁終端與 Telegram 共用 USSD 路由。啟用 WiFi Calling 時,請求使用 IMS/USSI;IMS 未就緒時會明確回報錯誤並提示等待註冊,不會回退到行動通訊 AT+CUSD。關閉 WiFi Calling 時使用行動通訊 USSD。選單回覆與取消始終綁定原裝置與對話。取消失敗會回報錯誤並保留工作階段以供重試;傳送結果不確定時不會自動換通道重新提交。 + +USSI 對話處理遵循 [3GPP TS 24.390](https://www.etsi.org/deliver/etsi_ts/124300_124399/124390/18.00.00_60/ts_124390v180000p.pdf)。BYE 失敗時保留取消記錄,但開始取消後禁止繼續提交選單輸入([RFC 3261 第 15.1.1 節](https://www.rfc-editor.org/rfc/rfc3261.html#section-15.1.1))。 + ## 更新 檢查是否有更新的 GitHub Release: @@ -348,10 +450,12 @@ cd web && npm run build 參見 [LICENSE](../LICENSE)。 - +## Star History + + - - - Star History Chart + + + Star History Chart From 9aedc312943a88907e931023a6c0837a8b9caf78 Mon Sep 17 00:00:00 2001 From: sunny Date: Wed, 4 Nov 2026 13:28:27 +0800 Subject: [PATCH 2/2] docs: fix light-theme Star History chart URLs across translations --- docs/README.ar.md | 2 +- docs/README.es.md | 2 +- docs/README.fr.md | 4 ++-- docs/README.ja.md | 4 ++-- docs/README.ru.md | 4 ++-- docs/README.zh-TW.md | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/README.ar.md b/docs/README.ar.md index 0c4811d6..c9cd918f 100644 --- a/docs/README.ar.md +++ b/docs/README.ar.md @@ -463,7 +463,7 @@ cd web && npm run build - + Star History Chart diff --git a/docs/README.es.md b/docs/README.es.md index b64a9609..d71d6760 100644 --- a/docs/README.es.md +++ b/docs/README.es.md @@ -463,7 +463,7 @@ Consulte [LICENSE](../LICENSE). - + Star History Chart diff --git a/docs/README.fr.md b/docs/README.fr.md index ca2346b1..7604c6dd 100644 --- a/docs/README.fr.md +++ b/docs/README.fr.md @@ -455,7 +455,7 @@ Voir [LICENSE](../LICENSE). - + Star History Chart -\n \ No newline at end of file +\n diff --git a/docs/README.ja.md b/docs/README.ja.md index 93c586df..b4be5719 100644 --- a/docs/README.ja.md +++ b/docs/README.ja.md @@ -455,7 +455,7 @@ cd web && npm run build - + Star History Chart -\n \ No newline at end of file +\n diff --git a/docs/README.ru.md b/docs/README.ru.md index e816c094..bb3c5dca 100644 --- a/docs/README.ru.md +++ b/docs/README.ru.md @@ -455,7 +455,7 @@ cd web && npm run build - + Star History Chart -\n \ No newline at end of file +\n diff --git a/docs/README.zh-TW.md b/docs/README.zh-TW.md index 65f0e42b..08f4bfe3 100644 --- a/docs/README.zh-TW.md +++ b/docs/README.zh-TW.md @@ -455,7 +455,7 @@ cd web && npm run build - + Star History Chart