feat(storage): X14 Phase 1 — unified storage layer (services/storage.py) #151

Merged
chaim merged 1 commits from worktree-storage-minio-phase1 into main 2026-06-08 07:48:19 +00:00
Owner

מה זה

שלב 1 של הגירת-האחסון ל-MinIO (X14, משימה 106.2): שכבת-אחסון יחידה services/storage.py — נקודת-החיבור היחידה לכל ה-I/O הבינארי, שמחליפה את ה-open()/shutil/Path.write_bytes המפוזרים על ~8 שירותים.

מנוע נבחר ע"י STORAGE_BACKEND

  • filesystem (ברירת-מחדל) — דיסק תחת DATA_DIR, זהה byte-for-byte להתנהגות הקיימת
  • dual — כתיבה לדיסק+S3, קריאה S3→דיסק (חלון-הגירה)
  • s3 — MinIO דרך aioboto3 (ייבוא lazy — נעדר בנתיב filesystem)

עיצוב

  • מפתחות = נתיבי POSIX יחסיים ל-DATA_DIR; ה-FS backend מתעלם מהדלי הלוגי ושומר את העץ היחיד הקיים → אפס שינוי-התנהגות בברירת-מחדל
  • S3 ממפה דלי-ממשל (documents/immutable/derived)→דלי MinIO; presigned נחתם מול ה-endpoint הציבורי ונושא שם-קובץ עברי דרך RFC-5987 Content-Disposition (INV-STG2)
  • שגיאת-S3 ב-dual נרשמת ב-log, לא נבלעת (כלל-הנדסה §6) — הדיסק נשאר מקור-אמת

שינויים

  • config.py: STORAGE_BACKEND + MINIO_* (endpoint/public/creds/region/3 דליות/presign-TTL)
  • mcp_env_catalog.py: קטגוריית storage חדשה + 10 specs (X10/INV-ENV1)
  • pyproject.toml: aioboto3>=13 (נצרך כאן → נפרס עם השימוש הראשון)
  • tests/test_storage.py: 18 בדיקות עוברות (round-trip FS, נרמול-מפתח + traversal guard, החלטת-מנוע, dual write-both + fallback כש-S3 למטה)

סוג + invariants

לא חוברו call-sites עדיין (זה שלב 2/106.3). STORAGE_BACKEND=filesystem בייצור → אין שינוי-התנהגות.
מקיים G2 (מסלול-אחסון יחיד). מבסס INV-STG1/2/3/6. ספ: docs/spec/X14-storage-minio.md.

🤖 Generated with Claude Code

## מה זה שלב 1 של הגירת-האחסון ל-MinIO (X14, משימה 106.2): **שכבת-אחסון יחידה** `services/storage.py` — נקודת-החיבור היחידה לכל ה-I/O הבינארי, שמחליפה את ה-`open()`/`shutil`/`Path.write_bytes` המפוזרים על ~8 שירותים. ## מנוע נבחר ע"י `STORAGE_BACKEND` - **filesystem** (ברירת-מחדל) — דיסק תחת DATA_DIR, **זהה byte-for-byte להתנהגות הקיימת** - **dual** — כתיבה לדיסק+S3, קריאה S3→דיסק (חלון-הגירה) - **s3** — MinIO דרך aioboto3 (ייבוא lazy — נעדר בנתיב filesystem) ## עיצוב - מפתחות = נתיבי POSIX יחסיים ל-DATA_DIR; ה-FS backend מתעלם מהדלי הלוגי ושומר את העץ היחיד הקיים → **אפס שינוי-התנהגות** בברירת-מחדל - S3 ממפה דלי-ממשל (documents/immutable/derived)→דלי MinIO; presigned נחתם מול ה-endpoint הציבורי ונושא שם-קובץ עברי דרך RFC-5987 Content-Disposition (INV-STG2) - שגיאת-S3 ב-dual **נרשמת ב-log, לא נבלעת** (כלל-הנדסה §6) — הדיסק נשאר מקור-אמת ## שינויים - `config.py`: `STORAGE_BACKEND` + `MINIO_*` (endpoint/public/creds/region/3 דליות/presign-TTL) - `mcp_env_catalog.py`: קטגוריית `storage` חדשה + 10 specs (X10/INV-ENV1) - `pyproject.toml`: `aioboto3>=13` (נצרך כאן → נפרס עם השימוש הראשון) - `tests/test_storage.py`: **18 בדיקות** עוברות (round-trip FS, נרמול-מפתח + traversal guard, החלטת-מנוע, dual write-both + fallback כש-S3 למטה) ## סוג + invariants לא חוברו call-sites עדיין (זה שלב 2/106.3). `STORAGE_BACKEND=filesystem` בייצור → **אין שינוי-התנהגות**. מקיים **G2** (מסלול-אחסון יחיד). מבסס **INV-STG1/2/3/6**. ספ: `docs/spec/X14-storage-minio.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chaim added 1 commit 2026-06-08 07:48:15 +00:00
The single choke-point for all binary file I/O (originals, derived
artifacts, exports), replacing the scattered open()/shutil/Path.write_bytes
calls across ~8 services. Backend chosen by STORAGE_BACKEND:
- filesystem (default): disk under DATA_DIR — byte-for-byte legacy behaviour
- dual: write disk + S3, read S3→disk fallback (migration window)
- s3: MinIO via aioboto3 (lazy import; absent in the filesystem path)

Keys are DATA_DIR-relative POSIX paths; the FS backend ignores the logical
bucket and keeps the existing single tree, so the default backend is zero
behaviour change. S3 maps a governance bucket (documents/immutable/derived)
→ MinIO bucket; presigned URLs are minted against the public endpoint
(browser-reachable) and carry the Hebrew filename via RFC-5987
Content-Disposition.

- config: STORAGE_BACKEND + MINIO_* (endpoint, public-endpoint, creds,
  region, 3 bucket names, presign TTL)
- mcp_env_catalog: new "storage" category + 10 specs (X10/INV-ENV1)
- pyproject: aioboto3>=13 (consumed here, deployed with first use)
- tests: 18 unit tests (FS round-trip, key normalization/traversal guard,
  bucket resolution, backend selection, dual write-both + S3-down fallback)

No call-sites are rewired yet — that is Phase 2 (106.3). STORAGE_BACKEND
stays filesystem in prod, so behaviour is unchanged.

Invariants: keeps G2 (one storage path replaces scattered I/O); establishes
INV-STG1 (single layer), INV-STG2 (atomic keys, Hebrew name in metadata),
INV-STG3 (governance buckets), INV-STG6 (presigned serving).
Spec: docs/spec/X14-storage-minio.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chaim merged commit 81b3de6f4f into main 2026-06-08 07:48:19 +00:00
chaim deleted branch worktree-storage-minio-phase1 2026-06-08 07:48:19 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/legal-ai#151