SQLite database¶
This page describes the image catalog database used by the Wsl-Manager modules. It is a small SQLite file that tracks available image sources (remote and builtin) and the local images downloaded on disk.
Location and lifetime¶
- The database file lives at
$env:LOCALAPPDATA/Wsl/RootFS/images.db(or$HOME/.local/share/Wsl/RootFS/images.dbon Linux, for testing purposes only) and is created on first use. - The class
WslImageDatabasewraps aSystem.Data.SQLite.SQLiteConnectionthroughSQLiteHelperand provides typed methods to manipulate the tables. - The helper function
Get-WslImageDatabaseopens a singleton instance ofWslImageDatabase, runs pending migrations, and starts a 3-minute timer that closes the connection when idle.Close-WslImageDatabaseends the session early when needed.
Schema overview¶
The initial structure is defined in Wsl-Image/db.sqlite.
Migrations in Wsl-Image/Wsl-Image.Database.ps1 add
columns and indexes over time.
erDiagram
direction LR
LocalImage }o--o| ImageSource : "source"
ImageSource o|--o{ ImageSourceCache : "cached in"
Table: ImageSource¶
Tracks every known image source.
| Column | Type / values | Notes |
|---|---|---|
| Id | TEXT, unique | Random GUID when inserted. |
| CreationDate | TEXT | Default CURRENT_TIMESTAMP. |
| UpdateDate | TEXT | Updated automatically; set via UpdateTimestampColumn. |
| Name | TEXT | Human-friendly name. |
| Tags | TEXT | Comma-separated tags. Part of the primary key since schema v7 (replaces Release). |
| Url | TEXT | Source URL (HTTP, Docker, file, etc.). |
| Type | TEXT | Image type enum string (e.g., Builtin, Incus, Docker, Uri, Local). |
| Configured | TEXT | 'TRUE' if the image is pre-configured; otherwise 'FALSE'. |
| Username | TEXT | Default username for configured images. |
| Uid | INTEGER | Default user UID for configured images. |
| Distribution | TEXT | OS distribution identifier. |
| Release | TEXT | Release label kept for display and matching. |
| LocalFilename | TEXT | Suggested filename for downloaded tarballs. |
| DigestSource | TEXT | Hash origin (docker, sums or single). |
| DigestAlgorithm | TEXT | Hash algorithm (default SHA256). |
| DigestUrl | TEXT | URL to the digest file when provided. |
| Digest | TEXT | Hash value of the image. |
| GroupTag | TEXT | Batch tag used when refreshing builtins; older rows with a different tag are removed. |
| Size | INTEGER | Optional image size (bytes). |
Primary key: (Type, Distribution, Tags, Configured); Id is also unique.
Table: LocalImage¶
Represents images present (or expected) on disk. Most columns are copied from
the corresponding ImageSource row when created. This allows tracking local
state even if the source is later removed or updated.
| Column | Type / values | Notes |
|---|---|---|
| Id | TEXT, PK | Random GUID per local entry. |
| ImageSourceId | TEXT | FK to ImageSource, nullable on delete. |
| CreationDate | TEXT | Default CURRENT_TIMESTAMP. |
| UpdateDate | TEXT | Updated automatically on writes. |
| Name | TEXT | Derived from metadata or filename. |
| Tags | TEXT | Comma-separated tags for matching. |
| Url | TEXT | Mirrors source URL when applicable. |
| State | TEXT | Synced, NotDownloaded, or Outdated. |
| Type | TEXT | Same enum as ImageSource Type. |
| Configured | TEXT | 'TRUE' when pre-configured; otherwise 'FALSE'. |
| Username | TEXT | Default username for configured images. |
| Uid | INTEGER | Default user UID for configured images. |
| Distribution | TEXT | OS distribution identifier. |
| Release | TEXT | OS release label. |
| LocalFilename | TEXT | Stored filename (often normalized to the hash). |
| DigestSource | TEXT | Hash origin (docker, sums or single). |
| DigestAlgorithm | TEXT | Hash algorithm (default SHA256). |
| DigestUrl | TEXT | URL to the digest file when provided. |
| Digest | TEXT | Hash value of the local image. |
| Size | INTEGER | Optional image size (bytes). |
There is a unique index on (ImageSourceId, Name) to prevent duplicates for the
same source.
Table: ImageSourceCache¶
Stores the last-seen cache headers for builtin/remote catalogs (etag and timestamp) to avoid redundant downloads.
| Column | Type | Notes |
|---|---|---|
| Type | TEXT, PK | Matches WslImageType. Either Builtin or Incus. |
| Url | TEXT | Source URL used to fetch the catalog. |
| LastUpdate | INTEGER | Unix timestamp from the cache file. |
| Etag | TEXT | ETag used to detect changes. |
Versioning and migrations¶
WslImageDatabase tracks the current schema version in PRAGMA user_version.
When opened, it runs UpdateIfNeeded to apply any missing migrations in order.
Each migration is a SQL snippet that modifies the schema or copies data as
needed. The current version is 7 as of this writing. The following is a
summary of the migrations:
- Create base tables from
db.sqlite. - Import cached builtin and Incus images into ImageSource.
- Add
GroupTagto ImageSource (used to tag refresh batches) and populate from cache etags. - Import existing local tarballs/JSON metadata into LocalImage
(
TransferLocalImages). - Add
Sizeto ImageSource and LocalImage. - Add unique index on LocalImage
(ImageSourceId, Name). - Change ImageSource primary key to use
Tagsinstead ofRelease(with data copy/rename).
Each step runs only once per database and bumps user_version.
How the module uses the database¶
Opening and caching¶
- There is a static singleton instance stored in
[WslImageDatabase]::Instance. Get-WslImageDatabaseopens the SQLite file, setsUpdateTimestampColumn = 'UpdateDate', and schedules automatic close after inactivity (3 minutes).- Callers are expected to reuse the singleton instead of opening new connections.
Inserting and updating image sources and local images¶
For insertion and updates, the module uses INSERT statements with
UPSERT clauses to ensure atomicity
and avoid duplicates.
For image sources, the unique primary key on
(Type, Distribution, Tags, Configured) ensures that only one row exists per
combination. For Incus images, the Tags field contains the Release value. In
consequence, we will have only one Incus/Ubuntu/22.04/FALSE row for
Ubuntu 22.04 whatsoever. For builtins, Tags contains latest, ensuring that
only one builtin per distribution exists.
For local images, the unique index on (ImageSourceId, Name) ensures that only
one local entry exists per name.
Importing builtin catalogs¶
- The cmdlet
Update-WslBuiltinImageCachedownloads Builtin or Incus catalog from the githubrootfsbranch. It updatesImageSourceCache, and inserts rows intoImageSourcewith generated GUIDs throughSaveImageBuiltins. SaveImageBuiltinswrites new builtin rows with aGroupTagcorresponding to the HTTP ResponseETagHeader, then deletes older rows for the same type and a differentGroupTag. This ensures that only the latest builtins are kept.SaveImageBuiltinsalso marks matchingLocalImagerows asOutdatedwhen digests change.
Managing individual image sources¶
SaveImageSourceupserts a single source (including digest and size data) and marks related local entriesOutdatedif the digest changes. This is used when refreshing local images or adding custom URIs.GetImageSourcesandGetImageBuiltinsreturn typedPSCustomObjectsfor consumers.
Importing local images¶
Migration from version prior to 3
Prior to version 3, local images were not tracked in a database. The images
metadata was stored in *.json files alongside the tarballs. The migration step 4
(TransferLocalImages) imports these files into the database and removes them
afterward.
This feature is primarily intended to migrate existing local images when
upgrading the database schema. For regular use, prefer Add-WslLocalImage to
register new local tarballs.
Move-LocalWslImagescans the image base directory for*.rootfs.tar.gzand matching*.jsonmetadata. Missing JSON files are synthesized viaNew-WslImage-MissingMetadatausing file names and tarball contents.- Each JSON entry is matched against existing ImageSource rows by
type/distribution/release, URL, or digest. When none is found, a new
ImageSource is created with a
GroupTagmatching the LocalImage ID. - Local files are renamed to
<digest>.rootfs.tar.gz(or<algorithm>_<digest>.rootfs.tar.gz) to ensure deterministic filenames, unless-DoNotChangeFilesis passed. - The resulting LocalImage row is inserted with
State = 'Synced'when the file exists orNotDownloadedotherwise. JSON metadata files are removed after import unless file changes are suppressed.
Creating and updating local entries programmatically¶
[WslImageDatabase]::CreateLocalImageFromImageSourceclones a source row into LocalImage withState = 'NotDownloaded'and respects the unique index to avoid duplicates.[WslImageDatabase]::SaveLocalImageupserts LocalImage rows from cmdlet output, normalizing hash data and size when provided.[WslImageDatabase]::RemoveLocalImageand[WslImageDatabase]::RemoveImageSourcedelete rows safely; the foreign key usesON DELETE SET NULLto keep local entries if their source is removed.
Cache handling¶
[WslImageDatabase]::GetImageSourceCacheand[WslImageDatabase]::UpdateImageSourceCacheread/write the cache table so callers can perform conditional requests (e.g.,ETagcomparison) when refreshing catalogs.
Practical tips¶
- Always call
Get-WslImageDatabaseto obtain the shared connection and ensure migrations run. - When introducing schema changes, add a migration block in
UpdateIfNeeded, bumpCurrentVersion, and add the SQL snippet alongside the others (similar toAddImageSourceGroupTagSql). - Keep
Tagspopulated; they are part of the primary key and are filled withReleasewhen absent during migrations. - Use the provided helpers (
SaveImageBuiltins,SaveImageSource,SaveLocalImage) rather than issuing raw SQL so digests, states, and timestamps stay consistent.