Carbon Preferences API
UnreviewedRead and write per-user key-value settings via the Carbon API. Store theme, feature flags, timestamps, and tool config that persists across sessions and carries between adom-desktop, Hydrogen Desktop,
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, ornullto 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
nulldeletes 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 /usercall, so there's no separate GET endpoint for just preferences