VPS-এ OpenAnalytics self-host করার আগে যা জানবেন
শুরু করার আগে জানুন আসল চাহিদা: ClickHouse, Postgres, Valkey, 4 GB RAM, 25 GB খালি disk এবং চারটি DNS record। Install পদ্ধতি ও disk কী ভরায়, তাও দেখুন।
প্রথম ধাপের আগে প্রয়োজনীয় রিসোর্স
OpenAnalytics self-host করতে প্রায় 4 GB RAM, 25 GB খালি disk space, Compose plugin-সহ Docker এবং ওই সার্ভারের দিকে নির্দেশ করা চারটি DNS record-সহ একটি Linux VPS প্রয়োজন। এটিই বাস্তব প্রয়োজনীয়তা। প্রথম command চালানোর আগেই এটি জানা উচিত।
এই stack-এ ছয়টি application service এবং তিনটি data store রয়েছে। Postgres control plane-এর তথ্য ধরে রাখে: account, site, API key এবং share link। ClickHouse raw event এবং dashboard যে rollup পড়ে সেগুলো সংরক্ষণ করে। Valkey দুবার ব্যবহৃত হয়। একবার durable event queue হিসেবে এবং আরেকবার এমন cache হিসেবে, যেটি হারিয়ে গেলেও সমস্যা নেই। এই দুটি কাজের জন্য পরস্পরবিরোধী eviction policy প্রয়োজন। শুধু query gateway-কে ClickHouse পড়ার অনুমতি দেওয়া হয়। কোনো query চালানোর আগে এটি প্রতিটি query envelope-এর Ed25519 signature যাচাই করে।
আপনার প্রয়োজন যদি একটি binary এবং একটি config file হয়, তাহলে এটি সেই সমাধান নয়। এই শ্রেণিতে GoatCounter হলো single-binary বিকল্প: একটি Go executable, default হিসেবে SQLite, এবং কোনো external database প্রয়োজন নেই। তুলনামূলকভাবে ভারী এই stack-এর সুবিধা হলো funnel, web vitals, আপনার নিজস্ব Stripe account থেকে revenue attribution এবং একটি MCP (model context protocol) server। self-hosted analytics tool বেছে নেওয়া নিবন্ধে এই trade-off বিশ্লেষণ করা হয়েছে। এই guide ধরে নিচ্ছে যে আপনি ইতিমধ্যে সিদ্ধান্ত নিয়েছেন।
প্রথমে চারটি DNS record সার্ভারের দিকে নির্দেশ করুন
কোনো কাজ শুরু করার আগে চারটি subdomain-কে সার্ভারের public IP-তে resolve করতে হবে। কারণ Caddy প্রথমবার চালু হলে Let's Encrypt certificate-এর জন্য অনুরোধ করে, এবং যে name এখনো resolve হয় না তার জন্য challenge ব্যর্থ হয়।
app.example.comdashboard পরিবেশন করে।api.example.comAPI এবং OAuth callback পরিবেশন করে।c.example.comcollector এবং tracker script পরিবেশন করে।rt.example.comrealtime stream পরিবেশন করে।
চারটি A record ব্যবহার করুন, অথবা একটি A record এবং সেটির দিকে নির্দেশ করা তিনটি CNAME ব্যবহার করুন। এগিয়ে যাওয়ার আগে dig +short app.example.com দিয়ে নিশ্চিত করুন। এক মিনিট আগে যোগ করা কোনো name-ও যে resolver ব্যবহার করে Let's Encrypt, তার cache-এ NXDOMAIN হিসেবে থেকে যেতে পারে। তাই প্রথম certificate প্রচেষ্টা ব্যর্থ হলে কিছু সময় অপেক্ষা করুন এবং Caddy log পরীক্ষা করুন। Install আবার চালালে DNS propagation দ্রুত হয় না।
Docker Compose ব্যবহার করে OpenAnalytics নিজে হোস্ট করার পদ্ধতি
একটি tagged release checkout করুন। default branch-এ development হয়, আর published images যে সংস্করণের সঙ্গে মেলে সেটি release tag। নিচের কমান্ডগুলো ধরে নেয় যে Docker এবং Compose plugin আগে থেকেই ইনস্টল করা আছে। VPS-এ Docker Compose service চালানো বিষয়টি এই প্রক্রিয়াটি ব্যাখ্যা করে।
git clone https://github.com/OpenLabs-so/openanalytics
cd openanalytics
git checkout "$(git tag -l 'v*' --sort=-v:refname | sed '/-/d' | head -1)"
cd infra/selfhost
./generate-secrets.sh --domain example.com --email you@example.com --with-geoip
docker compose pull && docker compose up -dcheckout line-এ sed '/-/d' pre-release tag বাদ দেয়। ফলে release candidate-এর পরিবর্তে সর্বশেষ stable version নির্বাচন হয়। generation-এর সময় --with-geoip DB-IP city database fetch করে। এটি বাদ দিলে প্রতিটি event-এ country-এর মান null থাকে। ফলে geography view-তে কিছুই দেখা যায় না। পরে infra/selfhost/geoip/fetch-dbip.sh চালিয়ে, env/collector.env-এ GEOIP_DB_PATH=/geoip/dbip-city-lite.mmdb সেট করে এবং docker compose up -d --force-recreate collector ব্যবহার করে collector পুনরায় তৈরি করে এটি যোগ করতে পারেন। এই database প্রতি মাসে refresh হয়। তাই প্রতি মাসে fetch করুন, নইলে city data ধীরে ধীরে পুরোনো হয়ে যাবে।
এগিয়ে যাওয়ার আগে উৎপন্ন secret-গুলোর ব্যাকআপ নিন
Generator তিনটি জিনিস লিখে। .env-এ domain name এবং image reference থাকে। env/*.env-এ প্রতিটি service-এর জন্য একটি করে secret file থাকে। docker-compose.override.yml-এ YAML block scalar হিসেবে তিনটি Ed25519 key pair থাকে, কারণ বহু-লাইনের PEM কোনো env file-এ রাখা যায় না। সবগুলোই git-ignored, এবং এগুলোর কোনোটি একই মানে পুনরায় generate করা যায় না।
এখনই file-গুলো মেশিনের বাইরে কপি করুন। প্রতিটি file হারালে নির্দিষ্ট সমস্যা হবে:
- Store password হারালে Postgres এবং ClickHouse-এ প্রবেশাধিকার বন্ধ হয়ে যাবে। এগুলো শুধু container-এর ভেতর থেকে reset করা যায়।
OA_CREDENTIAL_KEYRINGহারালে সংরক্ষিত প্রতিটি third-party credential পুনরুদ্ধার করা যাবে না। তাই যিনি Stripe account connect করেছেন, তাকে আবার connect করতে হবে।ANONYMOUS_IDENTITY_SECRETহারালে visitor identity নতুন ভিত্তিতে গণনা শুরু হবে: গতকালের সব visitor নতুন হিসেবে গণ্য হবে, এবং এর প্রভাব chart-এ দেখা যাবে।AUTH_SECRETহারালে প্রতিটি session invalid হয়ে যাবে। তাই সবাইকে আবার sign in করতে হবে।- Signing private key হারালে key pair rotate করুন। কোনো data হারাবে না।
দুটি secret-কে প্রতিটি ক্ষেত্রে দুইটি file-এ byte-identical রাখতে হবে। ANONYMOUS_IDENTITY_SECRET, collector.env এবং worker.env-এ থাকে, কারণ collector visitor hash গণনা করে এবং worker সেটি লেখে। OA_CREDENTIAL_KEYRING, api.env এবং worker.env-এ থাকে। অন্য সব secret ইচ্ছাকৃতভাবে ঠিক একটি service-এর মধ্যে সীমাবদ্ধ। কোনো service-কে তার প্রয়োজনের বাইরে secret দিলে সেটি start না হয়ে exit করে।
স্ট্যাক চালু করুন এবং পরীক্ষা করুন
grep OA_IMAGE .env
docker compose pull
docker compose up -d
docker compose logs -f migrate
docker compose psmigrate Postgres ও ClickHouse schema প্রয়োগ করে তারপর বন্ধ হয়ে যায়। তাই বন্ধ অবস্থায় থাকা migrate container-ই সঠিক চূড়ান্ত অবস্থা। tracker-build oa.js-কে এমন একটি volume-এ compile করে, যা Caddy serve করে, এবং এটিও বন্ধ হয়ে যায়। অন্য সবকিছু docker compose ps-এ healthy হওয়া উচিত। কোনো service বারবার restart হলে প্রায় সব ক্ষেত্রেই environment validation ব্যর্থ হচ্ছে। Log-এ প্রতিটি restart-এর জন্য আলাদা করে না দেখিয়ে সব সমস্যা একটি তালিকায় দেখানো হয়। সাধারণত দুটি কারণ থাকে। কোনো variable ফাঁকা রাখা হয়েছে, যা unset হিসেবে গণ্য না করে প্রত্যাখ্যান করা হয়। অথবা secret ভুল service file-এ রাখা হয়েছে।
arm64-এ, অথবা কোনো branch থেকে কাজ করলে published image থাকে না। তখন docker compose up -d --build ব্যবহার করে স্থানীয়ভাবে build করতে হবে। 4 GB host-এ build চলার মাঝপথে memory শেষ হয়ে যায়। আগে swap যোগ করুন। এটি শুধু build চলাকালীন প্রয়োজন:
fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstabBuild শেষ হতে প্রায় দশ মিনিট লাগে। Image pull করতে কয়েক মিনিট লাগে। এই কারণেই release image সরবরাহ করা হয়।
প্রথম account অবিলম্বে তৈরি করুন
https://app.example.com খুলুন। কোনো deployment-এ এখনো কেউ sign in না করলে sign-in form দেখা যায় না; এর বদলে প্রথম account তৈরি করার অপশন দেখা যায়। এই account-টিই স্থায়ীভাবে privileged account থাকে এবং deployment settings screen দেখতে পারে এমন একমাত্র account। account তৈরি হয়ে গেলে route-টি 409 উত্তর দেয়, তাই আপনার পরে অন্য কেউ এতে প্রবেশ করতে পারে না। stack healthy হওয়ার সঙ্গে সঙ্গে এটি করুন; পরের সপ্তাহ পর্যন্ত অপেক্ষা করবেন না।
ট্র্যাকার ইনস্টল করুন
ড্যাশবোর্ডে একটি site যোগ করলে সেটি আপনাকে tag দেয়। এর কাঠামো নির্দিষ্ট:
<script
async
src="https://c.example.com/oa.js"
data-key="YOUR_TRACKING_KEY"
data-collector="https://c.example.com"
></script>এটি page head-এ বসান। Tracking key ইচ্ছাকৃতভাবেই public, তাই এটি আপনার HTML-এ রাখা স্বাভাবিক; যে কেউ এটি পড়তে পারবে। Scriptটি window.oa ইনস্টল করে। oa("track", ...)-এর মতো call-গুলো একটি stub queue করে রাখে এবং file load হওয়ার পর সেগুলো flush করে। তাই file load হওয়ার আগে চালানো custom event বাদ পড়ে না। Page-এ অন্য কোনো code আগে থেকেই window.oa ব্যবহার করলে tracker পরিবর্তে window.openanalytics হিসেবে ইনস্টল হয়।
এরপর শুরু থেকে শেষ পর্যন্ত পুরো path পরীক্ষা করুন:
curl -s https://c.example.com/oa.js -o /dev/null -w '%{http_code} %{size_download}\n'
curl -s https://api.example.com/health | head -c 200
docker compose logs --tail=50 worker | grep -i batchপ্রথম command-টির output-এ 200 এবং কয়েক কিলোবাইট data দেখা উচিত। আপনার site-এ একটি page load করুন। এরপর কয়েক সেকেন্ডের মধ্যে worker log-এ একটি batch line খুঁজুন। Collector event গ্রহণ করার সঙ্গে সঙ্গে 202 উত্তর দেয়। 202-এর অর্থ event queued হয়েছে, stored নয়। Worker-ই event-গুলো ClickHouse-এ স্থানান্তর করে। Event গ্রহণ হওয়ার পরও dashboard-এ কিছু দেখা না গেলে worker আটকে আছে। Valkey queue depth ক্রমাগত বাড়তে থাকলে এটি নিশ্চিত হয়। সাধারণ কারণ হলো worker.env-এ ভুল ClickHouse credentials, অথবা migration-এ সদ্য যোগ করা কোনো table-এ প্রয়োজনীয় grant না থাকা।
Collector public রাখুন এবং dashboard-কে authentication-এর পেছনে রাখুন
Caddy compose file-এর মধ্যেই থাকে এবং চারটি hostname-এর জন্য নিজে certificate সংগ্রহ করে। তাই default path-এ আপনার কোনো proxy configuration-এর কাজ নেই। যদি box-এ আগে থেকেই nginx reverse proxy চলে, তাহলে সরবরাহ করা infra/selfhost/nginx.conf.example ব্যবহার করে stack-টিকে তার সামনে রাখুন এবং header handling অপরিবর্তিত রাখুন:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CF-Connecting-IP "";
proxy_set_header True-Client-IP "";
proxy_set_header Fly-Client-IP "";Collector client IP থেকে দৈনিক visitor hash তৈরি করে। তাই এটি সেই address connection থেকেই নেবে, কোনো header থেকে নয়। অবিশ্বস্ত hop থেকে CF-Connecting-IP অপরিবর্তিতভাবে পাঠালে যেকোনো caller যেকোনো address দাবি করতে পারে। এতে geolocation ভুল হয় এবং একই সঙ্গে visitor count কৃত্রিমভাবে বেড়ে যায়।
Hostname অনুযায়ী access পরিষ্কারভাবে ভাগ করুন। c. এবং rt. মাপা প্রতিটি site-এর প্রত্যেক visitor-এর জন্য reachable হতে হবে। তাই এই দুটির সামনে কখনো basic auth বা IP allowlist বসাবেন না। app. এবং api. শুধু sign in করা ব্যক্তিদের জন্য reachable হওয়া প্রয়োজন। Dashboard-কে application-এর নিজস্ব auth সুরক্ষিত রাখে। AUTH_PASSWORD_SIGNIN=enabled-এর মাধ্যমে env/api.env-এ password sign-in defaultভাবে চালু থাকে। কোনো provider-এর client ID এবং client secret দুটিই থাকলেই শুধু Google বা GitHub button দেখা যায়। Magic link-এর জন্য mail transport দরকার। এটি না থাকলে API শুধু send request-টি outbox-এ লেখে। ফলে কিছু deliver হয় না এবং কোনো error-ও দেখা যায় না।
একটি setting dashboard আদৌ কাজ করবে কি না তা নির্ধারণ করে। env/api.env-এর AUTH_TRUSTED_ORIGINS-এর মান dashboard origin-এর সঙ্গে হুবহু মিলতে হবে। মানটি ভুল হলে বা না থাকলে API কোনো CORS (cross-origin resource sharing) header পাঠায় না। Browser প্রতিটি call প্রত্যাখ্যান করে। তখন dashboard layout render করে, কিন্তু কোনো data দেখায় না, যদিও docker compose ps সবকিছুকে healthy দেখায়।
Proxy config-এ থাকতেই automated traffic-এর ব্যবস্থা করুন। Crawler-রা অন্য visitor-এর মতোই collector-এ request পাঠায়। তাদের page view ClickHouse এবং আপনার সংখ্যাগুলোতে যুক্ত হয়। Server-এ AI crawler block করা database-এ data লেখার আগেই এর একটি অংশ আটকায়। এতে accuracy এবং disk—দুটির ওপরই চাপ কমে।
এখানে cookieless বলতে কী বোঝায় এবং এর জন্য আপনাকে কী দিতে হয়
কোনো cookie নেই। Visitor identity একটি salted hash হিসেবে রাখা হয়, salt প্রতিদিন পরিবর্তিত হয়, এবং raw IP address কখনো সংরক্ষণ করা হয় না। Geolocation আপনার নিজের disk-এ থাকা DB-IP file-এর সঙ্গে localভাবে মেলানো হয়। তাই visitor সম্পর্কে কোনো lookup কখনো host-এর বাইরে যায় না।
এর সুবিধা হলো visitor-এর device-এ কোনো identifier persist করা হয় না। EU ePrivacy consent rules-এর আওতায় tracker পড়ার নির্দিষ্ট কারণটি এড়ানো যায়। তাই এই ধরনের aggregate-only setup সাধারণত consent banner ছাড়াই চালানো হয়। আপনি যা সংরক্ষণ করেন এবং যতক্ষণ সংরক্ষণ করেন, তার ওপর GDPR এখনও প্রযোজ্য। আপনার ক্ষেত্রে কী প্রযোজ্য হবে, তা README নয়, আপনার নিজের legal counsel নির্ধারণ করবেন।
এর খরচ হলো বিভিন্ন দিনের identity সংযুক্ত করা যায় না। Salt পরিবর্তনের কারণে সোমবার এবং বুধবার visit করা একই ব্যক্তি design অনুযায়ী 2 জন visitor হিসেবে গণনা হন। এর কোনো workaround নেই। দৈনিক unique count নির্ভরযোগ্য। সাপ্তাহিক ও মাসিক unique count দৈনিক count থেকে তৈরি হয় এবং reach বেশি দেখাবে। তাই দীর্ঘ সময়ের “returning visitor” figure তার label অনুযায়ী প্রকৃত পরিমাপ করে না। একই দিনের মধ্যে sessions এবং journeys নির্ভরযোগ্য। ANONYMOUS_IDENTITY_SECRET rotate করলে day boundary-এর মতো একই প্রভাব পড়ে। তাই এই rotation-কে routine hygiene নয়, data change হিসেবে বিবেচনা করুন।
Collector Do Not Track এবং Global Privacy Control মেনে চলে। এটি এমন একটি browser signal, যা site-কে personal data বিক্রি বা share না করতে জানায়। একই উদ্দেশ্যে script tag-এ নিজস্ব switch রয়েছে: data-respect-gpc, data-respect-dnt এবং data-require-consent। data-require-consent consent দেওয়া পর্যন্ত সব collection বন্ধ রাখে এবং উত্তরটি localStorage-এ oa.consent key-এর অধীনে সংরক্ষণ করে। data-storage="none" সেট করলে browser storage পুরোপুরি বন্ধ হয়ে যায়।
ছয় মাস পর ডিস্ক কেন পূর্ণ হয়ে যায়
এটাই self-hosted analytics সার্ভার অচল করে দেয়। সাধারণত events এর জন্য এটি ঘটে না।
প্রথমে images দেখুন। একটি release-এ দশটি image প্রকাশিত হয়, যেগুলোর জন্য ডিস্কে মোট প্রায় 13 GB লাগে। Upgrade-এর সময় পুরোনো generation সরানোর আগে নতুন generation pull করা হয়। তাই কিছু সময়ের জন্য দুইটি generation সংরক্ষিত থাকে। একটি page view আসার আগেই 25 GB প্রয়োজনের বেশিরভাগ অংশ এভাবেই পূরণ হয়ে যায়।
এরপর snapshots। snapshot.sh stack বন্ধ করে, প্রতিটি secret-সহ উভয় data volume archive করে, তারপর আবার চালু করে। এখানে cold copy-ই একমাত্র নিরাপদ পদ্ধতি। কারণ ClickHouse background-এ parts merge করে, এবং merge চলার সময় নেওয়া copy consistent থাকে না। upgrade.sh প্রতিটি upgrade-এর আগে স্বয়ংক্রিয়ভাবে একটি snapshot নেয়। ফলে archive-গুলো একই ডিস্কে জমতে থাকে, যতক্ষণ না আপনি সেগুলোর সীমা নির্ধারণ করেন।
./snapshot.sh create --label before-something-risky
./snapshot.sh list
./snapshot.sh --keep 3ডিস্কের সীমা কাছাকাছি পৌঁছে গেলে upgrade করার আগে আগের generation সরিয়ে ফেলুন। Stack চালু থাকা অবস্থায় এটি নিরাপদ। কারণ চলমান container-গুলোকে চালানো images তখনও referenced থাকে:
docker image prune -a -fএরপর events নিজেরা। ClickHouse columnar data শক্তভাবে compress করে। তাই raw event volume অধিকাংশ মানুষের প্রত্যাশার চেয়ে ধীরে বাড়ে। Dashboard যে rollup table পড়ে, সেটিও raw table-এর তুলনায় ছোট। অনুমান না করে পরিমাপ করুন:
docker system df -v
docker compose exec clickhouse df -h /var/lib/clickhouseপ্রতিটি table-এর আকার জানতে ClickHouse credentials ব্যবহার করে এটি চালান। Generator এই credentials infra/selfhost/env/-এর অধীনে লিখেছে:
SELECT table, formatReadableSize(sum(bytes_on_disk)) AS size, sum(rows) AS row_count
FROM system.parts
WHERE active
GROUP BY table
ORDER BY sum(bytes_on_disk) DESC;প্রথম সপ্তাহে এবং আবার চতুর্থ সপ্তাহে এই পরিমাপ নিন। দুটি data point থেকে growth rate নির্ণয় করা যায়। Growth rate জানলে বোঝা যায় কখন volume-এর জন্য আরও capacity প্রয়োজন হবে। August 2026 পর্যন্ত self-hosting guide-এ raw events-এর জন্য কোনো retention বা time-to-live knob নথিভুক্ত নেই। তাই পুরোনো row নিজে থেকেই expire হবে ধরে না নিয়ে, পরিমাপ করা growth rate অনুযায়ী ডিস্কের আকার নির্ধারণ করুন।
একটি deletion trap আগে থেকেই জানা ভালো। কোনো site বা account delete করলে worker-এর জন্য কাজ queue হয়। সেই worker-এর CLICKHOUSE_MAINTENANCE_USER এবং CLICKHOUSE_MAINTENANCE_PASSWORD সেট থাকতে হয়, এবং ClickHouse-এ matching oa_maintenance user থাকতে হয়। এগুলো না থাকলে deletion চিরকাল queue-তেই থাকে। Dashboard থেকে site অদৃশ্য হয়ে যায়, কিন্তু প্রতিটি row ডিস্কে থেকে যায়। ফলে cleanup হয়েছে বলে মনে হয়, কিন্তু কোনো disk space ফেরত পাওয়া যায় না।
আপগ্রেড এবং তিনটি খরচ
git fetch --tags
git checkout "$(git tag -l 'v*' --sort=-v:refname | sed '/-/d' | head -1)"
cd infra/selfhost
./upgrade.shupgrade.sh কাজ শুরু করার আগে তিনটি খরচ দেখায়। Downtime বাস্তব সমস্যা: collector বন্ধ থাকাকালীন চেষ্টা করা event হারিয়ে যায়, কারণ tracker সেগুলো আবার চেষ্টা করে না। Rollback করলে data হারায়, কারণ rollback.sh --to backups/<snapshot> উভয় store সম্পূর্ণভাবে প্রতিস্থাপন করে এবং snapshot নেওয়ার পরে লেখা প্রতিটি row বাদ দেয়। তৃতীয় খরচ হলো disk space, যা উপরে বর্ণিত snapshot-এর স্তূপ ব্যবহার করে।
দুটি restart rule সহজেই ভুল হতে পারে। API চালু করার আগে query gateway চালু করুন, কারণ নতুন API এমন query field পাঠায় যা পুরোনো gateway প্রত্যাখ্যান করে। আর ClickHouse-এর ক্ষেত্রে restart নয়, recreate করতে হয়, কারণ docker compose restart container-এর প্রাথমিক environment পুনরায় ব্যবহার করে এবং আপনার করা edit নীরবে উপেক্ষা করে:
docker compose up -d --force-recreate clickhouseDashboard-এও একই ধরনের সমস্যা আছে। env/web.env-এর তিনটি NEXT_PUBLIC_* origin browser bundle-এ compile করা থাকে এবং container চালু হওয়ার সময় প্রতিস্থাপিত হয়। তাই ভুল hostname-এ request পাঠানো dashboard ঠিক করতে docker compose up -d --force-recreate web ব্যবহার করতে হবে; restart ব্যবহার করে এটি ঠিক হবে না। Web container-এর log-এ শুরু করার সময় ব্যবহৃত origin-গুলো দেখা যায়। Fix কার্যকর হয়েছে কি না নিশ্চিত করার এটিই দ্রুততম উপায়।
Config edit-এর পরে ClickHouse চালু হতে অস্বীকার করলে তার log-এর প্রথম line পড়ুন। oa-entrypoint: দিয়ে শুরু হওয়া line হলো entrypoint-এর বার্তা। আপনি যে value সেট করেছেন, entrypoint সেটি প্রত্যাখ্যান করেছে। অন্য কিছু দেখা গেলে সাধারণত config file-এ invalid XML থাকে। এর সবচেয়ে সাধারণ কারণ হলো XML comment-এর ভেতরে double hyphen থাকা, যা সেখানে বৈধ নয়।
AGPL-3.0 এবং নাম
কোডটি AGPL-3.0 লাইসেন্সের অধীনে প্রকাশিত। নিজের সাইটের জন্য কোডটি অপরিবর্তিত অবস্থায় চালালে কোনো source প্রকাশের বাধ্যবাধকতা তৈরি হয় না। কোড পরিবর্তন করে সেই পরিবর্তিত সংস্করণ network service হিসেবে চালানোর পর এই বাধ্যবাধকতা শুরু হয়। তখন লাইসেন্স অনুযায়ী ওই service-এর ব্যবহারকারীদের আপনার পরিবর্তিত source দেওয়া আবশ্যক। এর মধ্যে আপনার instance-এ client-দের dashboard দেওয়া এবং আপনি যে পণ্য বিক্রি করেন তার মধ্যে কোডটি অন্তর্ভুক্ত করা—দুই ক্ষেত্রই পড়ে। আপনার পরিবর্তনগুলো public fork-এ রাখলেই অতিরিক্ত কোনো প্রক্রিয়া ছাড়াই এই শর্ত পূরণ হয়।
Brand কোড থেকে আলাদা। "OpenAnalytics" নাম এবং প্রকল্পটির hosted domain লেখকদের পরিচালিত instance-কে শনাক্ত করে; এগুলো license grant-এর অংশ নয়। আপনার deployment brand ব্যবহার না করেই software চালায়। তাই paying customer-দের সামনে service দেওয়ার আগে service-এর জন্য আলাদা নাম নির্ধারণ করুন।
FAQ
আমি কি 1 GB VPS-এ OpenAnalytics চালাতে পারি?
না। প্রকল্পটির প্রায় 4 GB RAM এবং 25 GB খালি disk প্রয়োজন, কারণ একটি deployment-এ Postgres, ClickHouse এবং দুটি Valkey instance-এর পাশাপাশি ছয়টি application service চলে। শুধু ClickHouse-ই ছোট কোনো process নয়। 1 GB server-এ container-গুলো চালু হওয়ার পর kernel-এর out-of-memory killer সেগুলোর একটি বন্ধ করে দেয়, সাধারণত ClickHouse-কে। 1 GB plan-ই যদি কঠোর সীমা হয়, তাহলে GoatCounter-এর মতো single-binary tool ব্যবহার করুন। এটি SQLite-এ চলে এবং কোনো external database প্রয়োজন হয় না।
OpenAnalytics-এর সঙ্গে কি আমার cookie banner প্রয়োজন?
এটি আপনার আইনজীবীর পরামর্শের বিষয়। তবে প্রযুক্তিগত তথ্য আপনার পক্ষে আছে। কোনো cookie নেই, visitor identity প্রতিদিন পরিবর্তিত একটি salted hash, এবং raw IP address কখনো সংরক্ষণ করা হয় না। তাই visitor শনাক্ত করার জন্য কোনো স্থায়ী তথ্য লেখা হয় না। আপনি কী সংরক্ষণ করেন এবং কতদিন রাখেন, তা GDPR-এর অধীনেই থাকে। আপনি যদি collection স্পষ্টভাবে consent-নির্ভর করতে চান, script tag-এ data-require-consent সেট করুন। এরপর consent দেওয়া না হওয়া পর্যন্ত tracker কিছু সংগ্রহ করবে না এবং উত্তরটি oa.consent-এর অধীনে localStorage-এ রাখবে।
Event-এ 202 ফেরত আসে, কিন্তু dashboard-এ কখনো দেখা যায় না কেন?
202-এর অর্থ collector event গ্রহণ করে queue-তে রেখেছে। এর অর্থ event-টি সংরক্ষণ করা হয়েছে, তা নয়। Worker সেই queue থেকে event নিয়ে ClickHouse-এ লেখে। তাই request সফল হলেও dashboard খালি থাকলে worker-এর দিকটি পরীক্ষা করুন। docker compose logs --tail=50 worker পড়ুন এবং Valkey queue-এর depth monitor করুন। Queue ক্রমাগত বড় হলে worker আটকে আছে। সাধারণ কারণ হলো worker.env-এ ভুল ClickHouse credential অথবা সাম্প্রতিক migration তৈরি করা কোনো table-এ প্রয়োজনীয় grant না থাকা।
প্রতিটি container healthy থাকা সত্ত্বেও dashboard খালি কেন?
প্রথমে env/api.env-এ AUTH_TRUSTED_ORIGINS পরীক্ষা করুন। এটি dashboard origin-এর সঙ্গে হুবহু মিলতে হবে। মিল না থাকলে API কোনো CORS header পাঠায় না। ফলে browser প্রতিটি call প্রত্যাখ্যান করে, এবং আপনি data ছাড়া একটি সচল layout দেখতে পান। এরপর env/web.env-এ থাকা তিনটি NEXT_PUBLIC_* value পরীক্ষা করুন। Web container চালু হওয়ার সময় এগুলো প্রতিস্থাপিত হয়। এগুলো সংশোধন করতে docker compose up -d --force-recreate web প্রয়োজন, কারণ সাধারণ restart পুরোনো value-গুলোই রেখে দেয়।
AGPL-3.0 কি আমাকে এটি client-দের দিতে বাধা দেয়?
না, তবে একটি শর্ত প্রযোজ্য। Code অপরিবর্তিত রেখে চালালে কাউকে কিছু দিতে হবে না। Code পরিবর্তন করে সেই modified version এমন service হিসেবে চালালে যা অন্যরা ব্যবহার করে, আপনাকে সেই user-দের modified source দিতে হবে। একটি public fork এই শর্ত পূরণ করে। আলাদাভাবে, "OpenAnalytics" নামটি code-এর সঙ্গে license করা হয়নি। তাই আপনি যা বিক্রি করবেন, তার নিজস্ব নাম থাকতে হবে।