TgWitness / תיעוד

מה נבדק ←

הפניה / ארכיון ושילובים

Telegram · הפניה ל-Case Board

סימניות, הדגשות מדויקות, קטעי פעולה, דיונים, גישה מוגבלת והיסטוריית ההערות החתומות שבהן משתמש Case Board.

TgWitness / תיעוד · חקירה

חקירה

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

מודל הראיות · גישה · דוחות

עדכון חקירה סינתטי

בעמוד זה

עבודה עם תיק#

פתחו את Investigation → Case Board במצב Standard או Expert. השפה תותאם ללוח. השתמשו ב-To board לצד רשומת יומן או הודעת צ'אט שנשמרה, או סמנו אותה ולחצו על B. הסימנייה מתווספת לתיבת הדואר הנכנס בלחיצה אחת. ערכו את הכותרת, התוויות וההערות שלה, או העבירו אותה לאוסף. כרטיס ששונה מצרף גרסה חתומה; הסרתו משמרת את ההיסטוריה.

במקרה הראשון, בחרו באיסוף והוסיפו את הרשומות העונות על שאלה אחת. השתמשו בהערות כדי לבדוק את הפרשנות, ולאחר מכן ב"מצגת" כדי לעבור על הכרטיסים והקשר המקור. בחרו את החומר ל"לוח המקרה" בנפרד ב-Report Studio בעת הכנת התוצר הסופי. שמירת קטע אינה מגדירה אוטומטית את מסנני הנתונים של הדוח.

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

לחצו על Shift ולחצו על שני רשומות בצ'אט אחד כדי לבצע "Slice between", השתמשו ב-"Slice by time", או שמרו את המסננים הנוכחיים של ה-Journal: צ'אט, תקופה וסוג אירוע. ל-"Slice" שנשמר יש תנאי סף סמכותי משלו: פתיחתו ב-Journal מחילה גם צ'אטים מרובים, מגבלות על מספר ההודעות ומסנני משתתפים. הצגת רשומות שנוספו מאז הסימניה מבודדת את התוספות המאוחרות יותר. שינוי מסנני ה-Journal מביא ליציאה מה-Slice. מתוך People, האפשרות "Slice this person’s actions" משתמשת במזהה השחקן שהוקלט; היא אינה מניחה שכותב ההודעה ערך, מחק או הגיב להודעה.

תיבת הדו-שיח "slice" מציגה תצוגה מקדימה של הספירה לפני שהיא מאפשרת יצירה. המונח "all" כולל כל סוג של תיעוד, לרבות תמונות מצב, עריכות, מחיקות, תגובות, עדכוני חברות/שירות, פעולות אישיות, כפתורים, תוצרי מדיה, הודעות ששוחזרו וסוגי אירועים עתידיים. הבחירה מתארת תצפיות שנשמרו. הודעות על מחיקת צ'אטים לא מזוהים הן הקשר נפרד, הזמין רק לבעלי גישה לכל סביבת העבודה, ואינו מיוחס לעולם לאף אחד מהצ'אטים שנבחרו.

לאוספים יש היקף צ'אט מוגדר. היקף ריק פירושו כל סביבת העבודה. אוסף אינו יכול להכיל כרטיסים מחוץ להיקפו; משתתף המוגבל לצ'אט מסוים אינו יכול לקרוא אוסף רחב יותר. תיבת הדואר הנכנס היא אוסף וירטואלי לכרטיסים שלא הוקצו. החיפוש והמסננים חלים על כרטיסים גלויים בלבד; שרשורים ואזכורים יורשים את הגישה של הכרטיס ההורי. לתגובות שהוסרו יש "אבן זיכרון" בדיון. לא נרשם היסטוריית צפייה.

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

הרשאות#

כל הפעולות דורשות גישה ל-view, להיקף סביבת העבודה/הצ'אט ול-board.read. board.write מוסיף כרטיסים, הערות, העברות ומצבי "פתוח"/"בבדיקה". board.manage מאפשר ניהול האוסף, מצבי "אושר"/"נדחה", תיקונים/הסרה של כרטיס של אדם אחר. מצב הוא החלטה של הצוות, ולא תוצאת אימות קריפטוגרפי.

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

הרשאה לצ'אט A אינה יכולה להשיג כרטיס או פרוסה עבור A+B, אפילו על ידי ניחוש ה-ID שלה או מסירת target.chat_id כוזב. מטא-נתוני היעד נקבעים מה-hash של האירוע החתום בשרת. אוספים, ספירת דיונים, כרטיסים קשורים והודעות בזמן אמת מסוננים לפני התגובה. ייצוא דורש את הפקודה `export` וכן את `board.read`; ייצואן קיים ללא גישה ללוח מקבל את ארכיון הראיות הרגיל ללא הערות הצוות. SSE בודק מחדש את ההרשאות הנוכחיות לפני כל אצווה.

חוזים מאוחסנים#

כמו תצפיות, ההערות נכתבות באופן טרנזקציוני לטבלת SQLite מסוג "הוספה בלבד" (append-only) בסביבת העבודה. הקובץ annotations.jsonl הוא הייצוא הקנוני המדויק שלה מאותה תמונת מצב לקריאה כמו events.jsonl. אין מאגר הערות מתחרה שני ואין אירוע יומן מסוג board.activity.

כל שורה מכילה בדיוק:

{
  "seq": 1,
  "at": "2026-11-02T14:00:00.000+00:00",
  "access_id": "admin",
  "access_label": "Main administrator",
  "kind": "board.bookmark",
  "payload": {"id": "32 lowercase hex digits", "target": {"event_hash": "64 lowercase hex digits"}},
  "previous_hash": "64 zeroes for the first record",
  "hash": "SHA-256 described below",
  "signature": "base64 Ed25519 signature"
}

הדוגמה המקוצרת שלעיל ממחישה את המעטפת; נתונים בפועל כוללים את הגדרות ברירת המחדל של הדגם ושדות היעד המוגדרים על ידי השרת. סכמות בקשות ה-API נמצאות תחת tgwitness/schemas, ונוצרו מהמודלים של Pydantic בזמן ריצה על ידי python tools/build_board_schemas.py. שדות בקשה נוספים נדחים. מזהים נוצרים בצד השרת כאשר הם מושמטים. זהות המחבר, הזמן, תמונת המצב והחתימה אינם ניתנים לציון על ידי המתקשר. מזהי ההרשאות המכונים עוקבים אחר הפורמט הקיים של Access, 16 תווים הקסדצימליים; מזהי הכרטיסים הם 32 תווים הקסדצימליים.

JSON הקנוני הוא ב-UTF-8, מפתחות אובייקטים ממוינים, ללא רווחים בין טוקנים, Unicode ללא בריחה, וללא NaN/אינסוף. ה-hash של ההערות הוא SHA-256 של האובייקט הקנוני המכיל את ה- format: "tgwitness-annotations-v1" ואת כל שבעת שדות המעטפת עד ל-previous_hash. תגית הפורמט הזו מפרידה את תחום ה-hash שלה מהתצפיות. החתימה היא Ed25519 על 32 בתים גולמיים של ההאש. הרצף מתחיל ב-1. גרסאות משמרות את מזהה הכרטיס ומציינות את ההאש של ההערה הקודמת בשדה "supersedes"; הן אינן יכולות לשנות את היקף הצ'אט. העברות, סטטוסים והסרות הם רשומות נפרדות. טבלת SQLite דוחה פקודות UPDATE ו-DELETE.

case-board.json מכיל {statement, signature}. ההצהרה הקנונית חתומה על ידי מפתח סביבת העבודה ומכילה format: "tgwitness-case-board-v1", public_key, created_at, journal_head, meaning ו-state. ה-state הוא השמעה דטרמיניסטית: annotations_head, מערך items מסודר (אוספים, כרטיסים, הערות כולל "אבני זיכרון") ו-status_history. הפריטים שומרים על ה-hash של ההערה האחרונה, הרצף, המחבר והזמן, כמו גם על היוצר המקורי וזמן היצירה לאורך כל הגרסאות. השחזור בודק גרסאות, הורים, הפניות לאוספים ומזהי פעולות כפולים. גם הקובץ `manifest.json` ונקודות הבדיקה החדשות מקשרים את `annotations_head`. יש לשמור על נקודת בדיקה באופן עצמאי כדי לזהות קיצוץ או כתיבה מחדש בעזרת מפתח חתימה מועתק.

גרסאות טקסט#

range.start הוא קיזוז נקודת קוד Unicode המתחיל מאפס, length הוא ספירת נקודות קוד חיובית, ו-quote חייב להיות שווה למחרוזת המשנה המדויקת של payload.text או payload.caption באירוע היעד. לא מוחלים NFC/NFKC, המרת רישיות או נורמליזציה של גרפמות. חתכי מחרוזות ב-Python ו- Array.from(text) ב-JavaScript מיישמים את אותה אינדקסציה. שילוב של סימנים ואמוג'י ZWJ עשוי לתפוס מספר נקודות קוד. range.version הוא ה-hash של האירוע היעד, ולא גרסת ההודעה הנוכחית או קיזוז הישות ב-UTF-16 של Telegram. עריכות מאוחרות לעולם אינן מעבירות את ההדגשה באופן אוטומטי.

זהות ופרק זמן#

query מקבל בדיוק chat_id אחד או chat_ids שאינם ריקים, טווח אופציונלי כולל from/to, גבולות כוללניים של מזהי הודעות (צ'אט אחד בלבד), kinds: "all" או סוגים מדויקים/סוגי תווים כלליים עם קידומת, actors מפורשים, search, hide_system ו-include_unscoped. קבוצות סוגי יומנים מתרחבות לפרדיקטים המדויקים שלהן. החיפוש משתמש בחיפוש המטען של היומן לפי NFC/case-folded מבלי לשנות את טקסט המקור החתום. תאריכים דורשים אזור זמן ומנורמלים ל-UTC. תאריכים מסננים את `observed_at`. `source_at` של טלגרם מוצג בנפרד. מחיקה ללא חותמת זמן מקורית אינה כוללת זמן מחיקה מומצא. רשומה מאוחרת שנצפתה מחוץ לטווח זמן אינה נכנסת אליו; פרוסת `message-ID` עשויה לכלול מחיקה מאוחרת יותר עבור `message-ID`-ים אלה.

snapshot קובע את זמן היצירה, את גבול הרצף/ה-hash של היומן, את מספר ההתאמות, את הרצף הראשון/האחרון ואת result_hash. האחרון הוא SHA-256 של מערך ה-JSON הקנוני ב-UTF-8 של ה-hashes של האירועים התואמים, ממוינים לפי סדר אלפביתי, כולל [] עבור פרוסה ריקה. הוא מכיל גם ספירות מדויקות של סוגי אירועים ומיניאטורה של זמן התצפית המורכבת מ-24 תאים. פרוסה מאחסנת שאילתה, ולא רשומות מקור כפולות . בעת הייצוא, board/slices/<id>.json מפרט את ההאשים הנוכחיים ואת הרצפים שנוספו/הוסרו. המאמת משחזר את התוצאה המקורית בגבול היצירה, ולאחר מכן את התוצאה הנוכחית. רשומות שנוספו מדווחות כעובדת שינוי. הסרת אירוע מקורי מפרה את שרשרת הראיות הבלתי ניתנת לשינוי; היא אינה מתקבלת בשתיקה כהסרה רגילה מפרוסה.

תמונות מצב של תגובות כוללות רשימות לא שלמות של משתתפים אחרונים. ה-Explorer משווה תמונות מצב סמוכות של אותו צ'אט/הודעה, כולל קודמת מחוץ לפרוסה, ומדווח על הפרשי ספירה מצטברים בין תצפיות. לא נטען כי משתתפים אחרונים חסרים הסירו תגובה. לתמונת המצב הראשונה אין מצב קודם שהוסק. תצפיות השחקנים נותרות מיוחסות ל תמונת המצב שבה סופקו, ולא לזמן פעולה שהומצא.

API וייצוא#

GET /api/board מקבל את הפרמטרים collection/chat/author/label/state/from/to/q/kind/comments, offset/limit וסדר newest/oldest. GET /api/board/card/{id} מחזיר כרטיס, תצוגה מקדימה של המקור, שרשור, היסטוריית סטטוס וכרטיסים קשורים. תת-המסלול participants מחזיר אפשרויות אזכור מתאימות. POST /api/board/{bookmark|highlight|slice|collection|comment|status|move|remove} מוסיף רשומה. POST /api/board/slice-preview מחשב שאילתה. רשומות החתך מחולקות לדפים ב- GET /api/board/slice/{id}/records; חישוב מחדש משתמש תמיד בתמונת מצב הקריאה היציבה הנוכחית. /api/stream בשידור חי משדר התראות לוח (board) ללא תוכן, בנפרד מסמני היומן.

GET /api/board/export מחזיר מסמך חתום. גישה מוגבלת לצ'אט מקבלת גילוי חתום המסומן בבירור, לעולם לא הערות מצ'אט אחר או את השרשרת המלאה. קבצי ZIP של ראיות מלאות מכילים את annotations.jsonl, case-board.json חתום ונספחי JSON של פרוסות. הגדרות הדוח ב-HTML/PDF/JSON מאפשרות לבחור באפשרות "ללא לוח", סימניות/חתכים ללא דיון, או כרטיסים עם הערות. הסעיף מפריד במפורש בין דעות הצוות לתצפיות. מסנני נתוני הדוח מגבילים גם את ההערות הניתנות לקריאה; קבצי המקור החתומים במלואם בקובץ ה-ZIP של הראיות נשארים שלמים.

GET /api/board/slice/{id}/export מייצר קטע JSON חתום מסוג tgwitness-board-selection-v1; ?format=csv מייצר טבלה נוחה ללא חתימה נפרדת. יש לשמור על ה-JSON וה-ZIP:

python -m tgwitness.verify evidence.zip --fingerprint TRUSTED_KEY_SHA256 --board
python -m tgwitness.verify board-slice.json --evidence evidence.zip --fingerprint TRUSTED_KEY_SHA256
python -m tgwitness.verify case-board.json --evidence evidence.zip --fingerprint TRUSTED_KEY_SHA256

בדיקת הבחירה מאמתת את ההצהרה החתומה, את רשומות המקור המדויקות ואת השלמות של השאילתה בגבול שצוין. קובץ ה-ZIP של הראיות עשוי להיות חדש יותר, אך עליו להכיל את הקידומת הזו. יעד תלוי מדווח במפורש. תמונת מצב מקורית שונה, הערה ששונתה, מצב השמעה שאינו תואם או מקור ששונה יגרמו לכישלון. ארכיונים ישנים ללא הערות שומרים על התנהגות האימות הקיימת שלהם.

ההעברה מוגדרת כברירת מחדל ל"אל תכלול". הכללה מפורשת מציגה כרטיסים דרך הרשומות שכבר צומצמו, משמיטה יעדים שאינם נגישים או שהושמטו, מסתירה הדגשות שטקסט המקור שלהן צומצם, מנקה טקסט צוותי לפי אותם כללי אזכור/קשר, ובאופן אופציונלי מחליפה תוויות חוקר ב-R01, R02… במטא-נתונים ובהופעות ידועות בהערות. לא נחשף קובץ annotations.jsonl המקורי. קובץ ה-sidecar החתום tgwitness-case-board-handover-v1והדוח מכילים את אותו החומר המצומצם. ספירת הפרוסות מתארת רק רשומות שנחשפו, כאשר פרטי השחקן המקוריים בתמונת מצב/שאילתה נשמרים בסוד. שמות בטקסט חופשי שאינם ידועים למצמצם עדיין דורשים בדיקה, כפי שקורה בהעברות רגילות. נגזרת זו אינה טוענת לשמירת חתימות ההערות המקוריות לאחר העריכה.

מגבלות תפעול#

הפרמטרים כוללים מגבלות רכות של 10,000 כרטיסים ו-50,000 הערות, דף חתך של עד 5,000 רשומות, ובקרת תצוגה מקדימה של מדיה. מגבלות רכות מציגות אזהרה מבלי למחוק את ההיסטוריה. דפי הפיד מכילים לכל היותר 200 כרטיסים. קטעי JSON עצמאיים מוגבלים ל-64 MiB; צמצמו את השאילתה או השתמשו ב-ZIP החתום עבור חומר גדול יותר. המאמת העצמאי מגביל את השמעת ההערות ל-256 MiB. Case Board אינו מאפשר או מיישם התראות על הערות חיצוניות.

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