ChromiumOS Configuration

Often times browser and OS programs need to change their behavior at runtime based on how the OS has been configured. Further, many of pieces of code often ask the same question (e.g. “Is dev mode enabled?”), and the way to answer the question might not be clear, or it might be possible to find the answer through different means. Here we'll document those various configuration sources and which ones you should (or should not) be using.

Remember that many of these settings are not finalized at build time, and the same binary might be used in many different configurations. For example, we do not compile programs for a specific channel (i.e. the same binary image is used on stable, beta, dev, and canary), nor do we compile for a specific runtime mode (i.e. programs need to detect dev-mode vs normal-mode).


This file provides a number of fields for the browser, daemons, and users. However, many of these fields are meant purely for users/debugging, and they should not be parsed out directly.

Here are all the known fields and whether people may rely on their value. If it is not marked as Usable, then you must not parse it out for anything other than informational display to users. If a field is not documented here, you must assume it's the same as Usable=No, in which case you must not parse it.

If you want to add more fields, they should go in os-release instead.

UsableFieldMeaningExample Values
NoCHROMEOS_RELEASE_APPIDThe release Omaha appid{DC2BBB48-BC2C-493E-82DA-89BEE8321A5A}
NoCHROMEOS_BOARD_APPIDThis boards Omaha appid{DC2BBB48-BC2C-493E-82DA-89BEE8321A5A}
NoCHROMEOS_CANARY_APPIDThe canary channel Omaha appid{90F229CE-83E2-4FAF-8479-E368A34938B1}
YesCHROMEOS_ARC_VERSIONThe ARC++ version in the system4979549
YesCHROMEOS_ARC_ANDROID_SDK_VERSIONThe Android SDK version supported by ARC++25
NoCHROMEOS_RELEASE_BOARDHuman readable board name (and keyset used for signing)betty eve-signed-mpkeys
YesCHROMEOS_RELEASE_BUILD_NUMBERMajor OS version number11012
YesCHROMEOS_RELEASE_BRANCH_NUMBERMinor OS version number used by branches0
YesCHROMEOS_RELEASE_CHROME_MILESTONEChrome major version/release number70
YesCHROMEOS_RELEASE_PATCH_NUMBERPatch OS version number (datestamps for developer builds)0 2018_08_28_1422
YesCHROMEOS_RELEASE_TRACKOS release channelstable-channel testimage-channel
NoCHROMEOS_RELEASE_DESCRIPTIONHuman readable description for this build11012.0.2018_08_28_1422 (Test Build - vapier) developer-build betty
NoCHROMEOS_RELEASE_BUILD_TYPEHuman readable build typeOfficial Build Test Build - vapier
NoCHROMEOS_RELEASE_NAMEHuman readable OS Product nameChrome OS ChromiumOS
YesCHROMEOS_RELEASE_UNIBUILDSet to 1 for unified builds.1
YesCHROMEOS_RELEASE_VERSIONThe full OS version number11012.0.2018_08_28_1422
YesGOOGLE_RELEASEThe full OS version number11012.0.2018_08_28_1422
YesCHROMEOS_AUSERVERURI used to get OS updates
YesCHROMEOS_DEVSERVERURI to access the dev_server


See the lsb-release document for more details.


This file provides a number of fields for the browser, daemons, and users. However, many of these fields are meant purely for users/debugging, and they should not be parsed out directly.

More details on this file can be found in the os-release man page. It's a standard file among Linux distros and we aim to follow the spec.

Here are all the known fields and whether people may rely on their value. If it is not marked as Usable, then you must not parse it out for anything other than informational display to users. If a field is not documented here, you must assume it's the same as Usable=No, in which case you must not parse it.

UsableFieldMeaningExample Value
YesBUG_REPORT_URLSite where to report bugs
YesBUILD_IDFull OS version11012.0.2018_08_28_1422
YesGOOGLE_CRASH_IDID used when reporting crashes (crash-reporter)ChromeOS
YesHOME_URLSite where people can find info about this project
YesIDUnique ID for the OSchromeos
YesID_LIKEUnique IDs that the OS is related tochromiumos
YesNAMEFor name for the OSChrome OS
YesVERSIONMajor release version70
YesVERSION_IDFull release version70


Overrides and fragments can be dropped in here. These have higher precedence than the /etc/os-release file. The format is simple: the filename is the key and the content is the value.

For example, if there is an /etc/os-release.d/NAME file, then it will be used instead of the NAME=... line in the /etc/os-release file.


If you want to access fields in this file, please do not parse it yourself.

Compiled Code

In platform code, ChromiumOS provides a generic brillo::OsReleaseReader in brillo/osrelease_reader.h that can process this file. This handles both /etc/os-release and /etc/os-release.d/ automatically.

#include <brillo/osrelease_reader.h>
  brillo::OsReleaseReader reader;

  std::string value;
  if (!reader.GetString(kSomeKey, &value)) {
    // kSomeKey isn't defined.
    return false;

  // Do something with |value| now.

Shell Code

If you‘re writing shell code, you shouldn’t be accessing this file. Please rewrite your code in a better language.


This vboot_reference crossystem program is used to query NVRAM and other low level system/firmware functionality.

While there are many fields, here are the only ones developers should rely upon in normal use. There are a few more, but only for specific usage, which we'll cover next.

All other fields not covered here should be avoided. Low level firmware projects (like firmware updater or vboot_reference) are a bit of an exception as they're tightly integrated.

Common Fields

block_devmodeR/WBlock all use of developer mode
clear_tpm_owner_requestR/WClear TPM owner on next boot
cros_debugROOS should allow debug features. NB: This has no relationship to the OS channel.
dev_boot_legacyR/WEnable developer mode boot Legacy OSes
dev_boot_signed_onlyR/WEnable developer mode boot only from official kernels
dev_boot_usbR/WEnable developer mode boot from USB/SD
devsw_bootRODeveloper switch position at boot. NB: Most code should check cros_debug instead.
devsw_curRODeveloper switch current position. NB: Most code should check cros_debug instead.
disable_dev_requestR/WDisable virtual dev-mode on next boot
fwidROActive firmware ID
inside_vmRORunning in a VM?
mainfw_actROActive main firmware
mainfw_typeROActive main firmware type
nvram_clearedROHave NV settings been lost?
recovery_reasonRORecovery mode reason for current boot
recovery_requestR/WRecovery mode request
recoverysw_bootRORecovery switch position at boot
recoverysw_curRORecovery switch current position
ro_fwidRORead-only firmware ID
wpsw_curROFirmware write protect hardware switch current position

Specific-Use Fields

These fields may be used, but generally they're used only in highly specific code and projects.

debug_buildROOS image built for debug features. NB: Code should almost always check cros_debug instead. This has no relationship to the OS channel.
dev_enable_udcROEnable USB Device Controller
fw_vboot2ROIf firmware was selected by vboot2
kernel_max_rollforwardR/WMax kernel version to store into TPM
tpm_attackR/WTPM was interrupted since this flag was cleared
tpm_fwverROFirmware version stored in TPM
tpm_kernverROKernel version stored in TPM
vdat_flagsRORaw bit flags from VbSharedData
wipeout_requestR/WFirmware requested factory reset (wipeout)

Banned Fields

There are some fields that specifically should not be used. We'll document their alternatives.

  • arch (RO): Shell code could use uname -a to get the kernel architecture. Userland code generally shouldn't be changing behavior based on the arch.
  • hwid (RO): See the board/device detection FAQ.


Keep in mind that all fields are not guaranteed to always have a valid value. That means your queries should be constructed to “fail closed”. In the case of cros_debug, you want to always check if dev-mode is enabled by using if crossystem 'cros_debug?1'; then and never check if dev-mode is disabled by using if ! crossystem 'cros_debug?0'; then. If the system were broken or failing and cros_debug was not set up properly, then cros_debug might not be 1 or 0, so the latter code will wrongly “succeed”!

Compiled Code

The crossystem.h header provides a few VbGet helpers in the vboot_host library. Be sure to use pkg-config to locate the vboot_host library instead of using -lvboot_host directly.

#include <crossystem.h>
  int value = VbGetSystemPropertyInt("cros_debug");
  if (value < 0) {
    // Unable to read the value.
    return false;

  if (value == 1) {
    // Dev mode is enabled.
    return true;
  } else {
    // Not dev mode.
    return false;

Shell Code

The crossystem command provides a simple interface for testing values.

  crossystem [param1 [param2 [...]]]
    Prints the current value(s) of the parameter(s).
  crossystem [param1=value1] [param2=value2 [...]]]
    Sets the parameter(s) to the specified value(s).
  crossystem [param1?value1] [param2?value2 [...]]]
    Checks if the parameter(s) all contain the specified value(s).

So you can write shell code like:

if crossystem 'cros_debug?1'; then
  # The cros_debug field is equal to 1.
  # The cros_debug field is not equal to 1 (including not set).


The vpd tool provides access to Vital Product Data. Its homepage provides all the details on tools and fields.

Briefly, you'll want to use vpd_get_value <field> in tools.


The chromeos-config project (for unibuild configurations) takes care of storing and accessing device-specific details. Its homepage provides all the details on tools and fields.

Briefly, you'll want to use the cros_config program and libcros_config library in tools. Be sure to use pkg-config to locate the cros_config library instead of using -lcros_config directly.

USE Flags

Gentoo USE flags may be used at build time to control high level features. This is useful for a build that wants to enable/disable large functionality like touchscreen support, stylus support, or ARC++ availability.

This will usually take the shape of the ebuild listing the flag in IUSE and then passing that down to the build environment.

These flags can be passed to the runtime as well via libchromeos-use-flags. This is explained in more detail in the login_manager documentation.

USE flags must not be used to set board names (e.g. kevin or link). See the board/device behavior FAQ for alternatives.


No package should be using the cros-board.eclass anymore. This was used in the past to allow packages to vary behavior at build time based on the board, but we no longer want to let packages do that.

You might still find some packages in the tree using them, but it‘s because we haven’t yet migrated them off. is tracking that work.


How do I query dev mode status?

Use cros_debug?1 from crossystem. Never use cros_debug?0.

This tells you whether to enable dev-mode-specific features (like shell access). In general, programs should not change their behavior depending on whether the system is in dev-mode or normal-mode. This makes testing much harder as we do not run any tests in normal-mode.

Do not confuse dev-mode with “rootfs verification enabled” or “verified mode”. It is possible to have rootfs verification enabled while in dev-mode.

Do not confuse dev-mode with the keyset used to sign or verify the system. This has no relationship to devkeys, premp keys, or mp keys.

Do not confuse dev-mode with the dev channel release track. They have no relationship at all.

How do I find the current channel?

Use CHROMEOS_RELEASE_TRACK from lsb-release.

This will have values like stable-channel, beta-channel, dev-channel, canary-channel, and testimage-channel. Take note that test images use a testimage prefix and not test.

It is not possible (by design) to build code for a specific channel. Our release process takes an OS image and publishes it in the canary channel. Promoting it to dev/beta/stable only involves modifying the lsb-release file. The code is never recompiled.

However, most code should never change its behavior based on the active channel. If you have a feature you want to keep testing in e.g. canary channel before releasing it in a newer version, then you should look into Finch instead. Among other things, it provides a kill switch in case something goes wrong.

How do I change behavior based on board/device?

If the board is already unibuild-enabled, then you should always use that to change runtime behavior. See the chromeos-config section for more details. If the project you‘re working on does not yet support unibuild, then you will need to update it to support unibuild. If the setting you want to vary isn’t in the unibuild schema yet, then you will need to update it to include/document this new setting. The difficulty of extending existing projects is not an excuse to use deprecated methods for changing settings (i.e. the next paragraphs).

If you‘re working on an older board that isn’t unibuild-enabled, then you have a few options:

How do I find the current board/device?

Please see the previous question about changing behavior based on board/device. If you follow those guidelines, you usually shouldn't need the exact board/device name in the first place.

Use cros_config / name to get the device name.

Put simply, a single board (e.g. coral) can be used across multiple devices (e.g. astronaut, nasher, robo, etc...). The device name is used to disambiguate at runtime in a single board image.

Do not use crossystem hwid or CHROMEOS_RELEASE_BOARD in /etc/lsb-release. These do not handle unibuild where a single board supports multiple devices.

How do I pass different flags to Chrome (the browser)?

See the login_manager documentation for more details.

How do I find out the keyset used to sign the image (PreMP/MP/etc...)?

In short, you don't. Code must not change its behavior based on whether the running image has been signed with “premp” or “mp” keys. Trying to do this significantly & negatively impacts both automated testing and early dogfood testing with users.

Use some other knob to control behavior (such as Finch).