איך אפליקציה מקבלת אוטומציות, חיבורים, התראות, כניסה ומסד נתונים בלי לכתוב אותם, איך תלויות מנוהלות היום, ומה קורה כשמכבים רכיב. הדף הזה נגזר מהמניפסטים והקומפיילר האמיתיים, לא מתיאור.
לאפליקציה יש תיקייה אחת וקובץ מניפסט אחד (app.json). זה הדבר היחיד שהפלטפורמה קוראת עליה. אין רישום מרכזי, אין קובץ בסיס שצריך ללמוד את השם שלה, ושער G9 ב-CI נכשל אם קובץ בשכבה נמוכה מזכיר אפליקציה בשכבה גבוהה. להתקין = להוסיף תיקייה. להסיר = למחוק אותה, וכל מה שנגזר ממנה (ניתוב, SSO, אריח, סודות, אוטומציות) נעלם ברינדור הבא.
הקומפיילר (platform/manager/reconcile.py) קורא את כל המניפסטים ואת platform.state.yaml (מה מופעל, ואילו ספקים נבחרו), פותר את גרף היכולות, ומייצר את כל ההרצה לתוך .generated/: שרשרת ה-compose, תוויות Traefik, תבנית Authentik, רישום הסודות, אריחי הקונסולה. Docker מתכנס על התוצאה. שום דבר לא מחווט ביד.
{
"id": "indox", // == שם התיקייה, נאכף
"title": {"he": "מסמכים נכנסים", "en": "INDOX"},
"layer": 3, "kind": "service", "version": "2.3.0",
"capabilities": [ // מה אני מציע לאחרים
{ "name": "indox-api", "required_ops": ["documents", "sync-state", "settings", "tests"] }
],
"use": [ // מה אני צריך
{ "capability": "workflow", "strength": "requires" },
{ "capability": "ocr", "strength": "requires", "ops": ["documentOcr", "documentJsonQuery"] },
{ "capability": "drive", "strength": "requires", "ops": ["list", "download", "upload", "move"] },
{ "capability": "chat", "strength": "wants", "after": false, "dependency": "weak" },
{ "capability": "notify", "strength": "wants", "after": false, "dependency": "weak" }
],
"routing": [{ "host": "incoming-documents", "port": 8080, "auth": "sso",
"module": { "group": "עסק", "icon": "inbox", "embed": true, "alias": "indox" } }],
"connections": [{ "id": "gemini-key", "required": true }, { "id": "gmail-app-password", "required": true }],
"blueprints": { "n8n": "blueprint/n8n", "requires_stdlib": ">=1.0" }, // אוטומציות של האפליקציה
"data": { "volumes": ["indox_data"], "on_uninstall": "keep" },
"progress_deadline_seconds": 600
}
| שדה | מה הוא מצהיר | מה הקומפיילר מייצר ממנו |
|---|---|---|
capabilities | חוזה: שם יכולת ורשימת הפעולות שהאפליקציה משרתת (required_ops) | רישום ספקים; צרכן שמבקש פעולה שאין לספק נכשל בתכנון, לא בייצור |
use | יכולות שהאפליקציה צריכה, עם חוזק (למטה), פעולות נדרשות, וסדר הפעלה | משיכת ספקים לגרף, בחירת ספק, סדר עלייה, זיהוי מעגלים |
routing | שם מארח, פורט, האם מאחורי SSO, נתיבים פטורים, ואריח בקונסולה | כל תוויות Traefik (3 נתבים: localhost, HTTP, HTTPS), ספק proxy ב-Authentik, האריח |
connections | אילו סודות האפליקציה צורכת, ואילו היא מוסיפה לקטלוג (contributes) | רישום "מי משתמש במה" למישור החיבורים, אשף ההתקנה שואל רק על מה שחסר, מדיניות מתווך ההרשאות |
blueprints.n8n | תהליכי אוטומציה שהאפליקציה מביאה איתה | ייבוא בהפעלה, הסרה בכיבוי. שער G13: חייבים גם use: workflow |
data | נפחים, ומה קורה להם בהסרה (keep/purge) | מסך הגילוי לפני התקנה; אזהרה אדומה על purge |
progress_deadline_seconds | כמה זמן מותר להיות לא-מוכן | תנאי ProgressDeadlineExceeded במקום "עולה" לנצח |
broker | האפליקציה לא מחזיקה סודות בסביבה | המשתנים מרוקנים; האפליקציה שוכרת מפתח חד-פעמי ממתווך ההרשאות בזמן ריצה |
| חוזק | משמעות | מה קורה כשהספק לא מופעל |
|---|---|---|
requires | קשיח. הספק נמשך לגרף ומופעל לפני האפליקציה. | הקומפיילר מוסיף אותו אוטומטית ("pulled in as provider"). אם אין ספק בקטלוג בכלל: שגיאת תכנון, שום קונטיינר לא עולה. |
requisite | קשיח, וחייב כבר להיות שם. לא נמשך. | שגיאת תכנון. |
binds_to | קשיח, למופע ספציפי אחד. | שגיאת תכנון. |
wants | רך. משתמשים בו אם קיים. לעולם לא מושך כלום. | האפליקציה עולה בלעדיו. באחריות הקוד שלה לעבוד בלי היכולת. |
conflicts | שתיהן לא יכולות להיות מופעלות יחד. | שגיאה שמציינת את שתיהן. אף פעם לא מפילים אחת בשקט. |
שני מתגים על סדר, לא על נוכחות: "after": false (צריך את זה, לא צריך שיעלה קודם) ו-"dependency": "weak" (מוריד את קשת הסדר; כך שוברים מעגל). עמימות לא מנוחשת: שני ספקים לאותה יכולת בלי כלל בחירה מפילים את התכנון ומציינים את שניהם. הכלל: bindings ב-platform.state.yaml (בחירת המפעיל, גוברת) → default_provider במניפסט של הצרכן → הספק היחיד.
שורת routing אחת = שלושה נתבים ב-Traefik, תעודת Let's Encrypt, ו-<host>.localhost לפיתוח.
auth: "sso" = Authentik forward-auth. האפליקציה קוראת את המשתמש מכותרות X-authentik-*. בלי קוד אימות.
module = אריח ב"האפליקציות שלי", עם embed: true המסך משובץ בתוך הניווט.
connections[] = הסודות מגיעים מקובץ ההגדרות או מהאשף; עם broker אין סודות עומדים בסביבה בכלל.
blueprints.n8n = תהליכי n8n שמיובאים ומופעלים עם האפליקציה ונמחקים איתה. קונבנציה: תהליך ראוטר אחד שחושף את הפעולות כ-{op, params}.
use: notify ו-error-tracking (בדרך כלל wants) = מרכז ההתראות ו-GlitchTip. אם הם כבויים, האפליקציה ממשיכה.
Resolved / Installed / Started / Ready עם סיבה, הודעה וזמן מעבר. AuthFailed נקרא ממישור החיבורים, לא מנוחש מבריאות הקונטיינר.
התקנה לפי מזהה, כתובת או קובץ, עם חתימת Ed25519, וגילוי לפני התקנה: מפתחות, פורטים, נתיבים, נפחים, עלות.
מתוך המניפסטים של 27 אפליקציות, כפי שהיו כשהטבלה נוצרה. במאגר הציבורי יש היום יותר מ-40. חובה = requires: הספק נמשך ומופעל קודם. רשות = wants: משתמשים אם קיים. ליד כל יכולת: מי מספק אותה.
| אפליקציה | מספקת | חובה | רשות | ניתוב · כניסה | אוטומציות | סודות |
|---|---|---|---|---|---|---|
| app-management ניהול אפליקציות · שכבה 1 | ניהול אפליקציות | — | — | apps: SSO | — | 0 |
| authentik משתמשים וכניסה · שכבה 1 | זהות (identity) שער כניסה (forward-auth) | בסיס נתונים (sql) ← postgres | — | auth: פתוח | — | 4 |
| connections חיבורים ומפתחות · שכבה 1 | סודות (secrets) מופעי חיבור | — | — | connections: SSO | — | 0 |
| postgres בסיס נתונים · שכבה 1 | בסיס נתונים (sql) | — | — | בלי ניתוב | — | 2 |
| redis מטמון ותורים · שכבה 1 | מטמון ותורים (kv) | — | — | בלי ניתוב | — | 1 |
| traefik שער הכניסה · שכבה 1 | נתב קצה | — | זהות (identity) ← authentik | traefik: SSO | — | 1 |
| any-concurrency כל מקביל · שכבה 2 | מדדי מקביליות | בסיס נתונים (sql) ← postgres מטמון ותורים (kv) ← redis | — | any-concurrency: SSO | — | 2 |
| console המרכז · שכבה 2 | מעטפת (shell) | סודות (secrets) ← connections זהות (identity) ← authentik | פנקס (ledger) ← apitools התראות (notify) ← notifications ספריית ממשק ← bina-ui | console: SSO | — | 0 |
| glitchtip מעקב שגיאות · שכבה 2 | מעקב שגיאות | בסיס נתונים (sql) ← postgres מטמון ותורים (kv) ← redis | — | glitchtip: פתוח | — | 4 |
| machine מכונה · שכבה 2 | מדדי מארח | — | — | machine: SSO | — | 0 |
| n8n אוטומציות · שכבה 2 | אוטומציות (workflow) ספריית אוטומציות | בסיס נתונים (sql) ← postgres מטמון ותורים (kv) ← redis סודות (secrets) ← connections | מעקב שגיאות ← glitchtip | n8n: פתוח | ✓ | 7 |
| notifications התראות · שכבה 2 | התראות (notify) | — | מעקב שגיאות ← glitchtip | alerts-svc: SSO | — | 1 |
| apitools כלי מסמכים · שכבה 3 | OCR פנקס (ledger) | בסיס נתונים (sql) ← postgres סודות (secrets) ← connections | — | apitools: פתוח | — | 6 |
| browser42 דפדפן · שכבה 3 | דפדפן | — | — | browser42: SSO | — | 2 |
| chat צ׳אט · שכבה 3 | צ'אט | אוטומציות (workflow) ← n8n סודות (secrets) ← connections | התראות (notify) ← notifications מסמכים נכנסים ← indox | chat: SSO | ✓ | 4 |
| drive דרייב · שכבה 3 | דרייב | אוטומציות (workflow) ← n8n בסיס נתונים (sql) ← postgres סודות (secrets) ← connections אחסון אובייקטים ← supabase | — | drive: SSO | ✓ | 2 · broker |
| google-workspace-agent סוכן Google Workspace · שכבה 3 | — | אוטומציות (workflow) ← n8n סודות (secrets) ← connections | — | בלי ניתוב | ✓ | 0 |
hello-binaשלום בינה · שכבה 3 | — | — | — | hello-bina: SSO | — | 0 |
| indox מסמכים נכנסים · שכבה 3 | מסמכים נכנסים | אוטומציות (workflow) ← n8n בסיס נתונים (sql) ← postgres סודות (secrets) ← connections מטמון ותורים (kv) ← redis REST לנתונים (data-api) ← supabase OCR ← apitools דרייב ← drive מייל ← mail | צ'אט ← chat התראות (notify) ← notifications | incoming-documents: SSO | ✓ | 6 |
| mail מייל · שכבה 3 | מייל שרת מייל מקומי | אוטומציות (workflow) ← n8n סודות (secrets) ← connections | — | mailapi: SSO mail: SSO | ✓ | 2 |
| my-docs מסמכים שלי · שכבה 3 | — | בסיס נתונים (sql) ← postgres זהות (identity) ← authentik פנקס (ledger) ← apitools | ספריית ממשק ← bina-ui | my-docs: SSO | — | 1 |
| ollama מודלים מקומיים · שכבה 3 | מודל שפה מקומי | — | — | ollama: SSO | — | 0 |
| openhands סוכן קוד · שכבה 3 | סוכן קוד | סודות (secrets) ← connections | מודל שפה מקומי ← ollama | openhands: SSO | — | 2 |
| portainer מכולות · שכבה 3 | — | — | — | portainer: פתוח | — | 1 |
| redisinsight מפקח Redis · שכבה 3 | — | מטמון ותורים (kv) ← redis | — | redisinsight: SSO | — | 1 |
| supabase סופאבייס · שכבה 3 | אחסון אובייקטים REST לנתונים (data-api) | בסיס נתונים (sql) ← postgres | — | supabase: פתוח supabase-studio: SSO | — | 4 |
system: הקונסולה לא מציעה לכבות אותם. זו ההגדרה הנוכחית, לא חוק טבע.requires: הקומפיילר מושך אותו חזרה (הוא נדרש), או, אם הוצא מהקטלוג, מפיל את התכנון בשגיאה שמציינת מי צריך מה. אף אפליקציה לא עולה חצי.wants: הצרכנים עולים בלעדיו. למשל: כיבוי GlitchTip משאיר את n8n, ההתראות והצ'אט עובדים; כיבוי הצ'אט משאיר את מסמכים נכנסים עובד בלי ערוץ וואטסאפ.auth: sso מחזיר 502 שמציין את שירות האימות. זה מכוון: ה-middleware מוגדר בקובץ ולא בתוויות, כדי ש-Traefik לא יזרוק את הנתבים ויחזיר 404 שנראה כמו "האפליקציה לא מותקנת" (נמדד: 3,718 דחיות נתבים בחצי שעה לפני התיקון). נתיבים עם auth: none (apitools, supabase API, glitchtip, n8n webhooks) ממשיכים לענות.הכוונה המוצהרת של הפרויקט: כל רכיב שאינו חיוני לאפליקציה הוא wants, ואם הוא כבוי האפליקציה עובדת בלעדיו. שער הכניסה הוא שער, לא תלות. שלושה שינויים סוגרים את הפער:
identity הוא ספק system. היעד: כש-authentik כבוי, הקומפיילר מרנדר את נתיבי ה-sso בלי ה-middleware ומדפיס אזהרה אחת גדולה ("כל המערכת פתוחה"), במקום 502. אפליקציה שמצהירה identity: requires (היום: המרכז ומסמכים שלי, כי הן קוראות את המשתמש מהכותרות) תפיל את התכנון בשגיאה ברורה, כי בשבילן זה לא שער אלא תלות. כל השאר עולות פתוחות. זה שינוי בקומפיילר בלבד; המניפסטים כבר אומרים מי צריך זהות באמת.wants. הסנכרון בודק אילו מקורות זמינים ומדלג על מה שכבוי, כמו שהוא כבר עושה לצ'אט. n8n ו-OCR נשארים requires, כי בלעדיהם אין צנרת.requires_stdlib: ">=1.0" כבר קיים במניפסט של מסמכים נכנסים; היעד הוא שגרסת יכולת תיאכף בתכנון (capabilities[].version, צרכן ^1.3), כך ששבירת חוזה בין ספק לצרכן תיתפס בקומפילציה ולא בזמן ריצה.| שער | מה הוא מבטיח |
|---|---|
| G5 ownership | שום קובץ לא חי מחוץ לאפליקציה שבבעלותו |
| G6 auth | נתב פתוח לא חולק כלל ניתוב עם נתב מוגן |
| G7 compose | אף אחד לא מריץ docker compose ישירות (זה no-op שקט) |
| G9 layering | שכבה נמוכה לא מזכירה אפליקציה משכבה גבוהה |
| G13 blueprints | אפליקציה עם תהליכי n8n מצהירה use: workflow |
| G15 image pins | תמונות צד שלישי מוצמדות לגרסה |
| G16 conditions | תנאי הבריאות מדווחים אמת, לא על עצמם |
| G17 resolver | הקומפיילר לא מנחש ספק |
| G18 healthcheck | בדיקת הבריאות משתמשת בבינארי שקיים בתמונה |
| G19 sub-workflows | קריאה לתת-תהליך לא מעבירה null |
| G20 sso providers | ספקי SSO נגזרים מהמניפסטים, לא כתובים ביד |
| G21 route claims | שתי אפליקציות לא תופסות אותו מארח |
ואחרי כולם: scripts/fresh-machine-check.sh, שמשכפל למכונה ריקה בלי .env ובלי נפחים ומוכיח שהכל מתקמפל מאפס. "עובד אצלי" לא נחשב.