تصور کنید تمام برنامهتان — از کدهای اجرایی گرفته تا دیتابیس و تنظیمات — تنها در یک فایل باشد که با یک دستور ساده جابهجا میشود. این ایده، هسته مرکزی پروژه SELF است که در ۲۵ اوت ۲۰۲۶ توسط fzakaria معرفی شد تا فایلهای اجرایی استاندارد را به پایگاهدادههای قابل پرسوجوی SQLite تبدیل کند. این رویکرد در واقع تکامل همان ایدهای است که در بررسیهای اولیه ما درباره تبدیل فایلهای اجرایی لینوکس به دیتابیس SQLite به آن پرداختیم.
برای دههها، توسعهدهندگان کد (Binary)، وضعیت (State) و تنظیمات (Configuration) را از هم جدا کردهاند. این جداسازی معمولاً نیازمند ساختارهای پیچیده در سیستم فایل مانند پوشههای /var، /tmp یا /home است تا برنامه بتواند ردی از فعالیتهایش داشته باشد. تصور کنید اگر کل اپلیکیشن شما، شامل دیتابیسش، فقط یک فایل بود که میتوانستید با یک دستور واحد آن را منتقل کنید.
همانطور که در تحلیلهای پیشین ما دربارهی بهینهسازی زیرساختهای نرمافزاری اشاره کردیم، حذف لایههای اضافی در مسیر دسترسی به داده، همواره اولویت مهندسان بوده است. SELF با استفاده از یک مفسر اختصاصی و سامانه binfmt_misc در لینوکس، به هسته سیستمعامل میگوید که ردیفهای یک جدول در دیتابیس را به بخشهای حافظه نگاشت کند و سپس به نقطه شروع برنامه بپرد. به این ترتیب، فایل اجرایی به محفظهای تبدیل میشود که میتوان با دستورات استاندارد SQL از آن استخراج داده کرد.
سازوکار یک باینری قابل پرسوجو
به نقل از گزارش fzakaria.com، این فرآیند با تبدیل بخشها (Segments)، نمادها (Symbols) و جابهجاییها (Relocations) به جداول دیتابیس عمل میکند. وقتی هسته لینوکس فایل را اجرا میکند، مستقیماً باینری را اجرا نمیکند، بلکه مفسری به نام self-exec را فرا میخواند.
این مفسر اتصال به SQLite را مدیریت کرده و سپس به نقطه ورود برنامه میپرد. نکته کلیدی این است که مفسر، مسیر فایل اجرایی را به عنوان argv[0] پاس میدهد. این قابلیت به برنامه اجازه میدهد تا فایل خودش را به عنوان یک دیتابیس باز کند؛ یعنی از دستور sqlite3_open(argv[0], db) استفاده کند.
در حال حاضر، مسیر /proc/self/exe برای این کار قابل استفاده نیست. با این حال، توسعهدهنده VFS لینوکس اخیراً پشتیبانی از binfmt_misc شفاف را به هسته اضافه کرده است که در آینده اجازه میدهد /proc/self/exe مستقیماً به فایل اصلی اشاره کند. در پیادهسازی فعلی SELF، مفسر پیش از پرش به نقطه ورود، اتصال دیتابیس را میبندد تا برنامه بدون مشکل قفل شدن (Locking)، بتواند فایل خودش را باز کند.

اثبات مفهوم: self-httpd
برای اثبات این ایده، نویسنده self-httpd را ساخت؛ یک وبسرور که کاملاً درون یک فایل SQLite جای گرفته است. این سرور یک برنامه تکفایلی است که از دل یک دیتابیس اجرا میشود. اگر دستور file را روی آن اجرا کنید، خروجی نشان میدهد که این فایل یک «پایگاهداده SQLite 3.x، شناسه اپلیکیشن ۱۳۹۷۰۵۰۴۳۸» است.
این سرور با دستور ./server --journal wal 8080 اجرا میشود و در بدو شروع، اعلام میکند که ۳ مسیر (Route) را از /srv/self/server سرو میکند و روی پورت http://0.0.0.0:8080 با ۴ ورکر (Worker) در حال گوش دادن است.
این سرور برای عملکرد خود از سه جدول اصلی استفاده میکند:
- routes: محتوای وبسایت (مسیر، نوع MIME و بدنه) را بهصورت BLOB ذخیره میکند. این دادهها پس از کامپایل و لینک شدن به فایل اجرایی اضافه میشوند.
- visits: زمان بازدید، User Agent و مسیر هر بازدیدکننده را ثبت میکند. این اطلاعات در حین اجرای برنامه، مستقیماً در دل فایل اجرایی نوشته میشوند.
- presses: هر بار که کاربر روی دکمهای کلیک میکند، شناسه، زمان و نام دکمه را ثبت میکند.
وقتی کاربر صفحهای را درخواست میکند، سرور یک پرسوجوی SELECT روی جدول routes خودش میزند تا محتوا را پیدا کند. هنگام تعامل کاربر، سرور یک دستور INSERT در جداول visits یا presses اجرا میکند. برای مثال، یک درخواست POST به /api/press منجر به ایجاد یک ردیف جدید در جدول presses میشود که میتوان آن را از بیرون با دستور sqlite3 server 'SELECT id, at, button FROM presses' تایید کرد. یک خروجی نمونه برای چنین پرسوجویی به صورت 1|2026-08-25 03:11:28|press خواهد بود.

فرآیند ساخت اپلیکیشن
ساخت یک برنامه SELF با روشهای آشنای توسعه شروع میشود. ابتدا یک باینری ELF معمولی با کامپایلر استاندارد ساخته میشود:cc -O2 server.c -o server.elf $(pkg-config --libs sqlite3)
سپس ابزار elf2self این فایل را به فرمت SELF تبدیل میکند: elf2self server.elf server. در نهایت، طرح (Schema) اپلیکیشن و محتوای سایت از طریق SQL وارد میشوند: sqlite3 server < site/schema.sql و سپس برای افزودن محتوا دستور sqlite3 server "INSERT INTO routes VALUES ('/index.html', 'text/html', readfile('site/index.html'))" اجرا میشود.
استقرار تراکنشی و ویرایش زنده
از آنجا که فایل اجرایی یک دیتابیس است، از ویژگیهای ACID (تراکنشهای ایمن) بهره میبرد. این یعنی توسعهدهنده میتواند وبسایت فعال را با اجرای یک دستور UPDATE روی همان باینری در حال اجرا، بهروزرسانی کند. این تغییر فوراً اعمال میشود و نیازی به ریاستارت سرور یا خط لوله استقرار (Deployment Pipeline) جدید نیست. برای مثال، دستور UPDATE routes SET body = readfile('new.html') WHERE path = '/index.html' محتوای سایت را در همان لحظه تغییر میدهد.
استقرار برنامه به سادگی انتقال یک فایل با scp است. برای انتقال دادهها بین نسخهها، نویسنده پیشنهاد میکند از دستور ATTACH برای متصل کردن سرور قدیمی به جدید استفاده شود و با دستور INSERT INTO ... SELECT لاگهای بازدید و وضعیتها منتقل گردند. برای مثال:
ATTACH '/srv/self/server' AS old;INSERT INTO visits (at, ua, path) SELECT at, ua, path FROM old.visits;INSERT INTO presses (at, button) SELECT at, button FROM old.presses;
این روش اجازه میدهد لاگهای بازدید حتی با تغییر کد و ساخت مجدد برنامه، در همان فایل زنده بمانند. نویسنده اشاره میکند که جدول segments نیز مانند هر جدول دیگری است، به این معنی که خودِ برنامه را میتوان به صورت معکوس مهاجرت داد.
ابزارها و جستوجو
با پذیرش فرمت SQLite، پروژه SELF به اکوسیستم عظیمی از ابزارها دسترسی پیدا میکند. برای مثال، ابزار sqldiff میتواند دقیقاً بررسی کند که بین دو نسخه از یک برنامه چه تغییراتی رخ داده است. یک خروجی نمونه از sqldiff --summary ممکن است نشان دهد که در جدول routes یک تغییر رخ داده، ۰ مورد درج و ۰ مورد حذف شده است، در حالی که جداول symbols و relocations بدون تغییر ماندهاند.
جستوجوی تماممتن (Full-text search) نیز بهصورت بومی ادغام شده است. با ایجاد یک جدول مجازی با استفاده از FTS5، وبسرور میتواند صفحات خودش را ایندکس کند و بدون نیاز به موتور جستوجوی خارجی، قابلیت جستوجو را فراهم کند. توسعهدهنده میتواند به سادگی دستور CREATE VIRTUAL TABLE search USING fts5(path, body) را اجرا کرده و آن را از جدول routes پر کند. پرسوجویی مانند SELECT path, snippet(search, 1, '[', ']', '...', 6) FROM search WHERE search MATCH 'transaction' به سرور اجازه میدهد متون خاصی را در دل باینری خودش پیدا کند.
این رویکرد از پروژه redbean اثر جاستین تانی الهام گرفته است که از آرشیوهای ZIP خوداستخراجشونده استفاده میکرد. اما تفاوت در این است که redbean یک «فایل اجرایی واقعاً قابل حمل» است، در حالی که SELF یک «فایل اجرایی واقعاً قابل پرسوجو» است. در حالی که redbean از هوکهای Lua برای مدیریت پاسخها استفاده میکند، SELF از یک جدول به نام handlers استفاده میکند که هر ردیف آن میتواند یک مسیر و پرسوجوی SQL متناظر با آن را تعریف کند؛ مثلاً: INSERT INTO handlers VALUES ('/api/busiest', 'SELECT path, count(*) FROM visits GROUP BY path ORDER BY 2 DESC LIMIT 5');.
این تغییر، فرض بنیادین درباره ایستا بودن کد و خارجی بودن داده را به چالش میکشد. این مسیر به سمتی میرود که کل بسته اپلیکیشن — شامل کتابخانههای libc و تمام وابستگیها — همراه با وضعیت خود در یک شیء واحد و قابل پرسوجو بستهبندی شود.
برای یک توسعهدهنده، این به معنای پایان مدیریت متغیرهای محیطی پیچیده و مجوزهای دایرکتوری برای برنامههای ساده است. شما دیگر یک «استک» (Stack) را مستقر نمیکنید؛ بلکه فایلی را مستقر میکنید که میداند چگونه خودش را مدیریت کند. برنامه همان دیتابیس است و دیتابیس همان برنامه.
اگر میخواهید این سیستم را در عمل ببینید، دموی زنده در selfdb.exe.xyz میزبانی شده است، جایی که هر کلیک روی دکمهها مستقیماً یک درج SQL در باینریِ سروکننده صفحه است. این سایت همچنین بخشهای segments، symbols و relocations را نمایش میدهد که در حین اجرا از خود باینری پرسوجو میشوند. کد این پروژه در fzakaria/selfdb در دسترس است.
گام بعدی شما
- اگر برنامههای کوچک لینوکسی مینویسید، ابزار
elf2selfرا برای حذف وابستگی به فایلهای Config خارجی امتحان کنید. - دمو زنده این پروژه را در
selfdb.exe.xyzبررسی کنید تا ببینید هر کلیک چگونه مستقیماً یک دستور SQL در دل باینری اجرا میکند. - برای مدیریت نسخههای برنامه، ابزار
sqldiffرا جایگزین مقایسههای متنی ساده کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو