Summary
The openrelik-server image ships a pre-baked /app/openrelik/sqlite_scanner.duckdb_extension binary compiled for DuckDB v1.4.4, but the duckdb Python package installed in the same image is v1.5.2. Any code path that LOADs the baked file hits:
IO Error: Failed to install '/app/openrelik/sqlite_scanner.duckdb_extension'
The file was built specifically for DuckDB version 'v1.4.4' and can only be
loaded with that version of DuckDB. (this version of DuckDB is 'v1.5.2')
Reproduction
Latest image as of 2026-04-23:
ghcr.io/openrelik/openrelik-server@sha256:89adb0ebc41f5fbb1296a098c1e440c1761e896aaa0e4be2169dc16eed3a8785
Inside the container:
$ python3 -c "import duckdb; print(duckdb.__version__)"
1.5.2
$ python3 -c "import duckdb; duckdb.connect().execute(\"LOAD '/app/openrelik/sqlite_scanner.duckdb_extension';\")"
duckdb.duckdb.InvalidInputException: Invalid Input Error: Failed to load
'/app/openrelik/sqlite_scanner.duckdb_extension', The file was built
specifically for DuckDB version 'v1.4.4' and can only be loaded with that
version of DuckDB. (this version of DuckDB is 'v1.5.2')
Impact
Features that rely on the baked extension — in our environment the file-level SQL-query path (/api/v1/files/{file_id}/sql/query) that lets analysts run SQL against uploaded SQLite databases (Plaso SQLite output, app caches, browser history DBs) — fail with the error above until the extension is replaced.
Suggested fix
Two options:
A (minimal): Re-bake the extension at image build time against whatever DuckDB version is in pyproject.toml. A small Dockerfile snippet along these lines fetches the matching version:
RUN python3 -c "import duckdb; c = duckdb.connect(); c.execute('INSTALL sqlite_scanner')" && \
cp /root/.duckdb/extensions/v*/linux_amd64/sqlite_scanner.duckdb_extension \
/app/openrelik/sqlite_scanner.duckdb_extension
B (arguably cleaner): Drop the pre-baked file entirely and have the code do INSTALL sqlite_scanner; LOAD sqlite_scanner; on first use. DuckDB will download from extensions.duckdb.org the first time the feature is invoked, cache it under ~/.duckdb/extensions/, and reuse the cached copy on subsequent calls. No version-skew possible. Small cost: first use of the feature in each container incurs a ~3-second network download.
Workaround (in-place fix that we're using today)
docker exec openrelik-server python3 -c "import duckdb; duckdb.connect().execute('INSTALL sqlite_scanner')"
docker exec openrelik-server cp \
/root/.duckdb/extensions/v1.5.2/linux_amd64/sqlite_scanner.duckdb_extension \
/app/openrelik/sqlite_scanner.duckdb_extension
Survives container restart; reverts on docker compose pull.
Environment
- Deployment: CYPFER multi-tenant DFIR platform running OR on isolated per-case Docker containers via docker-compose
- Image:
ghcr.io/openrelik/openrelik-server@sha256:89adb0ebc41f5fbb1296a098c1e440c1761e896aaa0e4be2169dc16eed3a8785
- Host OS: Ubuntu 22.04 in LXC
- Reported by: CYPFER-Inc (happy to PR a fix if the team has a preferred approach)
Summary
The
openrelik-serverimage ships a pre-baked/app/openrelik/sqlite_scanner.duckdb_extensionbinary compiled for DuckDB v1.4.4, but theduckdbPython package installed in the same image is v1.5.2. Any code path thatLOADs the baked file hits:Reproduction
Latest image as of 2026-04-23:
Inside the container:
Impact
Features that rely on the baked extension — in our environment the file-level SQL-query path (
/api/v1/files/{file_id}/sql/query) that lets analysts run SQL against uploaded SQLite databases (Plaso SQLite output, app caches, browser history DBs) — fail with the error above until the extension is replaced.Suggested fix
Two options:
A (minimal): Re-bake the extension at image build time against whatever DuckDB version is in
pyproject.toml. A small Dockerfile snippet along these lines fetches the matching version:B (arguably cleaner): Drop the pre-baked file entirely and have the code do
INSTALL sqlite_scanner; LOAD sqlite_scanner;on first use. DuckDB will download fromextensions.duckdb.orgthe first time the feature is invoked, cache it under~/.duckdb/extensions/, and reuse the cached copy on subsequent calls. No version-skew possible. Small cost: first use of the feature in each container incurs a ~3-second network download.Workaround (in-place fix that we're using today)
Survives container restart; reverts on
docker compose pull.Environment
ghcr.io/openrelik/openrelik-server@sha256:89adb0ebc41f5fbb1296a098c1e440c1761e896aaa0e4be2169dc16eed3a8785