
vpod v0.8.1
صناديق رمل Linux خفيفة الوزن وآمنة للعمليات غير الموثوقة. تعمل في المتصفح وعلى الخادم.
Vpod
ما هو vpod؟
vpod هو صندوق رمل (sandbox) خفيف الوزن وقابل للنقل يمنح عملية غير موثوقة بيئة Linux فورية. يستخدم بنية RISC‑V ويعمل بالكامل داخل WebAssembly.
- إقلاع سريع: يقلع في أقل من ثانية.
- قابل للنقل: يعمل في أي مكان دون الحاجة إلى أي إعداد.
- معزول: تبقى جميع حالات التنفيذ داخل صناديق رمل WASM.
كيف يعمل
يشغّل vpod نظام RISC‑V كاملاً (RV64GC، vCPU واحد) مُجمّعاً إلى WebAssembly. بداخله يقلع نواة Linux حقيقية مع مساحة مستخدم حقيقية، لذا تتصرف الصدفات والأدوات والخدمات تماماً كما لو كانت على عتاد فعلي.
اللقطات (Snapshots). بدلاً من إقلاع Linux من الصفر، يستعيد vpod لقطة: حالة آلة محفوظة (سجلات CPU، ذاكرة RAM، نظام ملفات) مأخوذة بعد الإقلاع مباشرة. يستغرق استعادتها أقل من ثانية. يعمل التعليق (suspend) بنفس الطريقة بالعكس، حيث تُكتب صفحات الذاكرة المتسخة فقط إلى القرص، لذا يمكنك إيقاف صندوق الرمل مؤقتاً واستئنافه لاحقاً، حتى من عملية أخرى.
الترجمة المسبقة (Ahead-of-time translation). المحاكاة النقية تعليمة-بتعليمة بطيئة، وقواعد WebAssembly تستبعد JIT في وقت التشغيل. لذا في وقت بناء اللقطة، تُترجم مسارات كود الضيف الأكثر استخداماً من RISC‑V إلى كود أصلي يُجمَّع داخل وحدة WASM نفسها. في وقت التشغيل، يوجّه المحاكي التنفيذ إلى هذه الكتل المترجمة عندما يطابق كود الضيف، ويعود إلى المفسّر عندما لا يطابق. هذا يعطي تسريعاً بنحو 5x في الأعمال المثقلة بوحدة المعالجة المركزية، دون أي تأثير على العزل: الكود المترجم يمر عبر نفس فحوصات MMU والذاكرة مثل الكود المفسَّر.
حدود WASI. يتواصل مكوّن WASM مع المضيف حصرياً عبر WASI 0.2. لا يرى الضيف أبداً واصفات ملفات المضيف أو مآخذ التوصيل أو الذاكرة: الوصول إلى نظام الملفات يتم عبر أدلة مُثبّتة صراحةً، والشبكات تمر عبر حزمة شبكات في وضع المستخدم داخل المكوّن تطلب من المضيف مآخذ توصيل صادرة عادية فقط. كل شيء آخر (نواة الضيف، العمليات، الذاكرة) يعيش داخل ذاكرة WASM الخطية ويموت معها.
مواصفات RV64GC
G (امتدادات الأغراض العامة)
- I: مجموعة تعليمات الأعداد الصحيحة الأساسية 64-بت.
- M: الضرب والقسمة عبر العتاد، مفيدة للتجزئة والتشفير.
- A: عمليات ذرية للبرامج الآمنة للخيوط.
- F/D: الفاصلة العائمة بدقة مفردة ومزدوجة، مناسبة للحوسبة العلمية واستدلال التعلم الآلي.
C (التعليمات المضغوطة) يقلل حجم الكود بنسبة 30%، محسّناً سرعة جلب التعليمات وكفاءة الذاكرة. هذا مهم عند تشغيل مساحة مستخدم Linux كاملة داخل بيئة WASM المقيّدة بالذاكرة.
[!NOTE] امتداد V (المتجهات) غير مُنفَّذ. تعليمات RVV ستُنفَّذ كـ RISC-V مُحاكى؛ لا يوجد تمرير SIMD إلى CPU المضيف. إضافة V ستزيد عبء المحاكاة دون أي فائدة أداء لأعباء العمل المتجهية.
البدء
TypeScript SDK
npm install @capsule-run/vpod
import { Sandbox } from "@capsule-run/vpod";
const sandbox = await Sandbox.create();
// الحالة محفوظة عبر الاستدعاءات
await sandbox.commands.run("export API_KEY=secret");
const key = await sandbox.commands.run("echo $API_KEY");
console.log(key.stdout); // secret
// Python REPL — المتغيرات تستمر
await sandbox.code.run("data = [1, 2, 3]");
const total = await sandbox.code.run("print(sum(data))");
console.log(total.text); // 6
await sandbox.close();
نفس الحزمة تعمل في تبويب المتصفح، حيث تُخزَّن اللقطة في تخزين خاص بالأصل (origin-private storage) بدلاً من القرص.
[!IMPORTANT] الاستدعاء الأول لـ
Sandbox.create()ينزّل اللقطة الافتراضية (alpine) ويخزّنها محلياً إذا لم تكن موجودة بالفعل.
Python SDK
pip install vpod
from vpod import Sandbox
# تشغيل أمر
sandbox = Sandbox.create()
result = sandbox.commands.run("whoami")
print(result.stdout) # root
sandbox.close()
# جلسة مستمرة — الحالة محفوظة عبر الاستدعاءات
with Sandbox.create() as sandbox:
sandbox.commands.run("export API_KEY=secret")
result = sandbox.commands.run("echo $API_KEY")
print(result.stdout) # secret
# Python REPL — المتغيرات تستمر
with Sandbox.create() as sandbox:
sandbox.code.run("import requests")
sandbox.code.run("data = [1, 2, 3]")
result = sandbox.code.run("print(sum(data))")
print(result.text) # 6
CLI
curl -fsSL https://install.vpod.sh | sh
أو التثبيت عبر PowerShell (ويندوز)
irm https://install.vpod.sh | iex
# سحب لقطة
vpod pull alpine:latest
# بدء صدفة تفاعلية
vpod
التوثيق
قم بزيارة توثيق Vpod.
القيود
- عبء المحاكاة: لا توجد افتراضية عتاد داخل WebAssembly، لذا كل كود الضيف مُحاكى. يعتمد العبء كلياً على عبء العمل: الأعمال المقيّدة بالإدخال/الإخراج والشبكات تعمل بسرعة قريبة من الأصلية، بينما الأعمال المثقلة بوحدة المعالجة المركزية تعمل أبطأ بشكل ملحوظ حتى مع الترجمة المسبقة (AOT). إذا كان عبء عملك في الغالب "تشغيل أداة، قراءة ملف، استدعاء API"، فلن تلاحظ الفرق.
- لا وصول إلى GPU: CUDA وMetal ومسرعات التعلم الآلي العتادية غير متاحة. قد يُضاف الدعم مستقبلاً عبر wasi-nn.
المساهمة
المساهمات مرحّب بها، من تقارير الأخطاء إلى دعم أجهزة جديدة. افتح مشكلة لمناقشة أي شيء جوهري قبل بنائه.
المتطلبات الأساسية
- Rust (أحدث إصدار مستقر) مع هدف
wasm32-wasip2:rustup target add wasm32-wasip2 - Python 3.10+ لـ Python SDK
- Node 20+ لـ TypeScript SDK
- Zig (0.16) وbsdtar، مطلوبان فقط إذا كنت تبني اللقطات بنفسك
إعداد التطوير
# لمرة واحدة: توليد كعب AOT (الاستنساخ الجديد لا يحتوي كتل مترجمة)
./scripts/aot-stub.sh
# بناء مكوّن WASM (المكتبة + CLI). ينسخ المستويين إلى sdks/python/vpod/
./scripts/build-wasm.sh
# تثبيت CLI المضيف
cargo install --path crates/vpod
# تثبيت Python SDK في وضع التطوير
pip install -e "sdks/python[dev]"
# بناء TypeScript SDK. يلتقط المكوّن من دليل Python SDK
cd sdks/typescript && npm install && npm run build
npm run build يضع افتراضياً --tier aot؛ CI يثبّت --tier base. البناء يرفض مكوّناً أقدم من أحدث ملف تحت crates/، لذا أعد تشغيل ./scripts/build-wasm.sh بعد تعديل المحاكي. تغيير المحاكي يظهر فقط عبر الضيف، لذا مكوّن قديم يُجمَّع وينجح في كل شيء تقريباً.
تشغيل الاختبارات
CI يشغّل هذه على كل PR، لذا شغّلها قبل الدفع:
cargo fmt --all -- --check # التنسيق
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all # اختبارات Rust
# اختبارات تكامل Python SDK (يتطلب مكتبة WASM في مكانها)
cp target/wasm32-wasip2/release/vpod_wasi_lib.wasm sdks/python/vpod/
pytest sdks/python/tests/ -v -m integration
# TypeScript SDK (من sdks/typescript)
npm run typecheck
npm test # وحدة
npm run test:all # وحدة + تكامل، يتطلب لقطة محلية
npm run test:perf # انحدارات وقت الضيف، ثوابت دقيقة
اختبارات TypeScript تستورد dist/ المبني، وليس src/، لذا ابنِ قبل تشغيلها. تبحث عن لقطة في دليل التخزين المؤقت المشترك؛ VPOD_TEST_SNAPSHOT=/path/to/x.snap يوجّهها إلى مكان آخر.
لتجربة المتصفح من البداية إلى النهاية، npm run dev يخدم الصفحة مع تفعيل COOP/COEP وnode dev/run-network.mjs --browser chrome يقودها بدون واجهة.
استخدام لقطة مبنية محلياً
تسحب SDKs من registry.vpod.sh افتراضياً. لتشغيل لقطة بنيتها بنفسك، سلّمها مباشرة بدلاً من اسم السجل:
// TypeScript: ملف على القرص (Node)، أو بايتات (في أي مكان)
await Sandbox.create({ snapshot: { path: "./dist/alpine-3.23.0-256mb.snap" } });
await Sandbox.create({ snapshot: { bytes, name: "alpine-3.23.0-256mb.snap" } });
# Python: VPOD_SNAPSHOT=/path/to/x.snap
أبقِ حجم RAM في اسم الملف في كلتا الحالتين، لأن المحاكي يقرؤه من هناك.
بناء اللقطات
يستخدم المشروع لقطات Alpine مبنية مسبقاً من registry.vpod.sh، لذا لا تحتاج هذا عادةً. لبناء واحدة محلياً:
./scripts/build-default-snapshot.sh # dist/alpine-3.23.0-256mb.snap
./scripts/build-data-snapshot.sh # نسخة 512 MB مع numpy/pandas/scipy
[!TIP] لاستخدام لقطة مبنية محلياً في CLI، أزل التعليق عن الأسطر في
resolve_snapshot()فيcrates/vpod/src/main.rs.
يمكن لبناء اللقطات أيضاً تشغيل تمرير AOT (scripts/aot-snapshot.sh <snapshot>)، الذي يتتبع عبء عمل تمثيلياً، ويترجم الكتل الساخنة، ويعيد بناء المحاكي معها مدمجة. يستغرق وقتاً؛ الكعب من aot-stub.sh كافٍ للتطوير اليومي، كل شيء يعمل بنفس الطريقة، فقط أبطأ.
من Dockerfile (macOS وLinux)
يمكن أيضاً بناء لقطات مخصصة من Dockerfile. يستخدم البنّاء CLI container من Apple على macOS وDocker Buildx على Linux. تثبيت macOS جديد يحتاج تكوين وقت التشغيل مرة واحدة، وإلا ينتظر البناء بنّاءً لا يبدأ أبداً:
container system kernel set --recommended
container builder start
على Linux، ثبّت Docker مع إضافة Buildx وسجّل محاكاة riscv64 مرة واحدة إذا كان المضيف غير مهيأ بالفعل للبناءات عبر المنصات:
docker run --privileged --rm tonistiigi/binfmt --install riscv64
./scripts/build-custom-snapshot.sh -f Dockerfile -n my-image # dist/my-image-256mb.snap
# اختيارياً: --aot --trace-cmd '<الأمر الساخن للصورة>' لدمج كتل AOT
يُبنى Dockerfile لـ linux/riscv64 (BuildKit ينفّذ خطوات RUN تحت المحاكاة)، ويستبدل نظام ملفات الجذر المسطّح minirootfs الخاص بـ Alpine، وبقية خط الأنابيب متطابقة: vpod overlay، الإقلاع، --snapshot-save.
فقط نظام الملفات ينجو من التصدير. ENV وCMD وENTRYPOINT من إعداد الصورة تُتجاهل، لذا ثبّت البيئة عبر /etc/profile.d/ في خطوة RUN. يُطبَّق بدء Python الدافئ تلقائياً للصور المبنية على musl التي توفر python3 في /usr/bin أو /bin.
طلبات السحب
- أبقِ طلبات السحب مركزة: تغيير واحد لكل PR.
- يجب أن تنجح
fmtوclippyومجموعة الاختبارات (CI يفرض الثلاثة). - إذا لمست مسارات التنفيذ أو الذاكرة في المحاكي، اذكر كيف تحققت من الصحة (مجموعة الاختبارات على الأقل؛ للتغييرات الدقيقة، إقلاع مع عبء عمل حقيقي داخل الضيف فحص سلامة جيد).
الترخيص
هذا المشروع مرخّص بموجب رخصة Apache 2.0. انظر ملف LICENSE للتفاصيل.