Carbon Preferences API
Public Made by Adomby adom
Read 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
# 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:
```typescript
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
```bash
# 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)
```bash
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)
```javascript
const user = await get(`${API_BASE_URL}/user`);
const theme = user.hydrogen_preferences.settings['general.appearance.theme'];
```
### Tauri (Hydrogen Desktop)
```javascript
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
```json
{ "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
```bash
# 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)
```bash
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)
```javascript
await patch(`${API_BASE_URL}/user/hydrogen/settings`, {
key: 'general.appearance.theme',
value: 'adom-light'
});
```
### Tauri (Hydrogen Desktop)
```javascript
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
```bash
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