# الوصول إلى قاعدة البيانات

تستطيع كل بيئة أن تعطيك سلسلة اتصال PostgreSQL للترحيلات والأدوات: `psql` و`pg_dump` وPrisma وDrizzle وأمثالها. تكون مطفأة حتى يشغّلها مالك أو مدير.

## التشغيل

1. افتح صفحة البيئة في لوحة الإدارة.
2. تحت **Database** اضغط **Turn on database access**.
3. انسخ سلسلة الاتصال التي تظهر. لا تظهر كلمة المرور إلا مرة واحدة؛ وزر **New password** يستبدلها، فتتوقف القديمة عن العمل.

تبدو السلسلة هكذا:

```text
postgresql://e_…_developer:<password>@127.0.0.1:6543/e_…
```

## الاتصال من حاسوبك

العنوان على الخادم نفسه. افتح نفق SSH، ثم استخدم السلسلة كما هي:

```bash
ssh -N -L 6543:127.0.0.1:6543 you@your-server
psql "postgresql://e_…_developer:<password>@127.0.0.1:6543/e_…?sslmode=disable"
```

أمثلة:

```bash
psql "postgresql://…@127.0.0.1:6543/e_…?sslmode=disable" -v ON_ERROR_STOP=1 -1 -f supabase/migrations/20261017000000_init.sql   # one migration file
pg_dump "postgresql://…@127.0.0.1:6543/e_…?sslmode=disable" --schema=public > public.sql
DATABASE_URL="postgresql://…@127.0.0.1:6543/e_…?sslmode=disable" npx prisma migrate deploy
supabase db push --db-url "postgresql://…@127.0.0.1:6543/e_…?sslmode=disable"   # every file in supabase/migrations
supabase gen types typescript --db-url "postgresql://…@127.0.0.1:6543/e_…?sslmode=disable" > database.types.ts
```

## ما يستطيعه حساب المطوّر

يعمل مثل المستخدم `postgres` في مشروع Supabase، داخل قاعدة بيانات هذه البيئة وحدها:

- إنشاء الجداول والعروض والدوال والسياسات في `public` وتعديلها. تُمنح الجداول الجديدة لـ `anon` و`authenticated` و`service_role`، فتصل إليها الواجهة، ويحدد أمان الصفوف ما يراه كل مستدعٍ. تُضبط هذه الصلاحيات الافتراضية مرة واحدة، عند أول تشغيل للوصول، والترحيل الذي يضيّقها يبقى اختياره قائمًا.
- إضافة مشغّلات (triggers) على `auth.users` (مثلًا لإنشاء ملف شخصي عند التسجيل) وسياسات على `storage.objects`.
- قراءة كل الصفوف وكتابتها؛ فهو يتجاوز أمان الصفوف، كما يفعل المستخدم `postgres`.
- حفظ سجل الترحيلات في `supabase_migrations`، وهو مخطط (schema) أُنشئ له، يسجّل فيه [وكلاء الذكاء الاصطناعي](https://base.sbarah.com/docs/guides/agents/) ما طبّقوه.
- إنشاء مخططات خاصة به، مثل `app` أو `private`، وتغيير أسمائها. لكنه لا يستطيع استخدام اسم تنشئه المنصة نفسها: `auth` و`storage` و`extensions` و`public` و`realtime` و`_realtime` و`vault` و`pgsodium` و`cron` و`net` و`graphql` و`graphql_public` وأسماء أخرى قليلة (`RESERVED_SCHEMAS` في `lab/durable_runtime.py`)، وأي اسم يبدأ بـ `supabase` أو `sbarbase`. فالمخطط الذي يسبق إليه بأحد هذه الأسماء سيشغّل شيفرته بصلاحيات المنصة، لذلك يرفض PostgreSQL الأمر، ويرفض كذلك تغيير اسم مخطط إلى أحدها.
- حفظ الأسرار في Vault (`vault.create_secret` و`vault.update_secret` و`vault.decrypted_secrets`) كما في Supabase، ويقرؤها `service_role` أيضًا. ولا يستطيع الحساب أن يعطي هذا الوصول لأدوار أخرى، فلا يفتح منحُه لـ `anon` الأسرارَ للواجهة.
- إنشاء الإضافات (extensions) التي يستخدمها التطبيق، مثل `pg_trgm` و`btree_gist` و`vector` و`pgtap` و`pg_graphql`. أما الإضافات التي تصل إلى شبكة الخادم، أو ترى نشاط كل البيئات، أو تحمّل شيفرة، أو تكتب ملفات على الخادم، أو تمسك مفتاح الخادم، فمرفوضة على الخادم المشترك: `dblink` و`postgres_fdw` و`http` و`pg_net` و`wrappers` و`pg_cron` و`pg_tle` و`pgsodium` و`pg_stat_statements` و`pg_stat_monitor` و`pg_walinspect` و`pg_buffercache` و`pg_prewarm` و`pg_repack` و`pgaudit` و`pgroonga` وغيرها قليل (`RESERVED_EXTENSIONS` في `lab/durable_runtime.py`).

- إضافة جداول إلى منشور `supabase_realtime`، وهو موجود دائمًا ويملكه الحساب، كما يملكه المستخدم `postgres` في مشروع Supabase. يشمل المنشور مخطط `public` كله، فجداوله منشورة أصلًا، وإضافة أحدها مقبولة ولا تغيّر شيئًا. ويمكن إضافة جداول المخططات الأخرى واحدًا واحدًا. وإن حذفه الحساب، تعيد المنصة إنشاءه في المرة التالية التي يُشغَّل فيها الوصول.

تُمنح هذه الصلاحيات عند تشغيل الوصول؛ وللبيئة التي كان الوصول فيها مفعّلًا من قبل، أوقفه ثم شغّله مرة واحدة.

تُشفَّر أسرار Vault بمفتاح جذري واحد يخص الخادم لا البيئة: كل البيئات على الخادم تستخدم المفتاح نفسه. وتبقى منفصلة لأن كل بيئة لا تصل إلا إلى قاعدة بياناتها. انتبه لذلك في النسخ الاحتياطية: نسخة من `vault.secrets` لبيئة ما يمكن قراءتها في أي بيئة على الخادم نفسه، وعلى خادم آخر فقط بمفتاح هذا الخادم.

ليس مستخدمًا خارقًا، ولا يستطيع إنشاء أدوار أو قواعد بيانات، ولا الوصول إلى قاعدة بيانات بيئة أخرى. وحده الأقصى 10 اتصالات.

## كيف يبقى آمنًا

- يستمع المنفذ على عنوان loopback في الخادم، على المنفذ 6543 افتراضيًا. يقرأ اسم الدخول في كل اتصال قبل أن يصل أي شيء إلى PostgreSQL، ولا يمرّر إلا حساب مطوّر لبيئة يطلب قاعدة بيانات تلك البيئة نفسها. أي حساب آخر، ومنه المستخدم الخارق، يُرفض هناك.
- ثم يتحقق PostgreSQL من كلمة المرور (SCRAM). وإطفاء الوصول يغلق الحساب وكل الجلسات المفتوحة به.
- لا يشفّر المنفذ الاتصال؛ نفق SSH هو الذي يشفّره. يمكن لـ `SBARBASE_DATABASE_BIND` أن يعرض المنفذ على عنوان آخر، لكن افعل ذلك على شبكة تثق بها فقط ([الإعدادات](https://base.sbarah.com/docs/reference/configuration/)).

يشغّل CI الوصول لبيئة على جهاز نظيف مع كل تغيير، وينفّذ ترحيلًا بأسلوب Supabase عبر المنفذ، ويتحقق من حالات الرفض وتجديد كلمة المرور والإطفاء ([الدليل](https://github.com/M7MMAD-OMAR/sbarbase/blob/main/docs/evidence/docker-database-checks.json)). وفحص آخر يدخل بحساب بيئة واحدة ويجرّب كل اسم مخطط محجوز، وتغيير الأسماء، والإضافات المرفوضة، ومنشور Realtime، والحماية نفسها، وVault، وقاعدة بيانات البيئة الأخرى ([الدليل](https://github.com/M7MMAD-OMAR/sbarbase/blob/main/docs/evidence/developer-schema-checks-2026-10-19.json)).

## الحدود

- حساب واحد لكل بيئة، حتى 10 اتصالات؛ ولا يوجد مجمّع اتصالات بعد.
- يرفض المنفذ TLS؛ استخدم `sslmode=disable` أو `prefer` عبر النفق.
- يُرفض الترحيل الذي يذكر الدور `postgres`، مثل `alter default privileges for role postgres`: فحساب المطوّر في كل بيئة يقوم بهذا الدور، أما `postgres` نفسه فمشترك بين كل الخادم. اكتبه دون `for role postgres`، فينطبق على الحساب الذي يشغّله.
