SSD Nodes Learn Hosting plans →
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-31

Paano i-self-host ang Open Connector para sa AI agents

Patakbuhin ang Open Connector auth gateway sa sariling VPS: pinned image, TLS origin, OAuth callbacks, at backups para hindi humawak ng SaaS token ang agents.

Ano ang ginagawa ng Open Connector para sa isang AI agent

Kapag ikaw mismo ang nagho-host ng Open Connector, naglalagay ito ng isang auth gateway sa pagitan ng iyong mga AI agent at ng lahat ng software as a service (SaaS) API na ginagamit nila. Dahil dito, hindi kailanman humahawak ang agent ng provider token. Isa itong open source gateway mula sa OOMOL Lab at lisensyado sa ilalim ng Apache 2.0. Tumatakbo ito bilang isang container, iniimbak ang state nito sa isang SQLite file, at inilalantad ang mga action ng provider sa HTTP at sa MCP (model context protocol).

Nagsisimula ang problema sa ikalawang integration. May sariling OAuth (open authorization) flow ang bawat provider, sariling lifetime ng refresh token, at sariling mga pangalan ng scope. Kapag manu-manong ikinabit ang limang provider sa isang agent, kailangan ng limang redirect handler, limang credential store, at limang refresh loop na dapat tumakbo bago mag-expire ang token. Halos walang nagsusulat ng ganoong code. Sa halip, gumagawa sila ng isang matagalang personal access token para sa bawat serbisyo at inilalagay ito sa agent config, environment file, o mismong prompt. Nabubura ang hangganan ng token na iyon dahil nababasa ito ng bawat tool na pinapatakbo ng agent, at napupunta ito sa transcript. Ito ang failure na inilalarawan sa pagpapanatiling lihim ng mga credential mula sa AI agent.

Hinahati ng auth gateway sa dalawang bahagi ang credential. Iniimbak ng gateway ang credential ng provider at pinapatakbo nito ang OAuth flow. Ang natatanggap ng agent ay isang runtime token na valid lamang laban sa gateway. Kapag tumawag ang agent ng isang action, kinukuha ng gateway ang nakaimbak na credential, ini-inject ito sa outbound request sa server side, at ibinabalik lamang ang response body. Hindi kailanman natatanggap ng agent ang access token ng provider. Kaya kung ma-leak ang transcript ng agent, isang runtime token lamang na maaaring i-revoke ang mawawala sa iyo, sa halip na ang iyong GitHub account.

Ipinapakita ng catalog ang mahigit 1,000 provider at 10,000 prebuilt action. Sariling bilang ito ng proyekto at hindi mo ito mabe-verify mula sa labas. Ang mabe-verify mo ay ang estruktura: isang HTTP endpoint para sa bawat action, isang nakaimbak na connection para sa bawat provider, at isang token para sa bawat agent. Kung bago pa sa iyo ang agent side nito at hindi pa malinaw ang mga terminong gaya ng tool call o MCP server, unti-unting binubuo ng staged path sa kung paano matutunan ang AI agent mula sa simula ang loop, mga tool, at mga kasanayan sa kaligtasan na ipinapalagay ng ganitong gateway na alam mo na.

Bakit mag-self-host ng Open Connector sa halip na gumamit ng hosted connector service

Pareho ang ginagawa ng hosted connector service, at hawak nito ang refresh token para sa bawat provider na ikinokonekta mo rito. Ang refresh token para sa Google o GitHub ay isang pangmatagalang cryptographic key para sa iyong mail at mga repository, at karaniwan itong nananatiling valid kahit magpalit ka ng password. Kapag na-breach ang serbisyo nila, breach mo rin iyon. Sa self-hosting, inililipat ang mga record na iyon sa SQLite sa isang machine na inuupahan at pinamamahalaan mo, at pinoprotektahan ng isang key na hindi kailanman lumalabas sa iyong box.

Tukuyin muna ang gastos bago magsimula. Ang VPS na ito ang magiging pinakamahalagang server na pinapatakbo mo. Naglalaman ito ng mga aktibong credential para sa dose-dosenang serbisyo sa iisang file, kaya dapat itong pangasiwaan tulad ng host ng password manager: firewall na 443 lamang ang inilalantad, walang shared login, backup na aktuwal mong na-restore kahit isang beses, at alert kapag hindi na ito sumasagot. Kung hindi mo ilalagay ang iyong password vault sa box na ito, huwag mo ring ilagay rito ang connector.

Mag-pin ng version bago mag-install ng anuman

Bago pa ang Open Connector. Unang lumitaw ang repository noong 29 June 2026, at noong 1 August 2026, ang pinakabagong tagged release ay v1.3.3, na inilabas noong 30 July 2026 at may latest tag din. Nagpa-publish din ang registry ng tip tag, na binuo mula sa pinakabagong commit sa main.

Sa ganito kabagong project, madalas magbago ang mga moving tag. Maaaring baguhin ng isang docker compose pull na lumaktaw ng dalawang release ang isang endpoint na kailangan ng iyong agent, at gugugulin mo ang gabi sa pag-debug na tila problema ito ng agent. I-pin ang image sa isang release tag, at mag-upgrade kapag ikaw ang nagpasya, matapos basahin ang release notes.

I-deploy ang Open Connector sa likod ng TLS sa sarili mong VPS

Bago magsimula ang container, kailangan mo ng:

  • Docker na may Compose plugin, sa Ubuntu 24.04 o katumbas na bersyon
  • hostname na ang A record ay nakaturo sa VPS na ito, halimbawa connect.example.com
  • reverse proxy na nagte-terminate na ng TLS (transport layer security) para sa hostname na iyon
  • dalawang random secret na bubuuin sa ibaba

Sinasaklaw ng Traefik reverse proxy para sa maraming Docker Compose app ang bahagi ng proxy. Nasa gabay na n8n sa VPS gamit ang Docker at HTTPS ang buong certificate setup, mula simula hanggang matapos, para sa isang app.

Buuin muna ang mga secret. Pinoprotektahan ng encryption key ang nakaimbak na credentials. Pinoprotektahan ng admin token ang web console at ang buong /api surface. Walang default ang alinman sa mga ito, at maayos na magsisimula ang runtime kahit wala ang mga ito.

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 .env

Kopyahin ang dalawang value sa password manager mo ngayon, bago ang unang pagsisimula. Walang recovery path ang encryption key. Ipinaliliwanag ang dahilan sa failure list sa ibaba.

Ngayon, compose.yaml. Naiiba ito sa upstream example sa dalawang bahagi, at parehong mahalaga ang mga ito.

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:

Ang unang pagbabago ay ang pinned tag sa halip na latest. Ang ikalawa ay ang port. Ipinapublish ng upstream file ang 3000:3000, na nagbi-bind sa bawat interface ng host. Isinusulat ng Docker ang mga published port nito sa NAT (network address translation) table bago makita ng ufw filter chain ang packet, kaya hindi isinasara ng ufw deny 3000 ang port na iyon. Ito ang trap na inilalarawan sa kung bakit nilalampasan ng Docker ports ang ufw. Kapag 127.0.0.1:3000:3000 ang isinulat, loopback interface lamang ang pinagbubukasan ng port, at kumokonekta ang reverse proxy mula sa parehong host.

Minamarkahan ng :? ang bawat variable bilang required, kaya tumatangging magsimula ang stack kapag nawawala ang .env sa halip na magsimula nang hindi encrypted ang credentials. Ang pagpapanatili ng mga value sa .env sa halip na sa compose file ang pattern mula sa Docker Compose env files at secrets.

docker compose up -d
docker compose logs -n 30 connector
curl -s http://127.0.0.1:3000/health
sudo ss -tlnp | grep 3000

Sinasagot ng /health ang { "ok": true } kapag umaandar na ang runtime. Kailangang mag-print ang ss ng 127.0.0.1:3000. Ang linyang 0.0.0.0:3000 ay nangangahulugang upstream pa rin ang port mapping, kaya direktang sumasagot ang gateway sa buong internet. Ang connection refused sa health check ay nangangahulugang hindi pa nakikinig ang container, kaya basahin muna ang logs bago galawin ang proxy.

Mga Traefik label para sa parehong serbisyo
    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"

Kapag tumatakbo ang Traefik sa Docker sa parehong host, idagdag ang serbisyong ito sa Traefik network at tanggalin ang ports: block, dahil ina-access ng Traefik ang container sa internal network at walang kailangang i-publish sa host. Kailangang tumugma ang certresolver=le sa resolver name sa static config ng Traefik. Kung hindi, magsisimula ang router nang walang certificate.

Bakit kailangan ng OAuth ng totoong hostname

OOMOL_CONNECT_ORIGIN ang setting na madalas nalalaktawan, at kapag nilaktawan ito, nasisira ang OAuth sa paraang mukhang bug ng provider. Binubuo ng runtime ang redirect URI mula sa origin na iyon, sa format na <origin>/oauth/callback. Kapag hindi ito itinakda, ang origin ay nagde-default sa http://localhost:3000. Dahil dito, nagpapadala ang runtime sa provider ng redirect URI na http://localhost:3000/oauth/callback, samantalang https://connect.example.com/oauth/callback ang nakarehistro sa OAuth app mo. Magkaiba ang dalawang string, kaya ito ang sagot ng GitHub:

The redirect_uri MUST match the registered callback URL for this application.

Nire-redirect ng OAuth provider ang browser pabalik sa URI na iyon. Ibig sabihin, dapat itong isang address na maaabot ng labas na mundo. Tinatanggihan ng mga provider ang plain http:// para sa anumang gamit maliban sa localhost. Iyan ang buong dahilan kung bakit kailangan ng deployment na ito ng hostname at certificate. Itakda ang origin bago ang unang start, dahil binabasa ang value sa startup. Pagkatapos i-edit ang .env o compose.yaml, patakbuhin muli ang docker compose up -d para mailapat ito.

Ikonekta ang una mong provider gamit ang OAuth

Unang gawin ang OAuth app sa provider. Sa GitHub, pumunta sa Settings, pagkatapos ay Developer settings, OAuth Apps, at New OAuth App. Itakda ang authorization callback URL sa https://connect.example.com/oauth/callback. Itago ang client ID at client secret.

Dala ng bawat /api call ang admin token, kaya isang beses mo itong i-export para sa shell session.

export ADMIN_TOKEN='paste-the-admin-token'
curl -s https://connect.example.com/api/oauth/configs \
  -H "authorization: Bearer $ADMIN_TOKEN"

Ipinapakita ng listing na iyon ang redirect URI na inaasahan ng runtime para sa bawat provider. Ito ang pinakamabilis na paraan para tiyaking naging aktibo ang iyong origin. Kung localhost pa rin ang ipinapakita, tumatakbo ang container gamit ang dating value at mabibigo ang OAuth flow sa huling hakbang.

I-store ang client credentials, pagkatapos ay magsimula ng authorization.

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"}'

Nagbabalik ang ikalawang call ng authorizationUrl. Buksan ito sa browser, aprubahan ang scopes, at ibabalik ng provider ang browser sa /oauth/callback. Doon ipinagpapalit ng runtime ang code at ini-store ang credential. Ipinapakita ng web console sa iyong origin ang parehong mga hakbang gamit ang form at sa ilalim ng parehong admin token. Nilalaktawan ng mga provider na plain API key ang lahat ng ito: direktang ini-store ng PUT /api/connections/<service> kasama ang {"authType":"api_key","values":{"apiKey":"..."}} ang key.

Bigyan ang bawat agent ng runtime token, hindi ng credential

Nag-a-authenticate ang agent sa gateway gamit ang runtime token na mina-mint ng admin API.

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"}'

May token ang response na nagsisimula sa oct_. Mag-issue ng tig-iisang token para sa bawat agent at pangalanan ito ayon sa agent na gagamit nito. Kung hindi mo matukoy ang token, kailangan mong i-revoke ang lahat ng token. Pagkatapos, tumatawag ang agent ng mga action gamit ang karaniwang HTTP.

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":{}}'

Ang tamang response ay isang envelope na may success na field na may halagang true, at nasa ilalim ng data ang payload mula sa provider. Wala saanman sa response na iyon ang GitHub token. Para sa MCP client, ituro ito sa https://connect.example.com/mcp gamit ang kaparehong bearer header. Nag-aalok ang gateway ng mga discovery tool gaya ng search_actions at execute_action sa halip na isang tool para sa bawat API, kaya nananatiling maliit ang tool list ng agent. Tinalakay sa Pagpapatakbo ng MCP servers sa isang VPS ang bahagi ng client sa wiring na ito.

Magsagawa ng isa pang pagsusuri bago mo ituring na tapos na ito. Ulitin ang action call pagkatapos tanggalin ang authorization header. Tinatatawag ng sariling quickstart ng proyekto ang /v1 nang walang bearer, kaya ang installation na walang naka-configure na runtime authentication ay magsasagawa ng mga action para sa sinumang makakaabot sa port. Kung matagumpay ang unauthenticated call, mayroon kang dalawang paraan: i-configure ang runtime tokens at tiyaking mabibigo na ang anonymous call, o limitahan ang /api, /v1 at /mcp sa reverse proxy para lamang sa mga address na pinagmumulan ng iyong mga agent. /oauth/callback lamang ang kailangang manatiling bukas sa buong internet, dahil ito ang tanging path na kailangan ng browser redirect ng provider.

Limitahan ang listahan ng mga aksyon sa mga kailangan ng agent

Malawak na attack surface ang gateway na may isang libong provider sa likod nito kapag ipinagamit ito sa isang language model. Lalo itong lumalawak kapag nagsimula nang magbasa ang model ng tekstong hindi nito isinulat, dahil maaaring maglaman ng mga instruction ang page na ibinalik ng sarili mong SearXNG instance na sumasagot sa mga web search ng agent at nakatuon ang mga ito sa anumang aksyong maaaring gawin ng agent. Ang parehong pag-iingat na dahilan kung bakit pinipili ng coding agent ang pinakamaliit na gumaganang pagbabago ay dapat ding gamitin sa permissions nito: ibigay lamang ang ilang aksyong aktuwal na kailangan ng trabaho, at wala nang iba. Dalawang control ang nagpapaliit sa saklaw nito.

Tumatanggap ang OOMOL_CONNECT_ALLOWED_ACTIONS ng comma-separated allowlist at nauunawaan nito ang service.* at *. Ang OOMOL_CONNECT_BLOCKED_ACTIONS ang denylist, at ito ang mananaig. Kapag itinakda ang allowlist sa github.get_current_user,github.list_issues, tatanggihan ang bawat iba pang aksyon anuman ang hiling ng agent. Ito ang kaibahan ng simpleng pagkakamali sa isang insidente. May sarili ring action rules ang runtime tokens bukod sa mga global rule, at walang laman sa simula ang kanilang allowedProxies list. Dahil dito, tatanggihan ang POST /v1/proxy/:service hanggang sa bigyan mo ito ng pahintulot. Ipinapasa ng proxy endpoint na iyon ang raw request sa isang provider kasama ang credential mo, kaya panatilihin itong walang laman maliban kung kailangan ito ng isang partikular na agent.

Naka-default ang OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK sa false. Pinipigilan nito ang self-hosted provider connection na tumuro sa private address gaya ng cloud metadata service sa 169.254.169.254 o sa database mo sa parehong network. Panatilihin itong naka-off. I-on lamang ito para sa provider na ikaw mismo ang nagho-host.

I-backup ang server na naglalaman ng lahat ng token

Dalawang bagay ang mahalaga, at walang silbi ang bawat isa kung wala ang isa pa. Ang database sa /app/data/connect.sqlite sa loob ng connector-data volume ang naglalaman ng mga naka-seal na credential. Ang encryption key sa .env ang nagbubukas sa mga ito. Walang maibabalik ang volume backup kung wala ang key, at walang maibabalik ang key kung wala ang volume. Kaya dapat ilagay ang key sa iyong password manager, at dapat isama ang volume sa regular mong backup rotation.

I-stop ang container habang kinokopya ang SQLite file, dahil maaaring ma-restore bilang corrupt na database ang kopyang ginawa habang may write operation.

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 connector

Ang pangalan ng volume ay iyong project directory na sinusundan ng _connector-data. Kaya naroon ang unang command: i-paste ang aktuwal na pangalan sa ikatlong command. Ipadala ang archive palabas ng VPS gamit ang restic backups mula sa VPS. Ie-encrypt nito ang archive bago ito umalis, dahil ang archive na iyon ang credential store.

Pinananatili ng runtime ang mga kamakailang action run bilang audit record, 5,000 ang default na bilang. Dahil dito, masasabi ng console kung aling agent ang nagpatakbo ng alin at kung kailan. Ang log na iyon ang unang dapat basahin kapag kakaiba ang pagkilos ng isang agent. Ituro rin ang isang Uptime Kuma status page sa https://connect.example.com/health. Kapag hindi na sumasagot ang gateway, maaaring maging malabo ang mga failure ng agent. Kapag alam mong down ang gateway, makakatipid ka ng isang oras sa pagbabasa ng agent output.

Mga nasisira, at ang mensaheng makikita mo

redirect_uri_mismatch sa provider. Magkaiba ang origin at ang registered callback URL. Ihambing ang eksaktong string mula sa /api/oauth/configs sa app settings ng provider, kabilang ang https sa http at anumang trailing slash.

Bawat tawag sa /api ay nagbabalik ng 401. Nawawala o mali ang spelling ng admin token header. Ang header ay Authorization: Bearer <token>, at hinihingi rin ng web console ang parehong token.

Tumatakbo ang container, at plain text ang mga credential. Nangyayari ito kapag hindi nakakarating ang OOMOL_CONNECT_ENCRYPTION_KEY sa container, dahil iniimbak ng runtime ang mga credential record nang hindi naka-encrypt sa halip na tumangging magsimula. Patunayan ito sa sarili mong install: kumonekta ng provider gamit ang API key na makikilala mo, pagkatapos ay hanapin ito sa database.

docker compose cp connector:/app/data/connect.sqlite /tmp/connect.sqlite
grep -c 'github_pat_' /tmp/connect.sqlite
shred -u /tmp/connect.sqlite

Ang count na higit sa 0 ay nangangahulugang hindi ginagamit ang key, kaya tiyaking nasa parehong directory ang .env at compose.yaml at ipinapakita ng docker compose config ang value. Kapag nakatakda na ang key, 0 ang ibinabalik ng parehong search, dahil naka-seal ang record gamit ang AES-256-GCM (advanced encryption standard, 256-bit key, Galois/counter mode).

Walang nade-decrypt pagkatapos ng restore. Nagbago o nawala ang encryption key. Hindi ito kailanman isinusulat katabi ng data, ayon sa disenyo, kaya walang recovery path at walang support ticket na makakatulong. Ikonekta muli ang bawat provider. Sinusuportahan ang key rotation sa pamamagitan ng hiwalay na key variable at data command sa runtime, kaya basahin ang kasalukuyang release notes bago mag-rotate ng anuman.

Nagkakaroon ng error ang agent na tumutukoy sa action na nakikita nito sa catalog. Magkahiwalay ang discovery at execution. Maaaring lumitaw ang isang action sa search_actions ngunit tanggihan pa rin ito ng OOMOL_CONNECT_ALLOWED_ACTIONS, ng denylist, o ng sariling rules ng runtime token na iyon.

Mga upgrade. I-back up ang volume, i-edit ang image tag sa bagong release, pagkatapos ay docker compose pull && docker compose up -d. I-monitor ang docker compose logs -n 50 connector para sa migration line, at patakbuhin muli ang health check at isang aktuwal na action bago ito muling pagkatiwalaan. Ang pag-rollback ay nangangahulugang ibalik ang dating tag, na gagana lamang dahil naka-pin ito.

FAQ

Kailangan ko ba ng public domain para mag-self-host ng Open Connector?

Para sa mga provider na gumagamit ng API key, hindi: sapat na ang gateway sa 127.0.0.1. Para sa OAuth, oo sa praktikal na paggamit. Nire-redirect ng provider ang browser sa iyong callback URL, kaya dapat ma-resolve ang URL na iyon mula sa public internet, at tumatanggi ang mga provider sa plain http:// sa labas ng localhost. Itakda ang OOMOL_CONNECT_ORIGIN sa iyong https:// hostname bago ang unang pag-start, at i-register ang <origin>/oauth/callback sa OAuth app ng provider.

Ano ang mangyayari kung mawala ang Open Connector encryption key ko?

Hindi made-decrypt ang mga naka-store na credential, at walang recovery. Sinasadyang hindi i-store ang key kasama ng data, kaya walang makakabasa nito na may hawak ng database, kabilang ka. Ang tanging opsyon mo ay magtakda ng bagong key at muling ikonekta ang bawat provider. Itago ang key sa password manager at isama ang database sa backup rotation mo, dahil parehong kailangan ang mga ito para sa restore.

Makikita ba ng AI agent ko ang provider access token?

Hindi kapag tumatawag ito sa pamamagitan ng gateway. Nag-a-authenticate ang agent gamit ang runtime token na nagsisimula sa oct_, at ini-inject ng gateway ang provider credential sa outbound request sa server. Ang response lamang ang ibinabalik nito. Dalawang bagay ang sumisira sa property na ito: ang /v1/proxy/:service endpoint, na nagpapasa ng raw request na nakakabit ang credential mo at sadyang walang laman sa simula ang mga grant nito, at ang mismong pag-paste mo ng API key sa agent, na ganap na lumalampas sa gateway.

Dapat bang maabot ang gateway mula sa public internet?

/oauth/callback lamang ang kailangang maabot. I-publish ang container port sa 127.0.0.1 upang hindi ito mailantad ng Docker's NAT rules lampas sa firewall mo, at ilagay sa harap nito ang reverse proxy. Pagkatapos, subukan ang isang action call nang walang authorization header. Kung magtagumpay ito, paghigpitan sa proxy ang /api, /v1 at /mcp upang mga address lamang na ginagamit ng iyong mga agent ang payagan, hanggang authenticated calls na lamang ang gumagana.

Handa na ba ang Open Connector para sa production use?

May Apache 2.0 license ito at mabilis ang development: lumabas ang repository noong 29 June 2026 at inilabas ang v1.3.3 noong 30 July 2026. Kaya ituring ang bawat version number sa gabay na ito bilang snapshot noong 1 August 2026. Patakbuhin ito na naka-pin sa isang release tag, hindi kailanman sa latest o tip. Basahin ang release notes bago ang bawat upgrade, at panatilihin ang volume backup na minsan mo nang na-restore. Maayos ang design para sa server na pagmamay-ari mo. Ang pangunahing panganib ay ang madalas na pagbabago ng version, hindi ang architecture.