# Bundle Catalog & Items Repositories ## Scope - Deterministic storage for offline bundle metadata with tenant isolation (RLS) and stable ordering. - Ready for PostgreSQL-backed implementation while providing in-memory deterministic reference behavior. ## Schema (logical) - `bundle_catalog`: - `tenant_id` (string, PK part, RLS partition) - `bundle_id` (string, PK part) - `digest` (hex string) - `imported_at_utc` (datetime) - `content_paths` (array of strings, sorted ordinal) - `bundle_items`: - `tenant_id` (string, PK part, RLS partition) - `bundle_id` (string, PK part) - `path` (string, PK part) - `digest` (hex string) - `size_bytes` (long) ## Implementation delivered - 2025-11-20: In-memory repositories enforcing tenant isolation and deterministic ordering: - `InMemoryBundleCatalogRepository` (upsert + list ordered by `bundle_id`). - `InMemoryBundleItemRepository` (bulk upsert + list ordered by `path`). - Models: `BundleCatalogEntry`, `BundleItem`. - Tests cover upsert overwrite semantics, tenant isolation, and deterministic ordering (`tests/AirGap/StellaOps.AirGap.Importer.Tests/InMemoryBundleRepositoriesTests.cs`). - 2026-04-20: Durable file-backed repositories for the live CLI mirror import path: - `FileSystemBundleCatalogRepository` stores tenant-scoped bundle catalog entries under the offline-kit local state root with atomic JSON writes. - `FileSystemBundleItemRepository` stores tenant+bundle item manifests under the same root with deterministic ordinal ordering by `path`. - `src/Cli/StellaOps.Cli` now registers the file-backed repositories for `MirrorBundleImportService`, so imported metadata survives fresh CLI processes instead of disappearing with the process-local container state. ## Migration notes (for PostgreSQL backends) - Create compound unique indexes on (`tenant_id`, `bundle_id`) for catalog; (`tenant_id`, `bundle_id`, `path`) for items. - Enforce RLS by always scoping queries to `tenant_id` and validating it at repository boundary (as done in in-memory reference impl). - Keep paths lowercased or use ordinal comparisons to avoid locale drift; sort before persistence to preserve determinism. ## Runtime contract - Live CLI mirror-bundle import uses the durable file-backed repositories rooted under `%LocalApplicationData%/stellaops/offline-kit/state/mirror-bundles`. - In-memory repositories remain valid for deterministic tests and isolated non-persistent harnesses, but they are no longer the live CLI runtime default. ## Next steps - If the AirGap controller or another shared service requires multi-process or multi-node bundle metadata, add a PostgreSQL-backed repository pair that mirrors the same tenant scoping and deterministic ordering. ## Owners - AirGap Importer Guild.