Carbon Preferences API

Store and retrieve per-user key-value pairs via the Carbon hydrogen_preferences object.
Adrian built this for Hydrogen's theme/UI settings, but it works for any lightweight per-user config (adom-desktop flags, feature toggles, timestamps, etc.).

Hard limit

10 MB total per user. Keep individual values small. Don't dump logs, blobs, or large JSON objects into preferences.

Data model

GET /user returns the authenticated user. The hydrogen_preferences field contains three sections:

hydrogen_preferences: {
  settings:    Record<string, string | number | boolean>
  keybindings: Record<string, string>
  extensions:  Record<string, { version: string, enabled: boolean }>
}

Reading preferences

Read via GET /user and extract the hydrogen_preferences field.

CLI

# Full user profile (hydrogen_preferences is a top-level field)
adom-cli carbon user get

# Extract just settings with jq
adom-cli carbon user get | jq '.hydrogen_preferences.settings'

# Get a single setting
adom-cli carbon user get | jq -r '.hydrogen_preferences.settings["general.appearance.theme"]'

curl (from container)

API_KEY=$(cat /var/run/adom/api-key)
curl -s -H "Cookie: session_token=$API_KEY" https://carbon.adom.inc/user \
  | jq '.hydrogen_preferences'

JavaScript (Hydrogen web)

const user = await get(`${API_BASE_URL}/user`);
const theme = user.hydrogen_preferences.settings['general.appearance.theme'];

Tauri (Hydrogen Desktop)

const { invoke } = await import('@tauri-apps/api/core');
const result = await invoke('auth_proxy', { path: '/user', method: 'GET' });
const settings = result.data.hydrogen_preferences.settings;

Writing settings

Endpoint

PATCH /user/hydrogen/settings
Content-Type: application/json

Request body

{ "key": "dotted.key.name", "value": "<string | number | boolean | null>" }
  • key -- dotted lowercase string matching ^(?:[a-z0-9_]+)(?:\.[a-z0-9_]+)*$
  • value -- string, number (f64), boolean, or null to delete

One key per request. Returns { "status": 200 } on success.

CLI

# Set a boolean
adom-cli carbon user hydrogen-settings '{"key":"desktop.enabled","value":true}'

# Set a string
adom-cli carbon user hydrogen-settings '{"key":"desktop.last_slug","value":"john-myproject-abc123"}'

# Set a number
adom-cli carbon user hydrogen-settings '{"key":"desktop.last_connect_epoch","value":1747612800}'

# Delete a key (set value to null)
adom-cli carbon user hydrogen-settings '{"key":"desktop.enabled","value":null}'

curl (from container)

API_KEY=$(cat /var/run/adom/api-key)
curl -s -X PATCH https://carbon.adom.inc/user/hydrogen/settings \
  -H "Cookie: session_token=$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key":"desktop.enabled","value":true}'

JavaScript (Hydrogen web)

await patch(`${API_BASE_URL}/user/hydrogen/settings`, {
  key: 'general.appearance.theme',
  value: 'adom-light'
});

Tauri (Hydrogen Desktop)

await invoke('auth_proxy', {
  path: '/user/hydrogen/settings',
  method: 'PATCH',
  body: { key: 'desktop.enabled', value: true }
});

Writing keybindings

Endpoint

PATCH /user/hydrogen/keybindings
Content-Type: application/json

CLI

adom-cli carbon user hydrogen-keybindings '{"key":"editor.save","value":"Ctrl+S"}'

Key naming conventions

Use dotted namespaces to avoid collisions. Third-party tools (adom-desktop, CLI tools, etc.) MUST use their own namespace prefix so they never clobber keys the Hydrogen frontend uses.

Namespace Owner Examples
general.* Hydrogen frontend general.appearance.theme, general.developer_mode
schematic.* Hydrogen schematic editor schematic.nudge_amount.small
3d.* Hydrogen 3D viewer 3d.control_style
code.* Hydrogen code editor code.vim_emulation_mode
simulator.* Hydrogen simulator simulator.code_editor_position
desktop.* adom-desktop / HD desktop.enabled, desktop.last_connect_epoch, desktop.last_slug

Rule: Never write to general.*, schematic.*, 3d.*, code.*, simulator.*, or extensions.* -- those belong to the Hydrogen frontend. Pick a unique prefix for your tool (e.g., desktop.*, chipfit.*, tsci.*).

Known Hydrogen settings (defined in settings tree)

These have schema definitions with types, defaults, and validation:

Key Type Default Description
general.developer_mode boolean false Developer mode
general.pane_focus_mode boolean true Only focused pane is active
general.appearance.theme string adom-dark Theme
general.appearance.user_interface_style string standard UI density (standard, compact)
schematic.nudge_amount.small number 1 Arrow key nudge (px)
schematic.nudge_amount.large number 8 Shift+arrow nudge (px)
schematic.invert_zoom_direction boolean false Invert scroll zoom
schematic.default_cursor string crosshair Default cursor
3d.control_style string fusion 3D controls (fusion, solidworks, blender)
3d.invert_zoom_direction boolean false Invert 3D scroll zoom
code.appearance.font.family string DM Mono Code font
code.line_numbers.enabled boolean true Show line numbers
code.line_numbers.style string absolute Line number style
code.vim_emulation_mode boolean false Vim mode
simulator.code_editor_position string right Code panel position

Custom keys (like desktop.*) are not in this schema -- they are stored and returned as-is with no server-side validation.

Behavior notes

  • Setting a value to null deletes the key entirely (not stored as null)
  • Setting a value equal to the schema default in the Hydrogen frontend also deletes it (clean storage)
  • Keys not in the schema are accepted and stored -- no server-side key validation
  • One key per PATCH request; batch by issuing multiple requests
  • Auth: same as all Carbon endpoints -- session token cookie or X-Api-Key header
  • The full preferences object is returned inline with every GET /user call, so there's no separate GET endpoint for just preferences