Hos Sendiri Open Connector untuk Ejen AI
Jalankan get laluan pengesahan Open Connector pada VPS sendiri tanpa mendedahkan token SaaS kepada ejen. Panduan ini merangkumi imej dipinkan, TLS, OAuth dan sandaran.
Open Connector untuk ejen AI
Pengehosan sendiri Open Connector meletakkan satu get laluan pengesahan antara ejen AI anda dengan setiap API perisian sebagai perkhidmatan (SaaS) yang dipanggilnya. Oleh itu, ejen tidak pernah menyimpan token penyedia. Ia ialah get laluan sumber terbuka daripada OOMOL Lab dan dilesenkan di bawah Apache 2.0. Ia berjalan sebagai satu kontena, menyimpan keadaannya dalam satu fail SQLite, serta mendedahkan tindakan penyedia melalui HTTP dan MCP (protokol konteks model).
Masalah bermula pada integrasi kedua. Setiap penyedia mempunyai aliran OAuth (kebenaran terbuka), tempoh sah token penyegaran dan nama skop tersendiri. Menyambungkan lima penyedia kepada ejen secara manual bermakna anda memerlukan lima pengendali ubah hala, lima stor kelayakan dan lima gelung penyegaran yang mesti berjalan sebelum token tamat tempoh. Hampir tiada pihak menulis kod itu. Mereka menjana satu token akses peribadi yang sah untuk tempoh panjang bagi setiap perkhidmatan, kemudian menampalnya ke dalam konfigurasi ejen, fail persekitaran atau gesaan itu sendiri. Token tersebut kemudiannya boleh dibaca oleh setiap alat yang dijalankan oleh ejen dan turut masuk ke dalam transkrip. Inilah kegagalan yang diterangkan dalam menghalang rahsia daripada ejen AI.
Get laluan pengesahan membahagikan kelayakan kepada dua bahagian. Get laluan menyimpan kelayakan penyedia dan menjalankan aliran OAuth. Ejen menerima token masa jalan yang hanya sah terhadap get laluan. Apabila ejen memanggil sesuatu tindakan, get laluan memuatkan kelayakan yang disimpan, menyuntikkannya ke dalam permintaan keluar pada bahagian pelayan, kemudian hanya mengembalikan isi respons. Ejen tidak pernah menerima token akses penyedia. Oleh itu, transkrip ejen yang bocor hanya menyebabkan anda kehilangan satu token masa jalan yang boleh dibatalkan, bukan akaun GitHub anda.
Katalog tersebut mengiklankan lebih daripada 1,000 penyedia dan 10,000 tindakan prabina. Angka ini ialah angka yang dinyatakan oleh projek itu sendiri dan tidak boleh anda sahkan dari luar. Perkara yang boleh anda sahkan ialah strukturnya: satu titik akhir HTTP bagi setiap tindakan, satu sambungan tersimpan bagi setiap penyedia dan satu token bagi setiap ejen.
Mengapa mengehos sendiri Open Connector dan bukannya menggunakan perkhidmatan penyambung terhos
Perkhidmatan penyambung terhos menjalankan tugas yang sama dan menyimpan refresh token untuk setiap penyedia yang anda sambungkan kepadanya. Refresh token untuk Google atau GitHub ialah kunci jangka panjang kepada e-mel dan repositori anda, dan biasanya kekal sah selepas perubahan kata laluan. Jika perkhidmatan itu diceroboh, anda turut terjejas. Pengehosan sendiri memindahkan rekod tersebut ke dalam SQLite pada mesin yang anda sewa dan tadbir, serta melindunginya dengan kunci yang tidak pernah meninggalkan mesin anda.
Nyatakan kosnya dengan jelas sebelum bermula. VPS ini akan menjadi pelayan paling bernilai yang anda kendalikan. Pelayan ini menyimpan kelayakan yang aktif untuk sedozen perkhidmatan dalam satu fail. Oleh itu, kendalikannya seperti hos pengurus kata laluan: gunakan tembok api yang hanya mendedahkan 443, jangan gunakan log masuk dikongsi, lakukan pemulihan daripada sandaran sekurang-kurangnya sekali, dan tetapkan amaran apabila pelayan berhenti memberikan respons. Jika anda tidak sanggup meletakkan peti besi kata laluan pada mesin ini, jangan letakkan penyambung padanya juga.
Tetapkan versi sebelum memasang apa-apa
Open Connector masih baharu. Repositori ini mula muncul pada 29 June 2026, dan pada 1 August 2026 keluaran bertag terbaharu ialah v1.3.3, yang diterbitkan pada 30 July 2026 dan turut membawa tag latest. Registry turut menerbitkan tag tip, yang dibina daripada commit terbaharu pada main.
Tag yang berubah bergerak dengan kerap untuk projek yang masih baharu. docker compose pull yang melangkau dua keluaran boleh mengubah endpoint yang diperlukan oleh ejen anda. Anda mungkin menghabiskan waktu petang untuk menyahpepijat masalah itu sebagai masalah ejen. Tetapkan imej kepada tag keluaran. Naik taraf apabila anda memutuskannya selepas membaca nota keluaran.
Deploy Open Connector di sebalik TLS pada VPS anda sendiri
Sebelum kontena dimulakan, anda memerlukan:
- Docker dengan pemalam Compose, pada Ubuntu 24.04 atau sistem yang hampir sama
- nama hos yang rekod A-nya menghala ke VPS ini, contohnya
connect.example.com - proksi terbalik yang sudah menamatkan TLS (keselamatan lapisan pengangkutan) untuk nama hos tersebut
- dua rahsia rawak yang dijana di bawah
Panduan Proksi terbalik Traefik untuk berbilang aplikasi Docker Compose menerangkan bahagian proksi. Proses konfigurasi sijil yang sama, dari mula hingga selesai untuk satu aplikasi, diterangkan dalam panduan n8n pada VPS dengan Docker dan HTTPS.
Jana rahsia terlebih dahulu. Kunci penyulitan melindungi kelayakan yang disimpan. Token pentadbir melindungi konsol web dan seluruh permukaan /api. Kedua-duanya tidak mempunyai nilai lalai, dan masa jalan akan bermula tanpa kedua-duanya.
mkdir -p ~/open-connector && cd ~/open-connector
umask 077
printf 'OOMOL_CONNECT_ENCRYPTION_KEY=%s\n' "$(openssl rand -base64 32)" > .env
printf 'OOMOL_CONNECT_ADMIN_TOKEN=%s\n' "$(openssl rand -base64 32)" >> .env
chmod 600 .envSalin kedua-dua nilai ke dalam pengurus kata laluan anda sekarang, sebelum permulaan pertama. Kunci penyulitan tidak mempunyai laluan pemulihan. Sebabnya diterangkan dalam senarai kegagalan di bawah.
Sekarang compose.yaml. Ia berbeza daripada contoh huluan pada dua tempat, dan kedua-duanya penting.
services:
connector:
image: ghcr.io/oomol-lab/open-connector:v1.3.3
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
volumes:
- connector-data:/app/data
environment:
OOMOL_CONNECT_DATA_DIR: /app/data
OOMOL_CONNECT_ORIGIN: "https://connect.example.com"
OOMOL_CONNECT_ENCRYPTION_KEY: "${OOMOL_CONNECT_ENCRYPTION_KEY:?set this in .env}"
OOMOL_CONNECT_ADMIN_TOKEN: "${OOMOL_CONNECT_ADMIN_TOKEN:?set this in .env}"
volumes:
connector-data:Perubahan pertama ialah tag yang ditetapkan secara khusus dan bukannya latest. Perubahan kedua ialah port. Fail huluan menerbitkan 3000:3000, yang mengikat setiap antara muka pada hos. Docker menulis port yang diterbitkannya ke dalam jadual NAT (terjemahan alamat rangkaian) sebelum rantaian penapis ufw melihat paket tersebut. Oleh itu, ufw deny 3000 tidak menutup port itu. Perangkap ini diterangkan dalam sebab port Docker memintas ufw. Menulis 127.0.0.1:3000:3000 menerbitkan port pada antara muka gelung balik sahaja, dan proksi terbalik anda bersambung dari hos yang sama.
:? menandakan setiap pemboleh ubah sebagai diperlukan. Oleh itu, tindanan enggan bermula apabila .env tiada, bukannya bermula dengan kelayakan yang tidak disulitkan. Menyimpan nilai dalam .env dan bukannya dalam fail compose ialah corak daripada fail env dan rahsia Docker Compose.
docker compose up -d
docker compose logs -n 30 connector
curl -s http://127.0.0.1:3000/health
sudo ss -tlnp | grep 3000/health menjawab { "ok": true } selepas masa jalan aktif. ss mesti mencetak 127.0.0.1:3000. Baris yang berbunyi 0.0.0.0:3000 bermaksud pemetaan port masih merupakan pemetaan huluan, dan get laluan menjawab terus kepada seluruh internet. Sambungan ditolak semasa pemeriksaan kesihatan bermaksud kontena belum mendengar sambungan. Oleh itu, baca log sebelum mengubah proksi.
Label Traefik untuk perkhidmatan yang sama
labels:
- "traefik.enable=true"
- "traefik.http.routers.connector.rule=Host(`connect.example.com`)"
- "traefik.http.routers.connector.entrypoints=websecure"
- "traefik.http.routers.connector.tls.certresolver=le"
- "traefik.http.services.connector.loadbalancer.server.port=3000"Apabila Traefik berjalan dalam Docker pada hos yang sama, sambungkan perkhidmatan ini ke rangkaian Traefik dan padamkan blok ports:. Traefik mengakses kontena melalui rangkaian dalaman, jadi tiada apa-apa perlu diterbitkan kepada hos. certresolver=le mesti sepadan dengan nama resolver dalam konfigurasi statik Traefik. Jika tidak, penghala bermula tanpa sijil.
Mengapa OAuth memerlukan nama hos sebenar
OOMOL_CONNECT_ORIGIN ialah tetapan yang sering dilangkau. Jika tetapan ini dilangkau, OAuth gagal dengan cara yang kelihatan seperti pepijat penyedia. Runtime membina URI lencongan daripada asal tersebut dalam bentuk <origin>/oauth/callback. Jika tidak ditetapkan, asal akan lalai kepada http://localhost:3000. Oleh itu, runtime menghantar URI lencongan http://localhost:3000/oauth/callback kepada penyedia, sedangkan aplikasi OAuth anda mendaftarkan https://connect.example.com/oauth/callback. Kedua-dua rentetan ini berbeza, lalu GitHub memberikan jawapan:
The redirect_uri MUST match the registered callback URL for this application.Penyedia OAuth mengarahkan pelayar kembali ke URI tersebut. Oleh itu, URI itu mestilah alamat yang boleh dicapai dari luar. Penyedia menolak http:// biasa untuk semua tujuan selain localhost. Itulah sebab keseluruhan penggunaan ini memerlukan nama hos dan sijil. Tetapkan asal sebelum memulakan buat kali pertama kerana nilainya dibaca semasa permulaan. Selepas mengedit .env atau compose.yaml, jalankan docker compose up -d sekali lagi untuk menggunakan perubahan tersebut.
Sambungkan pembekal pertama anda melalui OAuth
Cipta aplikasi OAuth di pihak pembekal terlebih dahulu. Di GitHub, pilih Settings, kemudian Developer settings, kemudian OAuth Apps, kemudian New OAuth App. Tetapkan URL panggilan balik keizinan kepada https://connect.example.com/oauth/callback. Simpan ID klien dan rahsia klien.
Setiap panggilan /api membawa token pentadbir, jadi eksport token itu sekali untuk sesi shell.
export ADMIN_TOKEN='paste-the-admin-token'
curl -s https://connect.example.com/api/oauth/configs \
-H "authorization: Bearer $ADMIN_TOKEN"Senarai itu menunjukkan URI ubah hala yang dijangka oleh runtime bagi setiap pembekal. Ini ialah pemeriksaan terpantas untuk memastikan origin anda telah berkuat kuasa. Jika masih memaparkan localhost, container sedang berjalan dengan nilai lama dan aliran OAuth akan gagal pada langkah terakhir.
Simpan kelayakan klien, kemudian mulakan proses keizinan.
curl -s -X PUT https://connect.example.com/api/oauth/configs/github \
-H "authorization: Bearer $ADMIN_TOKEN" \
-H 'content-type: application/json' \
-d '{"clientId":"...","clientSecret":"..."}'
curl -s -X POST https://connect.example.com/api/oauth/authorizations \
-H "authorization: Bearer $ADMIN_TOKEN" \
-H 'content-type: application/json' \
-d '{"service":"github"}'Panggilan kedua mengembalikan authorizationUrl. Buka URL itu dalam pelayar, luluskan skop, dan pembekal mengarahkan pelayar kembali ke /oauth/callback. Di situ, runtime menukar kod tersebut dan menyimpan kelayakan. Konsol web pada origin anda menjalankan langkah yang sama melalui borang dengan token pentadbir yang sama. Pembekal yang menggunakan kunci API biasa tidak memerlukan semua ini: PUT /api/connections/<service> dengan {"authType":"api_key","values":{"apiKey":"..."}} menyimpan kunci itu secara terus.
Berikan setiap ejen token masa jalan, bukan kelayakan
Ejen mengesahkan identiti kepada get laluan menggunakan token masa jalan yang dikeluarkan oleh API pentadbir.
curl -s -X POST https://connect.example.com/api/runtime-tokens \
-H "authorization: Bearer $ADMIN_TOKEN" \
-H 'content-type: application/json' \
-d '{"name":"research-agent"}'Respons mengandungi token yang bermula dengan oct_. Keluarkan satu token untuk setiap ejen dan namakannya mengikut ejen tersebut, kerana membatalkan token yang tidak dapat dikenal pasti bermakna membatalkan kesemuanya. Ejen kemudian memanggil tindakan melalui HTTP biasa.
curl -s -X POST https://connect.example.com/v1/actions/github.get_current_user \
-H "authorization: Bearer oct_..." \
-H 'content-type: application/json' \
-d '{"input":{}}'Respons yang sihat ialah sampul yang medan success-nya bernilai true, dengan muatan penyedia dalam data. Token GitHub tidak terdapat di mana-mana dalam respons itu. Untuk klien MCP, arahkan klien itu ke https://connect.example.com/mcp dengan header bearer yang sama. Get laluan tersebut menyediakan alat penemuan seperti search_actions dan execute_action, bukannya satu alat bagi setiap API. Ini mengekalkan senarai alat ejen supaya kecil. Menjalankan pelayan MCP pada VPS menerangkan bahagian pendawaian klien.
Jalankan satu pemeriksaan lagi sebelum menganggap konfigurasi ini selesai. Ulangi panggilan tindakan dengan header authorization dipadamkan. Panduan mula cepat projek itu sendiri memanggil /v1 tanpa bearer langsung. Oleh itu, pemasangan tanpa pengesahan masa jalan akan melaksanakan tindakan untuk sesiapa sahaja yang boleh mencapai port tersebut. Jika panggilan tanpa pengesahan anda berjaya, terdapat dua pilihan: konfigurasikan token masa jalan dan sahkan bahawa panggilan tanpa nama kini gagal, atau hadkan /api, /v1 dan /mcp pada proksi terbalik kepada alamat asal ejen anda. Hanya /oauth/callback perlu kekal terbuka kepada umum kerana itulah satu-satunya laluan yang diperlukan oleh ubah hala pelayar penyedia.
Hadkan senarai tindakan kepada perkara yang diperlukan oleh ejen
Gateway dengan seribu pembekal di belakangnya memberikan permukaan yang luas kepada model bahasa. Dua kawalan dapat mengecilkan permukaan ini.
OOMOL_CONNECT_ALLOWED_ACTIONS menerima senarai benarkan yang dipisahkan dengan koma dan memahami service.* serta *. OOMOL_CONNECT_BLOCKED_ACTIONS ialah senarai tolak, dan senarai tolak mempunyai keutamaan. Menetapkan senarai benarkan kepada github.get_current_user,github.list_issues bermakna semua tindakan lain ditolak tanpa mengira permintaan ejen. Perbezaan ini menentukan sama ada sesuatu kesilapan menjadi insiden. Token masa jalan mempunyai peraturan tindakan sendiri di samping peraturan global. Senarai allowedProxies token itu bermula kosong, jadi POST /v1/proxy/:service ditolak sehingga anda membenarkannya. Titik akhir proksi itu memajukan permintaan mentah kepada pembekal dengan kelayakan anda dilampirkan. Oleh itu, biarkan senarai itu kosong kecuali ejen tertentu memerlukannya.
OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK ditetapkan kepada false secara lalai. Tetapan ini menghalang sambungan pembekal yang dihoskan sendiri daripada menunjuk kepada alamat peribadi, seperti perkhidmatan metadata awan pada 169.254.169.254 atau pangkalan data anda pada rangkaian yang sama. Biarkan tetapan ini dimatikan. Hidupkannya hanya untuk pembekal yang anda hos sendiri.
Sandarkan kotak yang menyimpan setiap token
Dua perkara penting, dan setiap satunya tidak berguna tanpa yang satu lagi. Pangkalan data di /app/data/connect.sqlite dalam volum connector-data menyimpan bukti kelayakan yang disulitkan. Kunci penyulitan dalam .env menyahsulitnya. Sandaran volum tanpa kunci tidak memulihkan apa-apa, dan kunci tanpa volum juga tidak memulihkan apa-apa. Oleh itu, simpan kunci dalam pengurus kata laluan anda dan masukkan volum dalam kitaran sandaran biasa anda.
Hentikan container semasa anda menyalin fail SQLite. Salinan yang dibuat semasa operasi tulis boleh dipulihkan sebagai pangkalan data yang rosak.
docker volume ls | grep connector-data
docker compose stop connector
docker run --rm -v open-connector_connector-data:/data -v "$PWD":/backup alpine \
tar czf /backup/connector-data.tgz -C /data .
docker compose start connectorNama volum ialah direktori projek anda yang ditambah dengan _connector-data. Itulah sebabnya arahan pertama disertakan di situ: tampalkan nama sebenar ke dalam arahan ketiga. Hantar arkib keluar dari VPS menggunakan sandaran restic daripada VPS, yang menyulitkannya sebelum arkib itu dihantar, kerana arkib tersebut ialah stor kelayakan.
Runtime menyimpan pelaksanaan tindakan terkini sebagai rekod audit, iaitu 5,000 rekod secara lalai. Oleh itu, konsol boleh menunjukkan ejen yang menjalankan sesuatu tindakan serta waktunya. Log itu ialah perkara pertama yang perlu dibaca apabila ejen berkelakuan pelik. Halakan halaman status Uptime Kuma ke https://connect.example.com/health juga. Apabila gateway berhenti menjawab, ejen gagal dengan cara yang mengelirukan. Mengetahui bahawa gateway tidak berfungsi dapat menjimatkan sejam membaca output ejen.
Perkara yang gagal dan mesej yang akan dipaparkan
redirect_uri_mismatch pada pembekal. Asal dan URL panggil balik yang didaftarkan adalah berbeza. Bandingkan rentetan tepat daripada /api/oauth/configs dengan tetapan aplikasi pembekal, termasuk https dengan http dan sebarang garis miring di hujung.
Setiap panggilan /api mengembalikan 401. Pengepala token pentadbir tiada atau tersalah eja. Pengepalanya ialah Authorization: Bearer <token>, dan konsol web meminta token yang sama.
Kontena berjalan, dan kelayakan disimpan sebagai teks biasa. Keadaan ini berlaku apabila OOMOL_CONNECT_ENCRYPTION_KEY tidak pernah sampai ke kontena, kerana masa jalan menyimpan rekod kelayakan tanpa penyulitan dan bukannya enggan bermula. Buktikan pada pemasangan anda sendiri: sambungkan pembekal dengan kunci API yang boleh anda kenal pasti, kemudian cari kunci itu dalam pangkalan data.
docker compose cp connector:/app/data/connect.sqlite /tmp/connect.sqlite
grep -c 'github_pat_' /tmp/connect.sqlite
shred -u /tmp/connect.sqliteBilangan melebihi 0 bermaksud kunci tidak berkuat kuasa. Oleh itu, semak bahawa .env berada dalam direktori yang sama dengan compose.yaml dan bahawa docker compose config memaparkan nilainya. Apabila kunci ditetapkan, carian yang sama mengembalikan 0 kerana rekod itu dimeterai dengan AES-256-GCM (advanced encryption standard, kunci 256-bit, mod Galois/counter).
Tiada apa-apa yang boleh dinyahsulit selepas pemulihan. Kunci penyulitan berubah atau hilang. Kunci itu tidak pernah ditulis bersebelahan dengan data, mengikut reka bentuk. Oleh itu, tiada laluan pemulihan dan tiada tiket sokongan yang dapat membantu. Sambungkan semula setiap pembekal. Putaran kunci disokong melalui pemboleh ubah kunci berasingan dan arahan data dalam masa jalan. Oleh itu, baca nota keluaran semasa sebelum anda memutarkan kunci.
Ejen menerima ralat yang menamakan tindakan yang boleh dilihatnya dalam katalog. Penemuan dan pelaksanaan adalah berasingan. Tindakan boleh muncul dalam search_actions tetapi masih ditolak oleh OOMOL_CONNECT_ALLOWED_ACTIONS, oleh senarai penolakan, atau oleh peraturan token masa jalan itu sendiri.
Penaiktarafan. Sandarkan volum, edit teg imej kepada keluaran baharu, kemudian docker compose pull && docker compose up -d. Pantau docker compose logs -n 50 connector untuk baris migrasi, kemudian jalankan semula pemeriksaan kesihatan dan satu tindakan sebenar sebelum anda mempercayainya semula. Pengembalian bermaksud menetapkan semula teg lama, yang berfungsi hanya kerana anda menetapkannya secara khusus.
FAQ
Adakah saya memerlukan domain awam untuk mengehos sendiri Open Connector?
Bagi penyedia yang menggunakan kunci API, tidak: get laluan pada 127.0.0.1 sudah mencukupi. Untuk OAuth, secara praktiknya ya. Penyedia mengubah hala pelayar ke URL panggil balik anda, jadi URL itu mesti boleh dicapai dari internet awam, dan penyedia menolak http:// biasa selain localhost. Tetapkan OOMOL_CONNECT_ORIGIN kepada nama hos https:// anda sebelum permulaan pertama, dan daftarkan <origin>/oauth/callback dalam aplikasi OAuth penyedia.
Apakah yang berlaku jika saya kehilangan kunci penyulitan Open Connector?
Kredensial yang disimpan tidak boleh dinyahsulitkan dan tiada pemulihan. Kunci itu sengaja tidak pernah disimpan bersama data, jadi tiada sesiapa yang memiliki pangkalan data boleh membacanya, termasuk anda. Satu-satunya pilihan anda ialah menetapkan kunci baharu dan menyambungkan semula setiap penyedia. Simpan kunci dalam pengurus kata laluan dan pangkalan data dalam kitaran sandaran anda, kerana pemulihan memerlukan kedua-duanya.
Bolehkah ejen AI saya melihat token akses penyedia?
Tidak apabila ejen memanggil melalui get laluan. Ejen mengesahkan dirinya dengan token masa jalan yang bermula dengan oct_, dan get laluan memasukkan kredensial penyedia ke dalam permintaan keluar pada pelayan, lalu hanya mengembalikan respons. Dua perkara menjejaskan ciri ini: titik akhir /v1/proxy/:service, yang memajukan permintaan mentah dengan kredensial anda dilampirkan dan yang pemberiannya bermula kosong atas sebab tertentu, serta menampal kunci API ke dalam ejen sendiri, yang memintas get laluan sepenuhnya.
Patutkah get laluan boleh dicapai dari internet awam?
Hanya /oauth/callback perlu boleh dicapai. Terbitkan port kontena pada 127.0.0.1 supaya peraturan NAT Docker tidak dapat mendedahkannya melepasi tembok api anda, dan letakkan proksi songsang di hadapannya. Kemudian uji satu panggilan tindakan tanpa pengepala authorization. Jika panggilan itu berjaya, hadkan /api, /v1 dan /mcp pada proksi kepada alamat yang digunakan oleh ejen anda sehingga hanya panggilan yang disahkan dapat berfungsi.
Adakah Open Connector sedia digunakan dalam pengeluaran?
Open Connector dilesenkan di bawah Apache 2.0 dan berkembang dengan pantas: repositori itu muncul pada 29 June 2026 dan v1.3.3 dikeluarkan pada 30 July 2026, jadi anggap setiap nombor versi dalam panduan ini sebagai gambaran pada 1 August 2026. Jalankan dengan tag keluaran yang ditetapkan, jangan sekali-kali menggunakan latest atau tip, baca nota keluaran sebelum setiap naik taraf, dan simpan sandaran volum yang telah anda pulihkan sekurang-kurangnya sekali. Reka bentuk ini kukuh untuk pelayan yang anda miliki, dan risikonya ialah perubahan versi, bukannya seni bina.