Skip to content

Databases and settings

The unit keeps its persistent state in ~29 SQLite databases, and the application reaches them through two DBUS servers rather than by opening files itself. There are two copies of every database, and the difference between them is the thing that most often makes an edit appear to do nothing.

How these findings were obtained

Read-only analysis: the application image (the C_BCM_DB_MANAGER_SERVER / C_BCM_UP_SERVER interfaces and their query strings), the updater (upgrade.out/UpgPlugin.out), and one SPYSTORE dump of the live /USER_DATA tree. The structure of most databases is therefore not known — the tables below name only what has actually been established, and mark the rest. Inspect a database you own with the dbschema tool to fill the gaps.

The seed copy and the live copy

where who owns it
seed Data_base/sqlite/*.sqlite inside <module>/system.bin us — it is part of the media partition the tools rebuild
live /USER_DATA/user_data/sqlite/ the car — written as the unit is used

system.bin is a gzip'd tar the unit extracts to a read-only /SYSTEM/; the seeds sit in its Data_base/sqlite/ directory. Editing a seed changes nothing on the unit (confirmed on hardware): the application reads its settings from USER_DATA, not from /SYSTEM/. That is why the source scheduler and the AUX boot work cannot be steered by editing the seed.

The live directory is /USER_DATA/user_data/sqlite/, whose parent C_FS_STORAGE_CTRL_PATH::GetUserDataDir resolves as /USER_DATA/user_data/ (read). A SPYSTORE dump (executed, 2026-09-14) held 14 databases there, each with a .inf CRC sidecar; the same tree holds Audio/ (Tuner.dat/Radio.dat presets), Nav/, TTS/ and T2BF/.

Two things about those live files are established from that dump (read):

  • up_common.sqlite and up_user.sqlite are stored gzip'd (1f8b …), which is how the unit keeps them on disk — the boot log's gzUnixRead. nav_dest.sqlite, by contrast, is a plain SQLite file. Which other stores are compressed is not known.
  • The .inf sidecar beside each database is exactly CRC32: <signed decimal> and is (re)generated by C_UPGRADE::ManageSQLiteFiles (read; see Flashing).

connectivity.sqlite — the paired phones — is not under sqlite/: the boot log shows it is imported from the system partition, so it is out of reach of a USER_DATA backup.

tools/dbschema.py reads a gzip'd store transparently, so both forms are inspectable.

The live copy is the car's, not yours to overwrite

USER_DATA holds paired phones, navigation destinations and presets. Replacing a database there overwrites state the car owns, which reflashing does not put back. A build manifest with a user_data payload is refused by build_package.py unless it also sets accept_data_loss: true. Say so plainly before shipping one.

How the application reaches them

The application does not open a database path directly. It asks a DBUS service and receives a path (read):

  • C_BCM_DB_MANAGER_SERVER — get_database_path, save_database, database_corruption_detected. It is the registry front end and the corruption watch.
  • C_BCM_UP_SERVER — the up_* (user profile) settings stores.
  • db_manager.sqlite — the registry itself: the list of every database and its persistence policy. Its own tables and columns are not known.

The shared UP_Keys table

The up_* stores share one generic, typed key/value table (read):

UP_Keys(Section, Name, Type, Idx, IntValue, FloatValue, StringValue, BlobValue, …)

Type selects which value column is meaningful — an integer, a float, a string or a blob — and Idx distinguishes repeated keys. A row is addressed as Section.Name; the settings keys read for the AUX work are supervisor.Last_Source, supervisor.Last_Source_Priority and supervisor.Src_Radio_SchedPos (read, from the disassembly and a settings dump).

The unit's own dump — CMMUPKeys::ShowStatus, reported as 271 keys — prints each row as <Section>.Name.<Idx> <type> : <value> (read):

supervisor.Last_Source.0            <int> : 1
supervisor.Last_Source_Priority.0   <int> : 10
supervisor.Src_Radio_SchedPos.0     <int> : 7

This is also where per-source audio defaults such as AUX / Vol_aux live (read). The two trailing columns in the schema above — reset_factory_enabled and modified — come from the synthetic up_common.sqlite that tests/media_helpers.py builds; the unit's own CREATE TABLE has not been captured, so its full column set is not known.

The databases, by group

The inventory is Architecture §6. The third column names only the tables that have actually been established; not known means exactly that.

database group tables established
config.sqlite configuration not known
config_options.sqlite configuration not known — never opened
versions.sqlite configuration not known
version_history.sqlite configuration not known
db_manager.sqlite configuration not known — the database registry
upgrade.sqlite configuration not known
cheatcodes.sqlite configuration cheatcodes (read)
RadioLogo.sqlite configuration not known
tuner.sqlite configuration not known
media_catalog.sqlite configuration not known
media_cdc_catalog.sqlite configuration not known
media_jkb_catalog.sqlite configuration not known
nav_poi.sqlite configuration ZARAssociation (read)
up_common.sqlite user / runtime UP_Keys (read)
up_config.sqlite user / runtime UP_Keys (inferred — up_* family); never opened
up_user.sqlite user / runtime UP_Keys (inferred — up_* family)
up_user_hmi.sqlite user / runtime UP_Keys (inferred — up_* family); never opened
agenda.sqlite user / runtime not known
KeyboardHistory.sqlite user / runtime not known
Pictures.sqlite user / runtime not known
browser.sqlite user / runtime not known
connectivity.sqlite user / runtime not known — imported from the system partition
Trip.sqlite user / runtime not known
navigation.sqlite user / runtime not known
nav_dest.sqlite user / runtime not known — navigation destinations
diagnosis.sqlite user / runtime not known
diag_zi.sqlite user / runtime not known — copied into a SPYTAKE collect
db_alerts.sqlite user / runtime not known
desktopServices.sqlite desktop current_menu, current_carrousel, persistent_menu (names read; columns not known)

Two of those tables are worth a sentence:

  • cheatcodes is the one schema the project has already written down in full — see Cheatcodes and the spy system. The codes are data, not compiled in.
  • nav_poi.sqlite ships a ZARAssociation table; the "which POI subcategories are danger zones" membership is a join against it, so it is editable data (read; see What is reachable).

The four that have not been opened

These databases have not been opened at all (read):

up_config.sqlite, up_user_hmi.sqlite, desktopServices.sqlite, config_options.sqlite.

Feature flags hide in exactly such places, so these are the first candidates for a database that changes behaviour without a patch. desktopServices.sqlite is a partial exception: the menu tree is known to come from its current_menu / current_carrousel tables and it also carries persistent_menu, but the columns of those tables are not known.

Inspecting your own

tools/dbschema.py dumps the schema — tables, columns, types, NOT NULL, defaults, primary keys, indexes and foreign keys — of a database, or of every Data_base/sqlite/*.sqlite inside a media partition you own:

uv run tools/dbschema.py schema /USER_DATA/user_data/sqlite/nav_dest.sqlite
uv run tools/dbschema.py from-package SMEG_PLUS_UPG/NAV/system.bin
uv run tools/dbschema.py from-package SMEG_PLUS_UPG/NAV --json

It reads only, copies the bytes to a throwaway location first, and never ships vendor data. Point it at a system.bin from your own package, or at a database recovered from your own unit (a SPYSTORE backup, say) — not at anything from someone else's.

Not known

  • The table structure of every database except the few named above.
  • Whether the seed and live copies of a database always share a schema.
  • The exact meaning of each UP_Keys.Type value (inferred: it selects the value column).
  • Which live stores are gzip'd — observed for up_common and up_user only.
  • What db_manager.sqlite records, beyond being the registry.