---
title: "התקנת Quire באמצעות Docker Compose"
description: "התקינו את Quire בתשתית שלכם באמצעות Docker Compose."
image: "https://docs.quirelms.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.quirelms.com/he/llms.txt
> Use this file to discover all available pages before exploring further.

# התקנת Quire באמצעות Docker Compose

<span id="installing-quire-with-docker-compose"></span>

זהו המוצר המלא במארח אחד: מערכת הלמידה, עבודות הרקע, שירותי realtime ועריכה
משותפת וכל שירות אופציונלי נוסף שמוגדר בפרופיל. התכנון נמצא בסעיף 2 של
`docs/architecture/23-ops.md`.

יעדים אחרים: [Vercel](/he/ops/vercel/) ו-[Cloudflare Workers](/he/ops/cloudflare/) מפעילים רק
את שכבת האינטרנט. הוראות השדרוג נמצאות ב-[upgrade.md](/he/ops/upgrade/), והגיבויים ותרגול
השחזור ב-[backup-restore.md](/he/ops/backup-restore/).

## מה צריך <!--quire:what-you-need-->

- Docker Engine 27 ומעלה עם תוסף Compose בגרסה 2.30 ומעלה.
- 4 ליבות CPU ו-8 GB זיכרון עבור ה-stack ברירת המחדל; 8 ליבות ו-16 GB עם
  `--profile full` (ClamAV לבדו מחזיק כ-1.5 GB של חתימות).
- שם DNS לשכבת האינטרנט ושם נוסף לתוכן לא מהימן. אלה חייבים להיות hosts שונים:
  חבילות SCORM ו-HTML שהועלה פועלים במקור התוכן, כדי שלא יוכלו לקרוא את העוגיות
  של מערכת הלמידה.
- לבדיקה מקומית, `lvh.me` ו-`*.localhost` נפתרים ל-127.0.0.1, כפי שמוגדר
  ב-`docker/.env.example`. שירות ה-`proxy` של ה-stack עצמו מגיש את שניהם ב-https
  באמצעות רשות אישורים מקומית, כך שלא נדרשת התקנה נוספת (ראו "TLS").
- יש לפנות במארח את הפורטים 80 ו-443 (`QUIRE_PROXY_HTTP_PORT` ו-`QUIRE_PROXY_HTTPS_PORT`
  משנים אותם).

## הרצה ראשונה <!--quire:first-run-->

```sh
QUIRE_APP_ORIGIN=https://learn.example.org \
QUIRE_CONTENT_ORIGIN=https://content.example-content.org \
QUIRE_SETUP_ADMIN_EMAIL=you@example.org \
  docker/scripts/init-env.sh
docker compose -f docker/compose.yaml up -d --build
docker compose -f docker/compose.yaml logs init
```

`docker/scripts/init-env.sh` יוצר את `docker/.env` מתוך `docker/.env.example` עם
כל הסודות שנוצרו (סיסמאות למסד הנתונים, מפתחות חתימה ומפתח ראשי, זוג מפתחות להפעלת
תוכן) וכן את מפתח החתימה לנקודת הביקורת ב-`docker/secrets/audit-signing-key.pem`;
Compose מחבר אותו לעובדים כסוד. נדרשים רק `sh`,‏ `awk` ו-`openssl`, והסקריפט מסרב
להחליף `docker/.env` קיים. העתיקו את שני הקבצים אל מחוץ למארח: ללא `QUIRE_MASTER_KEY`
מסד ששוחזר אינו יכול לפענח את פרטי הגישה ששמורים בו. כדי למלא את הקובץ ידנית,
העתיקו `cp docker/.env.example docker/.env`; הקובץ מסביר כיצד ליצור כל סוד.

שני המקורות חייבים להיות `https`: שירות התוכן מסרב ל-http פשוט ב-production, והם
אינם יכולים לחלוק דומיין שניתן לרשום. שירות `proxy` מסיים את TLS לשניהם (ראו "TLS");
`init-env.sh` מסרב למקור `http://`.

ה-stack מופעל בסדר קבוע, וכל שלב ממתין לקודמו:

1. `postgres` מגיע למצב בריא. בהפעלה הראשונה, סקריפט האתחול שלו
   (`docker/postgres/init/90-passwords.sh`) מגדיר סיסמאות לארבעת התפקידים.
2. `migrate` מחיל כל מיגרציה ומאתחל את תור העבודות במסד הניהול ובכל מסד tenant
   ייעודי, מוודא שכולם תואמים ואז יוצא (ראו docs/ops/upgrade.md). מיגרציות פועלות
   בכל הפעלה והן idempotent, לכן שדרוג הוא תמונה חדשה והפעלה מחדש.
3. `init` (`apps/web/src/first-run.ts`) רושם את מסד היישום תחת `QUIRE_DATABASE_ID`
   וכאשר מוגדר `QUIRE_SETUP_ADMIN_EMAIL`, יוצר את הארגון הראשון ואת מנהל המערכת שלו.
   כתובת הכניסה והסיסמה שנוצרה מוצגות פעם אחת ביומן `docker compose logs init`.
4. `web`,‏ `content`,‏ `worker`,‏ `scheduler`,‏ `collab` ו-`centrifugo` מופעלים.
5. `proxy` מופעל לאחר ש-`web` ו-`content` בריאים.

פתחו את `https://demo.` ואחריו את דומיין היישום (יומן `init` מציג את כתובת הכניסה
המדויקת) והיכנסו. בהתקנה מקומית, סמכו תחילה על רשות האישורים של ה-proxy (ראו "TLS").
שנו את הסיסמה שנוצרה ב-`/account/security`.

תהליך שמופעל בלי סוד נדרש מסרב לעלות ומציין ביומן את ההגדרה החסרה. שום שירות אינו
מופעל במצב שתצורתו חלקית.

## שירותים ופרופילים <!--quire:services-and-profiles-->

| שירות | פרופיל | תפקיד |
| --- | --- | --- |
| postgres | תמיד | מסד הנתונים (PostgreSQL 18 עם pgvector, נבנה מ-`docker/postgres.Dockerfile`), עם ארכוב WAL מההפעלה הראשונה |
| migrate, init | תמיד | הרצה חד-פעמית: מיגרציות ואחריהן הרצה ראשונה |
| web | תמיד | מערכת הלמידה, ב-`QUIRE_HTTP_PORT` (8080) |
| content | תמיד | מקור לתוכן לא מהימן, ב-`QUIRE_CONTENT_PORT` (8081) |
| worker | תמיד | עבודות רקע: דוא״ל, דוחות, עיבוד קבצים ו-webhooks |
| scheduler | תמיד | עבודות מחזוריות: רושם את 64 לוחות הזמנים של זמן ריצה ומעביר אותם לעובד; מוביל אחד בכל פעם |
| collab | תמיד | WebSocket לעריכה משותפת, ב-`QUIRE_COLLAB_HTTP_PORT` (1234) |
| centrifugo | תמיד | הפצת realtime, ב-`QUIRE_REALTIME_PORT` (8000) |
| proxy | תמיד | Caddy, שער TLS קדמי בפורטים 80 ו-443 (ראו "TLS") |
| valkey | `cache` | מטמון ומגבלות קצב |
| clamav | `scan` | סריקת קבצים שהועלו לאיתור נוזקות |
| gotenberg | `preview` | תצוגות מקדימות של Office ל-PDF ורינדור תעודות |
| imgproxy | `images` | שינוי גודל והמרת תמונות |
| transcoder | `video` | תמונת worker עם ffmpeg ברישיון LGPL בלבד, להמרת סרטונים |
| seaweedfs | `storage` | אחסון אובייקטים תואם S3 במארח הזה |
| otelcol | `observability` | אוסף OpenTelemetry |
| mailpit | `devmail` | לוכד את כל הדואר היוצא, להתנסות ב-Quire |
| backup | `backup` | גיבוי בסיס חד-פעמי; ראו backup-restore.md |
| backup-scheduler, backup-offsite | `backup` | גיבוי בסיס כל `QUIRE_BACKUP_INTERVAL_HOURS`, עותקים מוצפנים מחוץ למארח ותרגול אימות שבועי |
| h5p | `h5p` | תמונת כלי H5P LTI 1.3 שאתם מספקים באמצעות `QUIRE_H5P_IMAGE`, בפורט `QUIRE_H5P_PORT` (8090); ראו "חיבור ספק H5P" |

`--profile full` מפעיל כל שירות אופציונלי פרט ל-`backup` ול-`h5p`. הפעילו שירות אחד
כך: `docker compose -f docker/compose.yaml --profile scan up -d`. גם בלי שירות אופציונלי
Quire ממשיך לפעול ומסביר מה חסר: ללא סורק, קבצים מאוחסנים ללא סריקה ומנהל המערכת
מקבל הודעה; ללא Gotenberg, אפשר להוריד קבצים במקום לצפות בתצוגה מקדימה; ללא
transcoder, הווידאו מופעל כקובץ המקורי.

כל תמונת צד שלישי וחובות הרישוי שלה מפורטות ב-`docker/third-party-containers.yaml`.

### חיבור ספק H5P <!--quire:connecting-an-h5p-provider-->

Quire אינו מטמיע או מספק סביבת H5P או רכיב נלווה (ADR 0019). אם אתם משתמשים ב-H5P,
ספקו מנוי מתארח משלכם או הפעילו בנפרד מופע H5P באירוח עצמי. רשמו את הספק ככלי
חיצוני LTI 1.3 והוסיפו את התוכן שלו לקורסים כפעילויות כלי. Quire מעביר ציונים
והתקדמות בפעילות ובבדיקה דרך Assignment and Grade Services (AGS) של LTI. אם הספק
שולח גם הצהרות xAPI, הגדירו את הדבר בנפרד למאגר ההצהרות xAPI של Quire; העברת ציונים
והתקדמות ב-AGS אינה שולחת הצהרות xAPI. ייבוא Moodle מדווח שפעילויות H5P צריכות
חיבור לכלי LTI. הספק נשאר אחראי על סביבת H5P, יצירת התוכן, בנק התוכן והיסטוריית
הניסיונות.

כדי להפעיל מופע באירוח עצמי במארח הזה, הגדירו את `QUIRE_H5P_IMAGE` לתמונה שלו
והפעילו את פרופיל `h5p`. Compose מפרסם אותו ב-`QUIRE_H5P_PORT` (8090) ושומר את
נתוניו ב-volume‏ `h5p-data`; התמונה והחובות הנלוות אליה נשארות באחריותכם.

## הגדרות <!--quire:settings-->

כל תהליך קורא את `docker/.env`. תבנית `docker/.env.example` מפרטת כל הגדרה ואת
ברירת המחדל שלה. הקבוצות:

### כתובות <!--quire:addresses-->

| הגדרה | משמעות |
| --- | --- |
| `QUIRE_APP_ORIGIN` | הכתובת הציבורית של מערכת הלמידה, כגון `https://learn.example.com` |
| `QUIRE_CONTENT_ORIGIN` | מקור התוכן, במארח אחר |
| `QUIRE_PLATFORM_DOMAINS` | הדומיינים שבהם נמצאים הארגונים, מופרדים בפסיקים |
| `QUIRE_MARKETING_ORIGIN` | Optional. The marketing site, default `https://quirelms.com`. The only origin the waitlist form (`POST /api/waitlist`, `POST /waitlist`) accepts and redirects to. Comma separated; a `www.` variant is allowed only if listed |
| `QUIRE_DEPLOY_TARGET` | כאן הערך הוא `compose`. ל-`vercel` ו-`cloudflare` ראו מדריכים אחרים |
| `QUIRE_TRUSTED_PROXY_CIDRS` | פרוקסי שמותר להאמין לכותרת `X-Forwarded-For` שלהם |

### סודות <!--quire:secrets-->

| הגדרה | משמעות |
| --- | --- |
| `QUIRE_SECRET_KEY` | חתימת הפעלות ואסימונים. 64 תווי hex |
| `QUIRE_MASTER_KEY` | עטיפת פרטי גישה שמורים, כגון סודות SSO ו-webhook.‏ 32 bytes,‏ base64. שכבת האינטרנט וה-worker זקוקים לערך זהה. החלפה: [key-rotation.md](/he/ops/key-rotation/) |
| `QUIRE_MASTER_KEY_VERSION` | תווית הגרסה של המפתח הראשי, `v1` אם לא הוגדר. הגדילו בעת החלפה |
| `QUIRE_MASTER_KEY_RETIRED` | מפתחות ראשיים קודמים שנדרשים עדיין לקריאת מה שחתמו, בפורמט `v1=<base64>`. הסירו לאחר שההחלפה מסתיימת בלי פריטים שלא נפתרו |
| `QUIRE_COLLAB_SIGNING_KEY` | משותף ל-web ול-collab כדי לחתום על אסימוני עריכה |
| `QUIRE_BACKUP_SIGNING_KEY` | חתימה על גיבויי קורסים (אופציונלי) |

שמרו עותק של `QUIRE_MASTER_KEY` במקום שאינו המארח הזה. מסד ששוחזר בלעדיו אינו יכול
לפענח את פרטי הגישה שבו.

### מסד נתונים <!--quire:database-->

| הגדרה | משמעות |
| --- | --- |
| `POSTGRES_PASSWORD` | superuser, לשימוש הקונטיינר והגיבויים |
| `QUIRE_DB_APP_PASSWORD`, `QUIRE_DB_MIGRATOR_PASSWORD`, `QUIRE_DB_REPORT_PASSWORD`, `QUIRE_DB_AUDIT_PASSWORD` | סיסמאות תפקידים, נקבעות בהפעלה הראשונה |
| `DATABASE_URL` | תפקיד היישום. אבטחה ברמת שורה חלה על כל שאילתה שלו |
| `DATABASE_MIGRATOR_URL`,‏ `QUIRE_MIGRATION_URL` | תפקיד הממגר, עבור `migrate` ו-`init` |
| `QUIRE_SUPERUSER_URL` | משמש רק בהרצה הראשונה |
| `QUIRE_REPORT_DATABASE_URL` | תפקיד דוחות לקריאה בלבד, עבור דוחות ובונה דוחות |
| `QUIRE_AUDIT_DATABASE_URL` | תפקיד הביקורת, עבור מסוף הביקורת וייצוא SIEM |
| `QUIRE_DATABASE_ID` | כל UUID שהוא, קבוע לאורך חיי ההתקנה |

סיסמאות תפקיד מוחלות רק בעת יצירת volume מסד הנתונים. לשינוי מאוחר יותר, השתמשו
ב-`ALTER ROLE` ועדכנו את כתובת ה-URL המתאימה.

`QUIRE_REPORT_DATABASE_URL` משמש למסד הפיזי שהוגדר ב-`DATABASE_URL`. עבור כל מסד
פיזי רשום אחר, הגדירו בסביבות web ו-worker כתובת חיבור `quire_report` משלו, ואז הכניסו
את שם משתנה הסביבה בשדה **Reporting environment variable** של אותו מסד, בתור
`env:NAME`. ההפניה צריכה להיות לאותו מסד של חיבור היישום, ועדיף לעותק לקריאה בלבד.
כל ממשק דוחות עוקב אחר ה-tenant לחיבור הדוחות של המסד שלו: בונה דוחות ודוחות
שמורים, שליחות מתוזמנות, ייצוא דוחות, analytics, יומן הביקורת, משאבי הביקורת של
REST וחיפוש הביקורת של העוזר. אף אחד מהם לעולם אינו משתמש בכתובת דוחות של מסד אחר.
כשאין חיבור דוחות למסד מסוים, דוחות רגילים פועלים באמצעות חיבור היישום שלו; analytics
וכל קריאת ביקורת נדחים ומסבירים זאת, מפני שלתפקיד היישום אין גישה לנתיב הביקורת.

### Drivers <!--quire:drivers-->

| הגדרה | בגרסה זו | הערות |
| --- | --- | --- |
| `QUIRE_STORAGE_DRIVER` | `local` (ברירת מחדל), `s3` או `azure` | `local` שומר קבצים ב-volume `files`.‏ `s3` תומך ב-AWS S3,‏ R2, תאימות GCS ומאגרי S3 אחרים, עם העלאות multipart שניתנות לחידוש |
| `QUIRE_REALTIME_DRIVER` | `inprocess` (ברירת מחדל), `sse`,‏ `centrifugo` או `durable_objects` | `inprocess` מתאים לקונטיינר web יחיד; השתמשו ב-`centrifugo` או `sse` כשיש כמה |
| `QUIRE_CACHE_DRIVER` | `memory` (ברירת מחדל), `postgres` או `valkey` | `memory` הוא לכל תהליך; השתמשו ב-`valkey` או `postgres` כדי שמגבלות הקצב יחולו בין קונטיינרים |
| `QUIRE_VIDEO_DRIVER` | `ffmpeg` (ברירת מחדל) או `progressive_mp4` | או ספק מתארח: Cloudflare Stream,‏ Mux או Bunny, באמצעות המפתחות שלהם |
| `QUIRE_IMAGE_DRIVER` | `noop` (ברירת מחדל), `imgproxy` או `cloudflare` | `noop` מגיש כל תמונה בגודלה המקורי. `imgproxy` דורש פרופיל `images` ואת ההגדרות בהמשך; `cloudflare` משתמש ב-Cloudflare Images |
| `QUIRE_MEETING_PROVIDER` | `bbb`,‏ `zoom`,‏ `teams`,‏ `meet`,‏ `jitsi` או `in_process` | ספק ברירת המחדל של הפלטפורמה למפגשים חיים. אם אינו מוגדר, מצוין שמפגשים חיים אינם מוגדרים עד שארגון מחבר חשבון משלו תחת Integrations, Live session provider. חשבון הארגון גובר תמיד על ערך זה. הגדרות ייחודיות לספק (`BBB_URL` ו-`BBB_SECRET`, משתני `ZOOM_*`,‏ `TEAMS_*`,‏ `GOOGLE_MEET_*` ו-`JITSI_*`) נקראות רק עבור הספק שנבחר כאן |
| `QUIRE_MEETING_REGIONS` | רשימת `eu`,‏ `uk`,‏ `us`, מופרדת בפסיקים | האזורים שבהם ספק ברירת המחדל מעבד מפגשים. אם אינו מוגדר, הוא לא נבדק מול ארגון המשויך לאזור — כמו בעבר. חשבון ארגון מצהיר על האזורים בעמוד שלו |

ערך driver שאינו נכלל בגרסה זו נדחה בעת הפעלת שכבת האינטרנט, תוך ציון ההגדרה,
במקום שיוחלף בשקט בערך ברירת המחדל.

### תמונות <!--quire:images-->

עמודים מבקשים תמונות בארבעה גדלים קבועים דרך `/api/files/{id}/image/{size}`.
הנתיב בודק את אותה גישה לקובץ עצמו ואז מפנה לשירות התמונות. כל ארגון רשאי לבקש
`QUIRE_IMAGE_SPECS_PER_HOUR` תמונות וצמדי גודל חדשים בשעה (ברירת מחדל 2000); גדלים
שכבר נוצרו באותה שעה אינם נספרים. השתמשו ב-`valkey` או ב-`postgres` עבור
`QUIRE_CACHE_DRIVER` כשיש יותר מקונטיינר web אחד, כדי שהמכסה תחול על כולם.

| הגדרה | Driver | הערות |
| --- | --- | --- |
| `IMGPROXY_URL` | `imgproxy` | הכתובת שאליה דפדפנים מגיעים, למשל `https://images.example.org`. פרופיל `images` מפרסם אותו ב-`QUIRE_IMAGES_PORT` (8082) |
| `IMGPROXY_KEY`,‏ `IMGPROXY_SALT` | `imgproxy` | מחרוזות hex, זהות לערכים שאיתם imgproxy מופעל. צרו כל אחת באמצעות `openssl rand -hex 32`. Quire חותם באמצעותן על כל כתובת תמונה, כך ש-imgproxy אינו מרנדר דבר ש-Quire לא ביקש |
| `QUIRE_IMAGE_SOURCE_ORIGIN` | `imgproxy` עם אחסון מקומי | המקום שממנו imgproxy מביא קובצי מקור. Compose מגדיר `http://web:3000`. עם אחסון `s3` או `azure` imgproxy מביא מה-bucket וההגדרה אינה משמשת |
| `CLOUDFLARE_ACCOUNT_ID`,‏ `CLOUDFLARE_IMAGES_TOKEN`,‏ `CLOUDFLARE_IMAGES_ACCOUNT_HASH` | `cloudflare` | אסימון API עם הרשאת עריכת Images, וגיבוב החשבון מתוך Images,‏ Developer resources. הפעילו וריאציות גמישות לחשבון |
| `CLOUDFLARE_IMAGES_SIGNING_KEY` | `cloudflare` | אופציונלי. כשהוא מוגדר, התמונות פרטיות וכל כתובת חתומה ופגה. בלעדיו התמונות ציבוריות בכתובות הנגזרות מ-`QUIRE_SECRET_KEY` שאיש אינו יכול לנחש |

Cloudflare Images שומר עותק משלו של כל מקור תמונה שהוא מגיש. כשקובץ נמחק, ה-worker
מוחק תחילה את העותק הזה ורק אחר כך את המקור.

### תור <!--quire:queue-->

עבודות רקע משתמשות ב-pg-boss באותו מסד PostgreSQL, לכן אין צורך להפעיל שירות תור
או להגדיר דבר. עבודות נכנסות לתור באותה טרנזקציה כמו השינוי שגרם להן, ולכן קריסה
אינה יכולה לאבד עבודה או לשלוח אותה פעמיים. כאן `QUIRE_QUEUE_DRIVER` הוא `pgboss`,
ערך ברירת המחדל; `vercel` ו-`cloudflare` מעבירים רק שליחות קלות של התראות ו-webhooks
לתור של הפלטפורמה, ומדריכי Vercel ו-Cloudflare מסבירים עליהם ועל אופן הכנסתם לתור
על ידי שכבות האינטרנט שלהם.

### דוא״ל <!--quire:email-->

הגדירו אחת מהאפשרויות:

- `QUIRE_EMAIL_PROVIDER_CONFIG`: אובייקט JSON שמציין ספק HTTP ואת פרטי הגישה, למשל
  `{"provider":"postmark","token":"..."}`. Postmark,‏ Amazon SES,‏ Mailgun,‏
  SendGrid ו-Resend נתמכים.
- `QUIRE_SMTP_URL`:‏ `smtp://user:password@host:587`. יעד זה בלבד; יעדים ללא שרת
  חוסמים SMTP.

`QUIRE_MAIL_FROM` הוא כתובת השולח. להתנסות ב-Quire הפעילו את פרופיל `devmail`, הגדירו
`QUIRE_SMTP_URL=smtp://mailpit:1025` וקראו דואר ב-`http://localhost:8025`.

### שירותים אופציונליים <!--quire:optional-services-->

| הגדרה | עם פרופיל |
| --- | --- |
| `CLAMAV_URL=tcp://clamav:3310` | `scan` |
| `GOTENBERG_URL=http://gotenberg:3000` | `preview` |
| `IMGPROXY_KEY`,‏ `IMGPROXY_SALT` | `images` |
| `VALKEY_URL=redis://valkey:6379` | `cache` |
| `QUIRE_OPENSEARCH_URL` או `QUIRE_MEILISEARCH_URL` | חיפוש חיצוני; אחרת חיפוש טקסט מלא ב-Postgres |
| `QUIRE_BREACH_CHECK_PROVIDER=off`,‏ `QUIRE_BREACH_CHECK_URL` | בדיקת סיסמה שדלפה. כברירת מחדל פועלת מול `api.pwnedpasswords.com` (נשלחת רק קידומת בת חמש תווים של גיבוב); `off` מכבה אותה, והכתובת מכוונת ל-API טווח שאתם מארחים |

### תצפיתיות <!--quire:observability-->

`OTEL_EXPORTER_OTLP_ENDPOINT` מציין את האוסף שאליו כל תהליך שולח traces ומדדים;
עם פרופיל `observability` הערך הוא `http://otelcol:4318`, ו-
`docker/otel-collector.yaml` הוא המקום שבו מוסיפים exporter למערכת שלכם. שכבת
האינטרנט, worker,‏ scheduler,‏ content ו-collab מייצאים spans דרך OTLP/HTTP
(בקשות אינטרנט, טרנזקציות במסדי tenant, עבודות worker וקריאות יוצאות) אם ההגדרה
קיימת, ומדדים לאותה כתובת בכל דקה (`OTEL_METRICS_EXPORTER=none` מכבה אותם).
`OTEL_TRACES_SAMPLER_ARG` מגדיר את שיעור ה-traces שנשמרים. יומנים נשלחים לפלט
הרגיל ברמה `LOG_LEVEL`, ו-Compose מבצע להם רוטציה. Traces לעולם אינם מכילים מידע אישי.

### תעבורה יוצאת לפי אזור (ריבונות נתוני EU) <!--quire:regional-egress-eu-data-residency-->

`QUIRE_REGION=eu` מציין שה-stack משרת ארגונים מהאיחוד האירופי. ה-worker מגביל כל
בקשה יוצאת עבור ארגון שמשויך ל-EU לרשימת היתרים (21-compliance.md סעיף 8.1).
הרשימה כוללת את המארחים שהשירותים שהוגדרו מציינים עבור האזור (נקודת קצה לאחסון,
ספק דוא״ל, ספק וידאו מתארח, יעדי האחסון של הארגון, ספקי AI וחשבון דוא״ל), את
המארחים של שירות תחת חריגה פעילה ואת המארחים שציינתם ב-`QUIRE_EGRESS_ALLOW_HOSTS`.
בקשה לכל מארח ציבורי אחר נדחית לפני שליחתה, הסירוב נרשם ביומן הביקורת של הארגון
בתור `privacy/egress_refused` והוא מופיע תחת Compliance, Data residency.

| הגדרה | ערכים | השפעה |
| --- | --- | --- |
| `QUIRE_EGRESS_ALLOW_HOSTS` | רשימת שמות מארח מופרדת בפסיקים, או `*.example.org` עבור כל תת-דומיין | מארחים נוספים שארגון באיחוד יכול להגיע אליהם. נקודות קצה של webhook,‏ xAPI ו-SIEM, הזנות בלוגים ומארחי Amazon SES שייכים לכאן, כי אלה בחירות של הארגון ושום שירות אינו מצהיר עליהם. כתובות loopback, כתובות פרטיות ושמות בעלי תווית אחת כמו `web` או `clamav` הם הרשת שלכם ולעולם אינם נבדקים |

ארגוני UK ו-US אינם מוגבלים לרשימת מארחים; בדיקות אזור השירות נשארות בתוקף.
הגדירו את הרשימה ב-worker; עמוד הניהול קורא אותה בשכבת האינטרנט כדי להציג אותה,
לכן שימו אותה ב-`docker/.env`, שכל השירותים קוראים.

בדיקת היישום נותנת הודעת שגיאה ברורה ורשומת ביקורת, אך היא אינה הערובה: הקוד עלול
להיות שגוי. הערובה היא הרשת. Compose אינו אוכף אותה עבורכם. עבור stack אזורי, הציבו
את שירותי `worker` ו-`web` ברשת `internal: true` שהנתיב היחיד שלה החוצה הוא proxy
לתעבורה יוצאת (למשל Squid או קונטיינר tinyproxy) שמתיר את אותם מארחים כמו
`QUIRE_EGRESS_ALLOW_HOSTS` ואת מארחי השירותים שהגדרתם, והגדירו `HTTPS_PROXY` לשירותים
הללו. עמוד ריבונות הנתונים מפרט את המארחים שהיישום מתיר, כך שאפשר להשוות בין שתי
הרשימות.

## תקינות <!--quire:health-->

| נקודת קצה | משמעות |
| --- | --- |
| `/healthz` | חיוניות: התהליך מגיב. בדיקות התקינות של Compose משתמשות בה |
| `/readyz` | מוכנות: אפשר להגיע לתלויות, וכל שירות אופציונלי מדווח אם הוגדר. כוונו אליה את מאזן העומסים |

`docker compose -f docker/compose.yaml ps` מציג את מצב הבריאות של כל שירות.

## TLS <!--quire:tls-->

שירות `proxy` (Caddy,‏ Apache-2.0,‏ `docker/caddy/Caddyfile`) הוא חלק מה-stack
ברירת המחדל. הוא מאזין בפורטים 80 ו-443 ומנתב:

| מארח או נתיב | מועבר אל |
| --- | --- |
| `QUIRE_PROXY_CONTENT_HOST` | `content` |
| `QUIRE_PROXY_APP_HOST`, כל תת-דומיין tenant ודומיין מותאם אישית | `web` |
| `/_collab/` במארחים האלה | `collab`‏ (websocket,‏ `QUIRE_COLLAB_URL`) |
| `/_realtime/connection/` במארחים האלה | websocket לקוח של `centrifugo`; ה-API שלו לשרת אינו חשוף |
| `/_images/` במארחים האלה | `imgproxy`, עם פרופיל `images` (`IMGPROXY_URL`) |

`init-env.sh` מפיק את `QUIRE_PROXY_APP_HOST`,‏ `QUIRE_PROXY_CONTENT_HOST`,‏
`QUIRE_PROXY_HTTPS_PORT`,‏ `QUIRE_COLLAB_URL` ו-`IMGPROXY_URL` משני המקורות, כדי
שיישארו מסונכרנים. ערכו אותם יחד אם משנים מקור ידנית.

האישורים נקבעים לפי `QUIRE_PROXY_TLS`:

- `internal` (ברירת המחדל): רשות האישורים של Caddy עצמו, עבור `localhost`,‏
  `*.localhost` ו-`lvh.me`. בטחו פעם אחת באישור השורש ואז גלשו:

  ```sh
  docker compose -f docker/compose.yaml cp \
    proxy:/data/caddy/pki/authorities/local/root.crt ./quire-local-ca.crt
  ```

  הוסיפו את `quire-local-ca.crt` למאגר האישורים של המערכת או הדפדפן. ב-`curl`
  מעבירים אותו באמצעות `--cacert`.
- כתובת דוא״ל: אישורי ACME אוטומטיים (Let's Encrypt ואז ZeroSSL) לשמות מארח אמיתיים.
  DNS של שני המקורות ושל כל מארחי ה-tenants חייב להפנות לכאן, והאינטרנט חייב להגיע
  לפורטים 80 ו-443.

אישורי מארחי tenant מונפקים לפי דרישה, בביקור הראשון, ורק לאחר שה-web מאשר שהשם
שייך להתקנה הזאת (`/tls-allowed`, בבקשה ברשת Compose). אין צורך באישור wildcard או
בתוסף לספק DNS, ואדם זר שמפנה שם אל המארח אינו יכול לגרום לבקשת אישור. האישורים
ורשות האישורים המקומית נמצאים ב-volume‏ `caddy-data`; אם אתם משתמשים ב-`internal`,
גבו אותו יחד עם השאר.

ה-web מאמין לערך `X-Forwarded-For` מה-proxy בלבד: ל-proxy כתובת קבועה
(`QUIRE_PROXY_ADDRESS`, ברירת מחדל `172.29.64.10`) ברשת משנה קבועה
(`QUIRE_COMPOSE_SUBNET`), ו-`QUIRE_TRUSTED_PROXY_CIDRS` מציין כתובת זו. אם רשת המשנה
מתנגשת עם רשת אחרת במארח, שנו את שתיהן והפעילו `docker compose down` לפני `up`.

## מאחורי reverse proxy משלכם <!--quire:behind-your-own-reverse-proxy-->

כדי להשתמש במאזן עומסים או ב-proxy שכבר פועל אצלכם, השאירו את `proxy` מחוץ להפעלה
(`docker compose up -d --scale proxy=0`) וסיימו TLS לפני `web` (8080),‏ `content`
(8081),‏ `collab` (1234, websocket) ו-`centrifugo` (8000, websocket). הגדירו את
הכתובות הציבוריות ב-`QUIRE_APP_ORIGIN`,‏ `QUIRE_CONTENT_ORIGIN` וב-`QUIRE_COLLAB_URL`
(`wss://`), ואת טווח כתובות ה-proxy שלכם ב-`QUIRE_TRUSTED_PROXY_CIDRS`.

## פתרון בעיות <!--quire:troubleshooting-->

- `init` יוצא עם "QUIRE_DATABASE_ID is not a UUID": הגדירו אותו באמצעות `uuidgen`.
- `web` מופעל מחדש עם "did not start on compose": היומן מפרט כל הגדרה שאי אפשר
  לכבד ומה לשים במקומה.
- שינוי סיסמת תפקיד ב-`.env` לאחר ההפעלה הראשונה אינו משפיע: סקריפט האתחול רץ פעם
  אחת בלבד. השתמשו ב-`ALTER ROLE`.
- העלאות נכשלות עם שגיאת סריקה כש-`CLAMAV_URL` מוגדר: ClamAV מוריד חתימות בהפעלה
  הראשונה וזה נמשך כמה דקות.

Source: https://docs.quirelms.com/he/ops/install/index.mdx
