همهٔ مستندات

نصب میز پشتیبانی روی زیرساخت خودتان

میز پشتیبانی روال را می‌شود روی سرور خود شرکت نصب کرد. گفتگوها، فایل‌های تماس و ایندکس جست‌وجو در دیتابیس خودتان می‌مانند و سرور فقط با api.revaal.app در داخل ایران حرف می‌زند. این صفحه همهٔ مراحل را از یک سرور خالی تا میزی که کار می‌کند می‌گوید.

چه چیزی کجاست#

روی سرور شما چهار سرویس اجرا می‌شود: برنامهٔ وب میز، api آن، انبار دانش (برای جست‌وجو) و Caddy که درِ ورود وب با گواهی TLS است. اگر فایل‌های تماس خوانده شوند، یک رابط هم کنارشان کار می‌کند. همهٔ داده در PostgreSQL خودتان است: گفتگوها، تماس‌ها و فایل صدایشان، ایندکس جست‌وجو و درخت موضوع‌ها.

کار هوش مصنوعی (رونویسی، خلاصه و دسته‌بندی، ساخت بردار برای جست‌وجو) را روال انجام می‌دهد. سرور شما برای همهٔ این‌ها فقط به api.revaal.app وصل می‌شود و روال چیزی از آن را نگه نمی‌دارد.

  • از اینترنت هیچ دسترسی ورودی‌ای به سرور لازم نیست، مگر اینکه نام عمومی و گواهی Let's Encrypt بخواهید.
  • نسخه‌های تازه شب‌ها خودشان نصب می‌شوند؛ پیش از هر به‌روزرسانی از دیتابیس نسخهٔ پشتیبان گرفته می‌شود و اگر نسخهٔ تازه بالا نیاید، نسخهٔ قبلی برمی‌گردد.
  • اشتراک با یک کلید لایسنس کار می‌کند. نصب روزی یک بار با روال تماس می‌گیرد و فقط جمع مصرف را گزارش می‌کند، نه محتوا.

پیش از شروع#

روال این‌ها را به شما می‌دهد:

موردبرای چه
کلید لایسنسفعال‌سازی، ساخت اولین مدیر و تماس روزانه با روال. تنها کلیدی که روی سرور می‌گذارید.
حساب رجیستری registry.revaal.app (فقط دانلود)دریافت ایمیج‌ها و نسخه‌های تازه.

و شما این‌ها را آماده می‌کنید:

  • یک سرور لینوکسی با Docker (بخش بعد).
  • یک دیتابیس PostgreSQL (بخش «دیتابیس»).
  • یک نام برای میز، مثلاً desk.company.ir، که به این سرور اشاره کند.
  • اختیاری: گواهی TLS شرکت برای همان نام. بدون آن، Caddy خودش گواهی می‌سازد.

سرور و شبکه#

موردحداقل
سیستم‌عاملUbuntu 22.04 یا 24.04 (روی 24.04 آزمایش شده)
پردازنده و حافظه۲ هسته، ۴ گیگابایت رم
دیسک۳۰ گیگابایت آزاد برای Docker. ایمیج‌ها حدود ۱٫۶ گیگابایت‌اند و نسخه‌های پشتیبانِ پیش از به‌روزرسانی هم همین‌جا ذخیره می‌شوند.
نرم‌افزارDocker Engine با افزونهٔ docker compose (نسخهٔ ۲)، curl و jq
جهتمقصدپورتبرای چه
خروجیapi.revaal.app (ایران)443لایسنس، رونویسی، مدل‌ها و بردارها
خروجیregistry.revaal.app (ایران)443ایمیج‌ها و نسخه‌های تازه
خروجیapi.goftino.com443فقط اگر منبع گفتینو وصل شود
داخل شبکهFTP مرکز تلفن21فقط اگر فایل‌های تماس خوانده شوند
داخل شبکهPostgreSQL5432داده‌های میز
ورودیسیستم‌های کارکنان443 (و 80)باز کردن میز در مرورگر

اگر میز نام عمومی دارد و گواهی Let's Encrypt می‌خواهید، پورت‌های 80 و 443 باید از اینترنت در دسترس باشند تا گواهی صادر و تمدید شود.

دیتابیس PostgreSQL#

میز جدول‌ها، ایندکس جست‌وجو و فایل‌هایش را در دیتابیس شما نگه می‌دارد؛ بکاپ آن با خود شماست.

  • نسخهٔ PostgreSQL 18 (نسخهٔ 16 هم آزمایش شده).
  • افزونه‌های vector (pgvector) و uuid-ossp، که فقط یک superuser می‌تواند بسازد.
  • یک کاربر که مالک دیتابیس باشد؛ میز در هر به‌روزرسانی جدول‌هایش را می‌سازد و تغییر می‌دهد.
  • دسترسی از سرور میز. اگر دیتابیس TLS را اجباری می‌کند، در .env مقدار DB_SSLMODE=require بگذارید.

مدیر دیتابیس یک بار، با کاربر superuser، این دستورها را اجرا می‌کند (رمز را خودتان انتخاب کنید):

SQL
CREATE ROLE desk LOGIN PASSWORD '...';
CREATE DATABASE desk OWNER desk;
\c desk
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

نصب#

همهٔ دستورها روی سرور میز اجرا می‌شوند. هر جا <version> آمده، نسخه‌ای را بنویسید که روال اعلام کرده؛ از آن به بعد خود میز نسخه‌های تازه را می‌گیرد.

  1. ورود به رجیستری

    با حسابی که روال داده:

    docker login registry.revaal.app
  2. پوشهٔ نصب و فایل‌های نسخه

    فایل‌ها از خود رجیستری برداشته می‌شوند: docker-compose.yml، Caddyfile، .env.example و ابزار به‌روزرسانی revaal-desk-update.

    sudo mkdir -p /opt/revaal-desk && sudo chown "$USER" /opt/revaal-desk
    cd /opt/revaal-desk
    docker create --name bundle registry.revaal.app/onprem/desk-bundle:<version> x
    docker cp bundle:/bundle/. . && docker rm bundle
    cp .env.example .env && chmod 600 .env
  3. دو رمز محلی

    این دو فقط روی همین سرور می‌مانند و به هیچ‌جا فرستاده نمی‌شوند:

    for k in AUTH_JWT_SECRET ML_API_KEY; do sed -i "s/^$k=$/$k=$(openssl rand -hex 32)/" .env; done
  4. پر کردن بقیهٔ .env

    به شکل KEY=value و بدون گیومه:

    متغیرمقدار
    DESK_DOMAINنام میز، مثلاً desk.company.ir
    DESK_TLSinternal برای نام داخلی (Caddy خودش گواهی می‌سازد)؛ برای نام عمومی یک ایمیل، تا Caddy گواهی Let's Encrypt بگیرد و تمدید کند؛ یا گواهی شرکت: /certs/fullchain.pem /certs/privkey.pem (فایل‌ها در پوشهٔ certs کنار همین فایل)
    DESK_VERSIONهمان <version>؛ از این پس ابزار به‌روزرسانی آن را جلو می‌برد
    DB_HOST، DB_PORT، DB_DATABASE، DB_USERNAME، DB_PASSWORDدیتابیس بخش قبل
    DB_SSLMODEdisable، یا require
    REVAAL_LICENSE_KEYکلید لایسنسی که روال داده
    DESK_UPDATE_WINDOW (اختیاری)ساعت‌هایی به وقت تهران که به‌روزرسانی خودکار مجاز است؛ پیش‌فرض 2-5
  5. دریافت ایمیج‌ها و اجرا

    در اولین اجرا جدول‌ها در دیتابیس خالی ساخته می‌شوند؛ چند ثانیه طول می‌کشد.

    docker compose pull && docker compose up -d
    docker compose ps
  6. بررسی فعال‌سازی

    چند ثانیه بعد از اجرا، نصب با روال تماس می‌گیرد و فعال می‌شود. تا قبل از آن همهٔ صفحه‌ها به‌جز ورود قفل‌اند.

    D=$(grep ^DESK_DOMAIN= .env | cut -d= -f2)
    curl -sk --resolve "$D:443:127.0.0.1" "https://$D/api/desk/license"

    پاسخ درست: "mode":"open"، و در reachable هر سه مقدار true.

  7. نصب ابزار به‌روزرسانی

    یک بار. از این پس نسخه‌های تازه شب‌ها خودکار نصب می‌شوند (بخش «به‌روزرسانی و بکاپ»).

    cd /opt/revaal-desk
    sudo install -m 0755 revaal-desk-update /usr/local/bin/
    sudo DESK_DIR=/opt/revaal-desk revaal-desk-update install

اولین مدیر و همکاران#

اولین مدیر یک بار و با کلید لایسنس ساخته می‌شود؛ وقتی کاربری وجود داشته باشد، این درخواست رد می‌شود. نام، ایمیل، موبایل، نام شرکت و رمز (حداقل ۸ نویسه) پرسیده می‌شود:

cd /opt/revaal-desk && D=$(grep ^DESK_DOMAIN= .env | cut -d= -f2)
read -rp 'Name: ' NAME; read -rp 'Email: ' EMAIL; read -rp 'Mobile (09...): ' MOBILE
read -rp 'Workspace (company name): ' WS; read -rsp 'Password (8+): ' PASS; echo
jq -n --arg name "$NAME" --arg email "$EMAIL" --arg mobile "$MOBILE" --arg password "$PASS" --arg workspaceName "$WS" \
  '{name:$name,email:$email,mobile:$mobile,password:$password,workspaceName:$workspaceName}' |
  curl -sk --resolve "$D:443:127.0.0.1" -H 'content-type: application/json' \
    -H "x-desk-license: $(grep ^REVAAL_LICENSE_KEY= .env | cut -d= -f2-)" -d @- "https://$D/api/desk/setup"

سپس https://<DESK_DOMAIN> را در مرورگر باز کنید و با همان ایمیل و رمز وارد شوید. میز از قبل فعال است.

  • گواهی داخلی: با DESK_TLS=internal، مرورگرها تا وقتی ریشهٔ Caddy روی سیستم‌های کارکنان معتبر نشود هشدار می‌دهند. فایل ریشه را بردارید و مثلاً با Group Policy پخش کنید: docker compose cp caddy:/data/caddy/pki/authorities/local/root.crt ./desk-root.crt
  • همکاران: نصب ایمیل نمی‌فرستد. در «کاربران» هر نفر را با ایمیل یا موبایل اضافه کنید، لینک دعوت را کپی کنید و داخل شرکت بفرستید؛ هر نفر رمز خودش را روی همان لینک می‌گذارد.
  • رمز فراموش‌شده: مالک فضای کاری در «کاربران»، از منوی «⋯» کنار نام همکار، «لینکِ تغییرِ رمز» را می‌زند و لینک را داخل شرکت می‌فرستد. لینک یک بار و تا ۲۴ ساعت کار می‌کند و همهٔ ورودهای قبلی آن شخص را می‌بندد.
  • لینک خود مالک روی سرور ساخته می‌شود (بدون ایمیل، فهرست کاربران را نشان می‌دهد): docker compose exec api node dist/auth/desk-password-link.js <email>
  • هر کس رمز خودش را در «تنظیمات»، زبانهٔ «حساب من»، عوض می‌کند.

تماس‌ها و گفتینو#

اگر میز شما از سرویس ابری روال به سرور خودتان می‌آید، این دو را بعد از انتقال داده‌ها و با هماهنگی روال وصل کنید. منبع گفتینو که هم‌زمان در دو جا خوانده شود، دو بار خوانده و دو بار هزینه می‌شود.

تماس‌ها: رابط تماس فایل‌های ضبط را از FTP مرکز تلفن (Asterisk، Issabel یا FreePBX) می‌خواند و برای میز می‌فرستد.

  1. کلید ارسال

    در میز: «منابع» ← «ارسال با API» ← کلید را کپی کنید.

  2. فایل connector.env

    کنار docker-compose.yml بسازید (chmod 600) و پر کنید:

    متغیرمقدار
    REVAAL_KEYکلید مرحلهٔ قبل
    FTP_HOST، FTP_USER، FTP_PASSWORDنشانی FTP مرکز تلفن و کاربری با دسترسی فقط خواندن
    FTP_DIRپوشه‌ای که پوشه‌های سال (2026/، 2025/ …) زیر آن است
    SINCEاولین روزی که تماس‌هایش فرستاده شود، YYYY-MM-DD
    OPERATORSنام اپراتورها بر اساس داخلی: 101=نام,102=نام
  3. اجرا

    docker compose --profile calls up -d connector

    تماس‌های کوتاه‌تر از ۱۵ ثانیه و تماس‌های بین دو داخلی فرستاده نمی‌شوند. رمز FTP فقط روی همین سرور می‌ماند.

اگر مرکز تلفن در شبکهٔ دیگری است (مثلاً میز روی یک سرور ابری و مرکز تلفن در دفتر)، رابط را روی ماشینی داخل شبکهٔ دفتر اجرا کنید و نشانی میز را به آن بدهید: در connector.env همان متغیرها، به‌اضافهٔ REVAAL_URL=https://<DESK_DOMAIN>، و کنارش این docker-compose.yml:

docker-compose.yml
services:
  connector:
    image: registry.revaal.app/onprem/desk-connector:<version>
    restart: unless-stopped
    env_file: connector.env
    volumes:
      - connector-data:/data
volumes:
  connector-data:

با DESK_TLS=internal، ریشهٔ Caddy را به رابط هم بدهید: فایل desk-root.crt را در container بگذارید و NODE_EXTRA_CA_CERTS را به آن اشاره دهید. volume با نام connector-data دفتر ارسال‌هاست؛ پاکش نکنید، وگرنه همهٔ تماس‌ها دوباره فرستاده و دوباره هزینه می‌شوند.

گفتینو: در میز، «منابع» ← گفتینو، با توکن گفتینوی شرکت.

به‌روزرسانی و بکاپ#

به‌روزرسانی خودکار است. ابزار revaal-desk-update هر ساعت نسخهٔ تازه را بررسی می‌کند و آن را شب‌ها، بین ۲ تا ۵ بامداد به وقت تهران، نصب می‌کند. مالک فضای کاری در «تنظیمات» ← «نسخه» می‌تواند همان لحظه به‌روز کند یا حالت خودکار را خاموش کند.

پیش از هر به‌روزرسانی از دیتابیس نسخهٔ پشتیبان (pg_dump) در /opt/revaal-desk/backups گرفته می‌شود و پنج نسخهٔ آخر می‌ماند. اگر نسخهٔ تازه تا ده دقیقه بالا نیاید، نسخهٔ قبلی خودکار برمی‌گردد. بکاپ منظم دیتابیس همچنان با شرکت است.

سه دستوری که لازم می‌شود: وضعیت و نتیجهٔ آخرین به‌روزرسانی، به‌روزرسانی همین حالا، و گزارش ابزار:

sudo revaal-desk-update status
sudo revaal-desk-update apply
journalctl -u revaal-desk-update

برگشت به نسخهٔ قبل با هماهنگی روال انجام می‌شود: بکاپ پیش از به‌روزرسانی و فایل‌های نسخهٔ قبل (docker-compose.yml.<version> کنار فایل تازه) با هم برمی‌گردند.

بررسی سلامت و عیب‌یابی#

بررسیدستورپاسخ درست
لایسنس و ارتباط با روالcurl -sk https://<DESK_DOMAIN>/api/desk/license"mode":"open" و در reachable همه true
کانتینرهاdocker compose psapi، web، ml و caddy در حالت Up
لاگ برنامهdocker compose logs api --tail 200خط check-in ok
  • یکی از مقدارهای reachable برابر false: آن بخش از روال الان پاسخ نمی‌دهد، یا سرور به api.revaal.app نمی‌رسد. کار مربوط منتظر می‌ماند و خودش ادامه می‌دهد؛ چیزی از دست نمی‌رود. دسترسی خروجی 443 به api.revaal.app را بررسی کنید.
  • mode غیر از open بعد از نصب: خط lastError در همان پاسخ علت را می‌گوید (کلید اشتباه، نرسیدن به روال، لایسنس معلق).
  • خطای دیتابیس در اولین اجرا (در لاگ api): اتصال (DB_HOST، شبکه، DB_SSLMODE)، رمز اشتباه، یا extension "vector" is not available که یعنی افزونه‌ها ساخته نشده‌اند.
  • bound to another install: این لایسنس به سرور دیگری بسته است؛ روال باید آن را آزاد کند.
  • به‌روزرسانی انجام نشده: sudo revaal-desk-update status نتیجهٔ آخرین تلاش و علت شکستش را می‌گوید.

برای گزارش مشکل به روال، خروجی همین دستورها را بفرستید؛ هرگز فایل .env را نفرستید.

چه چیزی از سرور خارج می‌شود#

مقصدچه چیزیبرای چه
api.revaal.appگزارش روزانه (نسخه، شناسهٔ نصب، جمع هزینه، بدون محتوا)؛ صدای تماس برای رونویسی؛ متن گفتگو برای خلاصه، دسته‌بندی و ایجنت؛ تکه‌های متن برای ساخت بردارلایسنس، صورت‌حساب، پردازش هوش مصنوعی
registry.revaal.appدانلود ایمیج‌ها، و هر ساعت یک بار فهرست نسخه‌هانصب و به‌روزرسانی
api.goftino.comخواندن گفتگوهای خود شرکتمنبع گفتینو
  • سرور میز مستقیم به خارج از ایران وصل نمی‌شود. روال کار را به سرویس‌های خودش می‌دهد: متن از طریق پراکسی روال به مدل‌های OpenRouter در خارج از ایران، و فقط به ارائه‌دهندگانی که داده را نگه نمی‌دارند؛ صدا به Soniox برای رونویسی؛ و تکه‌های متن به سرور بردار روال در ایران.
  • روال متن و صدا را ذخیره نمی‌کند؛ نسخهٔ Soniox از صدای هر تماس بعد از گرفتن متن پاک می‌شود.
  • ایندکس جست‌وجو، گفتگوها و فایل‌های تماس فقط در دیتابیس شما می‌مانند.
  • اطلاعات اتصال دیتابیس، رمزهای محلی و رمز FTP از سرور خارج نمی‌شوند.
  • گزارش خطا (Sentry) روی نصب خاموش است.

روال

بیانیه

باارزش‌ترین چیزی که هر آدمی با خودش سر کار می‌آورد، توجه اوست. هرکدام از ما هر روز مقدار محدودی انرژی و تمرکز و حضور داریم، و روزی که خرج شود برنمی‌گردد. این دربارهٔ تک‌تک آدم‌های یک تیم درست است، نه فقط کسانی که بالای هرم‌اند. پس پرسش واقعی هر شرکت، پرسش ساده‌ای است: از آدم‌ها می‌خواهیم روزهایشان را خرج چه کنند؟

بیشتر وقت‌ها پاسخ، تکرار است. از آدم‌ها می‌خواهیم کپی کنند، بچسبانند، قالب را مرتب کنند، همان فهرست را یک بار دیگر چک کنند و همان پیام را برای صدمین بار بنویسند. کار تمام می‌شود، ولی چیزی از دست رفته است. کسی که کار تکراری می‌کند، کسی است که فکر نمی‌کند. به نظر ما این اشتباه بزرگی است؛ نه به این دلیل که چنین کاری در شأن کسی نیست، بلکه به این دلیل که آدم ابزار درستی برای آن نیست. ماشین با تکرار صبور است. آدم نیست، و لازم هم نیست باشد.

ما داریم آن روی دیگر را می‌سازیم. مدل‌ها حالا هم توان پردازش دارند و هم هوش لازم را، تا بخش تکرارشونده را بردارند: خواندن، دسته‌بندی، پیش‌نویس‌نویسی، هر چیزی که از الگویی شناخته‌شده پیروی می‌کند. آن را واگذار کنید و ببینید چه برای آدم‌ها می‌ماند: ساختن، کنجکاو ماندن، کلنجار رفتن با مسئله‌هایی که پاسخ قطعی ندارند، نشستن پای پرسشی که هنوز روشن نشده، و ساختن چیزی تازه و بعد باز هم عوض‌کردنش. روز یک آدم باید خرج همین‌ها شود. به نظر ما این باید روز معمولی باشد، نه روز کمیاب.