Hydrogen Desktop Bootstrap (apps + skills)
Public Made by Adomby adom
Layer Hydrogen Desktop's workspace onto a standard Adom workstation.
name: hd-adom-auth description: > Adom auth in HD — how login/logout works (auth intent flow), where the session token lives, the profile menu, and how the Adom session feeds the setup steps. For Claude Code auth specifically, see hd-claude-auth. READ when troubleshooting Adom login failures, session token expiry, the profile dropdown, or virgin-reset Adom-auth options. Trigger words — adom login, login, logout, auth, session token, auth intent, confirmation code, profile menu, avatar, sign in, sign out, hydrogen-session.txt, auth proxy, clear session, HD adom login, Your profile, Your repositories.
Hydrogen Desktop -- Authentication Reference
Auth Flow Overview
HD uses an auth intent flow (no email/password entry in the app). The Tauri backend creates a short-lived intent token on the Carbon API, opens the user's default browser to the Adom login page, and polls until the user completes sign-in in the browser.
Sequence
- HD calls
invoke('auth_intent_start')-> Rust POSTs tohttps://carbon.adom.inc/auth/intentswith{"max_age": 7776000}(90-day session). - Carbon returns
{token, confirmation_code, expires_at, auth_url}. - HD opens the browser to the
auth_urlCarbon returned, falling back tohttps://hydrogen.adom.inc/auth/intent?token={token}(singularintent, token as a query param — NOT/auth/intents/{token}). - HD displays the confirmation code on screen and shows a spinner.
- Frontend polls
invoke('auth_intent_poll', {token})every 2 seconds. Rust GETshttps://carbon.adom.inc/auth/intents/{token}/status. - When the user confirms in the browser, the response includes
{state: "authenticated", session_token: "..."}. - Rust saves the session token to
%APPDATA%\hydrogen-desktop\hydrogen-session.txtand a replay copy to%APPDATA%\hydrogen-desktop\replay-session.txt. - Frontend redirects to the dashboard.
Source files
- Login page:
src/routes/auth/login/+page.svelte - Auth Rust backend:
src-tauri/crates/hd-app/src/lib.rs(functionsauth_intent_start,auth_intent_poll,auth_proxy,check_replay_credentials,replay_adom_login). NOTE: the oldsrc-tauri/src/lib.rsis a pre-crate-split vestige — the live code is in thehd-appcrate. - Auth components:
src/lib/components/auth/(Header, Form, Footer, SubmitButton, Error, Success) - User API:
src/lib/api/authenticated_user.ts
Session Token
Location: %APPDATA%\hydrogen-desktop\hydrogen-session.txt
A plain-text file containing the Adom session cookie value. Created by either:
- The auth intent flow (on successful poll)
- The auth proxy (when Carbon returns a
set-cookie: session_token=...header)
The auth proxy (invoke('auth_proxy', ...)) reads this file and attaches it
as a Cookie: session_token=... header on every request to carbon.adom.inc.
Token lifecycle
- Created: on login (auth intent or browser set-cookie capture)
- Refreshed: automatically when the auth proxy receives a new set-cookie
- Cleared: on logout (
auth_proxy({path: "/auth/clear-local"})) or on 401 response (Rust auto-deletes the file) - Max age: 90 days (set at intent creation)
Reading the token programmatically
# From the control API (default 47084; the port is dynamic — read ~/.adom/hd-control-url)
curl "$(cat ~/.adom/hd-control-url)/auth-token"
# From inside the workspace (injected by setup step inject-api-key, mode 644)
# run inside the workspace — WSL2: `wsl -d Adom-Workspace -u adom -- cat /var/run/adom/api-key`; Docker: `docker exec <container> cat /var/run/adom/api-key`
cat /var/run/adom/api-key
Auth Proxy
The Tauri command auth_proxy is the gateway for all authenticated requests
to the Adom Carbon API. It:
- Reads the session token from disk
- Attaches it as a cookie to the outgoing request
- Captures any
set-cookieresponse and updates the saved token - On 401, deletes the token and returns
{expired: true} - Supports a special
path: "/auth/clear-local"to delete the token file
Base URL: https://carbon.adom.inc
// Example: get the current user
const result = await invoke('auth_proxy', {
path: '/api/me',
method: 'GET'
});
// result = { status: 200, data: { id: "...", name: "jlauer12", ... }, ok: true }
Credential Replay
On login page mount, HD checks for saved credentials before starting the full auth intent flow:
invoke('check_replay_credentials')checks if replay files exist on disk.- If
replay-session.txtexists (has_adom = true), HD shows a "REPLAY" badge and a 5-second countdown. invoke('replay_adom_login')attempts to authenticate using the saved session token.- If replay succeeds, the user is logged in without opening a browser.
- If replay fails, HD falls back to the normal auth intent flow.
This makes re-login after a virgin reset instant when the user chose to preserve the Adom session token.
Claude Credentials
Location: %APPDATA%\hydrogen-desktop\replay-claude-credentials.json
Stores Claude Code OAuth credentials so they can be replayed into the
workspace during bootstrap. The setup step inject-api-key reads the
session token to authenticate with the Adom API, and Claude credentials
are injected separately.
Virgin reset options for auth
The virgin reset panel (Setup Panel -> Virgin Reset) has per-item toggles:
| Toggle | File | What it clears |
|---|---|---|
| Adom session token | hydrogen-session.txt |
Adom login. Next launch requires re-auth. |
| Claude credentials | replay-claude-credentials.json |
Claude Code OAuth. Next bootstrap re-authenticates Claude. |
These are independent -- you can wipe Claude creds without losing Adom login, or vice versa.
CSS selectors for virgin reset auth options
label.virgin-opt -- each checkbox row
input[type="checkbox"] -- the toggle (bind:checked to virginOpts.adom_token / virginOpts.claude_token)
.virgin-hint -- shows filename (hydrogen-session.txt / replay-claude-credentials.json)
.vp-badge -- status badge ("exists" / "clean")
.vp-exists -- green badge (file exists)
.vp-clean -- gray badge (file gone)
Profile Menu
Location: Top-right corner of the editor nav bar.
Source: src/lib/components/editor/EditorNav.svelte (lines ~2403-2443)
Click the circular user avatar to open a dropdown with two sections.
Menu items
| Item | Icon | Action |
|---|---|---|
| Your profile | mdi:account-circle-outline |
Navigates to /users/{username} |
| Your repositories | mdi:folder-outline |
Navigates to /users/{username}/repositories |
| Your molecules | mdi:atom |
Navigates to /users/{username}/molecules |
| Log out | mdi:logout |
Confirms, then clears session + redirects to login |
CRITICAL: Logout is ONLY in the profile menu (top-right avatar), NOT in the Adom menu (top-left logo). Do not look for logout in the wrong menu.
CSS selectors
| Element | Selector |
|---|---|
| Profile dropdown container | .profile-dropdown-container |
| Avatar button | .profile-dropdown-button |
| Avatar image | .profile-dropdown-button img |
| Dropdown (open) | .profile-dropdown-content.open |
| Menu items | .profile-dropdown-content .dropdown-link |
| Menu item icons | .dropdown-link span |
CDP eval examples
// Open the profile menu
document.querySelector('.profile-dropdown-button')?.click();
// Click "Your profile"
var links = document.querySelectorAll('.profile-dropdown-content .dropdown-link');
if (links[0]) links[0].click();
// Click "Your repositories"
if (links[1]) links[1].click();
// Click "Your molecules"
if (links[2]) links[2].click();
// Click "Log out"
var logoutBtn = Array.from(document.querySelectorAll('.profile-dropdown-content .dropdown-link'))
.find(function(e) { return e.textContent.includes('Log out'); });
if (logoutBtn) logoutBtn.click();
// Note: this triggers a confirm dialog. To auto-confirm, you need to handle the dialog.
// Read the current user's avatar URL
var avatar = document.querySelector('.profile-dropdown-button img');
avatar ? avatar.src : 'no avatar';
// Read the current username from the avatar alt text
avatar ? avatar.alt : 'unknown';
Logout Flow
- User clicks "Log out" in profile menu.
showConfirm('Are you sure you want to log out?', 'Log Out')-- confirmation dialog.- If confirmed (Tauri mode):
invoke('auth_proxy', {path: '/auth/logout', method: 'DELETE'})-- server-side logoutinvoke('auth_proxy', {path: '/auth/clear-local', method: 'POST'})-- deleteshydrogen-session.txt- Clears auth-related domain preferences from
localStorage['hd-url-routing-prefs']
- Redirects to
/auth/login.
Login Page UI
Route: /auth/login
Tauri mode (auth intent)
- Adom logo + "Log in to Hydrogen" header
- Confirmation code in a monospace box (
.confirmation-code) - Spinner + "Waiting for you to sign in..."
- "Open browser again" button (
.retry-button) - "Continue without login" link at the bottom (
.skip-login a)
Browser mode (email/password)
- Standard form with email + password fields
- "Log In" submit button
- Footer with registration link
Auth intent CSS selectors
| Element | Selector |
|---|---|
| Container | .intent-container |
| Confirmation code | .confirmation-code |
| Spinner | .spinner |
| Status message | .intent-message |
| Hint text | .intent-hint |
| Success message | .intent-success |
| Error message | .intent-error |
| Retry button | .retry-button |
| Replay badge | .replay-badge |
| Skip login link | .skip-login a |
CDP eval on the login page
// Check auth intent state
document.querySelector('.intent-message')?.textContent;
// -> "Waiting for you to sign in..." or "Authenticated! Redirecting..."
// Read the confirmation code
document.querySelector('.confirmation-code')?.textContent;
// Click retry
document.querySelector('.retry-button')?.click();
// Skip login (continue without auth)
document.querySelector('.skip-login a')?.click();
How Auth Affects Setup Steps
The setup/bootstrap process depends on a valid session token:
inject-api-key -- reads the HD-side
hydrogen-session.txt, writes the token into the workspace at/var/run/adom/api-key(mode 644) as the Adom API key. If the token is missing or expired, this step fails and downstream steps (adom-cli, gallia, etc.) cannot authenticate.Claude auth -- uses
replay-claude-credentials.jsonif available; otherwise requires the user to complete the Claude OAuth flow inside the VS Code iframe.
If bootstrap fails on auth, the fix is usually: log in again via the profile menu login page, then re-run bootstrap from the Adom menu.
Control API Auth Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /auth-token |
Read the saved Adom session token |
| POST | /claude-auth |
Start Claude Code OAuth (PKCE) |
| GET | /oauth/callback |
Receives OAuth redirect (auto, do not call) |
| POST | /start-oauth-proxy |
Spin up temp TCP listener for OAuth callback |
| POST | /auth/proxy |
Proxy request to Carbon API with session token |
See the hd-settings skill (API Explorer section) for the full endpoint reference.
Cross-references
- hd-ui -- menu layout, where profile menu lives
- hd-setup -- bootstrap steps that depend on auth
- hd-settings -- API Explorer (auth endpoints tab)
- hd-networking -- control API port (default 47084; dynamic — read ~/.adom/hd-control-url)
- carbon-preferences -- user preferences that require auth
---
name: hd-adom-auth
description: >
Adom auth in HD — how login/logout works (auth intent flow), where the
session token lives, the profile menu, and how the Adom session feeds the
setup steps. For Claude Code auth specifically, see hd-claude-auth.
READ when troubleshooting Adom login failures, session token expiry, the
profile dropdown, or virgin-reset Adom-auth options. Trigger words —
adom login, login, logout, auth, session token, auth intent, confirmation
code, profile menu, avatar, sign in, sign out, hydrogen-session.txt, auth
proxy, clear session, HD adom login, Your profile, Your repositories.
---
# Hydrogen Desktop -- Authentication Reference
## Auth Flow Overview
HD uses an **auth intent** flow (no email/password entry in the app).
The Tauri backend creates a short-lived intent token on the Carbon API,
opens the user's default browser to the Adom login page, and polls until
the user completes sign-in in the browser.
### Sequence
1. HD calls `invoke('auth_intent_start')` -> Rust POSTs to
`https://carbon.adom.inc/auth/intents` with `{"max_age": 7776000}`
(90-day session).
2. Carbon returns `{token, confirmation_code, expires_at, auth_url}`.
3. HD opens the browser to the **`auth_url` Carbon returned**, falling back to
`https://hydrogen.adom.inc/auth/intent?token={token}` (singular `intent`,
token as a query param — NOT `/auth/intents/{token}`).
4. HD displays the confirmation code on screen and shows a spinner.
5. Frontend polls `invoke('auth_intent_poll', {token})` every 2 seconds.
Rust GETs `https://carbon.adom.inc/auth/intents/{token}/status`.
6. When the user confirms in the browser, the response includes
`{state: "authenticated", session_token: "..."}`.
7. Rust saves the session token to `%APPDATA%\hydrogen-desktop\hydrogen-session.txt`
and a replay copy to `%APPDATA%\hydrogen-desktop\replay-session.txt`.
8. Frontend redirects to the dashboard.
### Source files
- **Login page**: `src/routes/auth/login/+page.svelte`
- **Auth Rust backend**: `src-tauri/crates/hd-app/src/lib.rs` (functions
`auth_intent_start`, `auth_intent_poll`, `auth_proxy`,
`check_replay_credentials`, `replay_adom_login`). NOTE: the old
`src-tauri/src/lib.rs` is a pre-crate-split vestige — the live code is in the
`hd-app` crate.
- **Auth components**: `src/lib/components/auth/` (Header, Form, Footer,
SubmitButton, Error, Success)
- **User API**: `src/lib/api/authenticated_user.ts`
---
## Session Token
**Location**: `%APPDATA%\hydrogen-desktop\hydrogen-session.txt`
A plain-text file containing the Adom session cookie value. Created by either:
- The auth intent flow (on successful poll)
- The auth proxy (when Carbon returns a `set-cookie: session_token=...` header)
The auth proxy (`invoke('auth_proxy', ...)`) reads this file and attaches it
as a `Cookie: session_token=...` header on every request to `carbon.adom.inc`.
### Token lifecycle
- **Created**: on login (auth intent or browser set-cookie capture)
- **Refreshed**: automatically when the auth proxy receives a new set-cookie
- **Cleared**: on logout (`auth_proxy({path: "/auth/clear-local"})`) or on
401 response (Rust auto-deletes the file)
- **Max age**: 90 days (set at intent creation)
### Reading the token programmatically
```bash
# From the control API (default 47084; the port is dynamic — read ~/.adom/hd-control-url)
curl "$(cat ~/.adom/hd-control-url)/auth-token"
# From inside the workspace (injected by setup step inject-api-key, mode 644)
# run inside the workspace — WSL2: `wsl -d Adom-Workspace -u adom -- cat /var/run/adom/api-key`; Docker: `docker exec <container> cat /var/run/adom/api-key`
cat /var/run/adom/api-key
```
---
## Auth Proxy
The Tauri command `auth_proxy` is the gateway for all authenticated requests
to the Adom Carbon API. It:
1. Reads the session token from disk
2. Attaches it as a cookie to the outgoing request
3. Captures any `set-cookie` response and updates the saved token
4. On 401, deletes the token and returns `{expired: true}`
5. Supports a special `path: "/auth/clear-local"` to delete the token file
**Base URL**: `https://carbon.adom.inc`
```javascript
// Example: get the current user
const result = await invoke('auth_proxy', {
path: '/api/me',
method: 'GET'
});
// result = { status: 200, data: { id: "...", name: "jlauer12", ... }, ok: true }
```
---
## Credential Replay
On login page mount, HD checks for saved credentials before starting the
full auth intent flow:
1. `invoke('check_replay_credentials')` checks if replay files exist on disk.
2. If `replay-session.txt` exists (has_adom = true), HD shows a "REPLAY"
badge and a 5-second countdown.
3. `invoke('replay_adom_login')` attempts to authenticate using the saved
session token.
4. If replay succeeds, the user is logged in without opening a browser.
5. If replay fails, HD falls back to the normal auth intent flow.
This makes re-login after a virgin reset instant when the user chose to
preserve the Adom session token.
---
## Claude Credentials
**Location**: `%APPDATA%\hydrogen-desktop\replay-claude-credentials.json`
Stores Claude Code OAuth credentials so they can be replayed into the
workspace during bootstrap. The setup step `inject-api-key` reads the
session token to authenticate with the Adom API, and Claude credentials
are injected separately.
### Virgin reset options for auth
The virgin reset panel (Setup Panel -> Virgin Reset) has per-item toggles:
| Toggle | File | What it clears |
|--------|------|----------------|
| Adom session token | `hydrogen-session.txt` | Adom login. Next launch requires re-auth. |
| Claude credentials | `replay-claude-credentials.json` | Claude Code OAuth. Next bootstrap re-authenticates Claude. |
These are independent -- you can wipe Claude creds without losing Adom login,
or vice versa.
### CSS selectors for virgin reset auth options
```
label.virgin-opt -- each checkbox row
input[type="checkbox"] -- the toggle (bind:checked to virginOpts.adom_token / virginOpts.claude_token)
.virgin-hint -- shows filename (hydrogen-session.txt / replay-claude-credentials.json)
.vp-badge -- status badge ("exists" / "clean")
.vp-exists -- green badge (file exists)
.vp-clean -- gray badge (file gone)
```
---
## Profile Menu
**Location**: Top-right corner of the editor nav bar.
**Source**: `src/lib/components/editor/EditorNav.svelte` (lines ~2403-2443)
Click the circular user avatar to open a dropdown with two sections.
### Menu items
| Item | Icon | Action |
|------|------|--------|
| Your profile | `mdi:account-circle-outline` | Navigates to `/users/{username}` |
| Your repositories | `mdi:folder-outline` | Navigates to `/users/{username}/repositories` |
| Your molecules | `mdi:atom` | Navigates to `/users/{username}/molecules` |
| Log out | `mdi:logout` | Confirms, then clears session + redirects to login |
**CRITICAL**: Logout is ONLY in the profile menu (top-right avatar), NOT in
the Adom menu (top-left logo). Do not look for logout in the wrong menu.
### CSS selectors
| Element | Selector |
|---------|----------|
| Profile dropdown container | `.profile-dropdown-container` |
| Avatar button | `.profile-dropdown-button` |
| Avatar image | `.profile-dropdown-button img` |
| Dropdown (open) | `.profile-dropdown-content.open` |
| Menu items | `.profile-dropdown-content .dropdown-link` |
| Menu item icons | `.dropdown-link span` |
### CDP eval examples
```javascript
// Open the profile menu
document.querySelector('.profile-dropdown-button')?.click();
// Click "Your profile"
var links = document.querySelectorAll('.profile-dropdown-content .dropdown-link');
if (links[0]) links[0].click();
// Click "Your repositories"
if (links[1]) links[1].click();
// Click "Your molecules"
if (links[2]) links[2].click();
// Click "Log out"
var logoutBtn = Array.from(document.querySelectorAll('.profile-dropdown-content .dropdown-link'))
.find(function(e) { return e.textContent.includes('Log out'); });
if (logoutBtn) logoutBtn.click();
// Note: this triggers a confirm dialog. To auto-confirm, you need to handle the dialog.
// Read the current user's avatar URL
var avatar = document.querySelector('.profile-dropdown-button img');
avatar ? avatar.src : 'no avatar';
// Read the current username from the avatar alt text
avatar ? avatar.alt : 'unknown';
```
---
## Logout Flow
1. User clicks "Log out" in profile menu.
2. `showConfirm('Are you sure you want to log out?', 'Log Out')` -- confirmation dialog.
3. If confirmed (Tauri mode):
- `invoke('auth_proxy', {path: '/auth/logout', method: 'DELETE'})` -- server-side logout
- `invoke('auth_proxy', {path: '/auth/clear-local', method: 'POST'})` -- deletes `hydrogen-session.txt`
- Clears auth-related domain preferences from `localStorage['hd-url-routing-prefs']`
4. Redirects to `/auth/login`.
---
## Login Page UI
**Route**: `/auth/login`
### Tauri mode (auth intent)
- Adom logo + "Log in to Hydrogen" header
- Confirmation code in a monospace box (`.confirmation-code`)
- Spinner + "Waiting for you to sign in..."
- "Open browser again" button (`.retry-button`)
- "Continue without login" link at the bottom (`.skip-login a`)
### Browser mode (email/password)
- Standard form with email + password fields
- "Log In" submit button
- Footer with registration link
### Auth intent CSS selectors
| Element | Selector |
|---------|----------|
| Container | `.intent-container` |
| Confirmation code | `.confirmation-code` |
| Spinner | `.spinner` |
| Status message | `.intent-message` |
| Hint text | `.intent-hint` |
| Success message | `.intent-success` |
| Error message | `.intent-error` |
| Retry button | `.retry-button` |
| Replay badge | `.replay-badge` |
| Skip login link | `.skip-login a` |
### CDP eval on the login page
```javascript
// Check auth intent state
document.querySelector('.intent-message')?.textContent;
// -> "Waiting for you to sign in..." or "Authenticated! Redirecting..."
// Read the confirmation code
document.querySelector('.confirmation-code')?.textContent;
// Click retry
document.querySelector('.retry-button')?.click();
// Skip login (continue without auth)
document.querySelector('.skip-login a')?.click();
```
---
## How Auth Affects Setup Steps
The setup/bootstrap process depends on a valid session token:
1. **inject-api-key** -- reads the HD-side `hydrogen-session.txt`, writes the
token into the workspace at `/var/run/adom/api-key` (mode 644) as the Adom
API key. If the token is missing or expired, this step fails and downstream
steps (adom-cli, gallia, etc.) cannot authenticate.
2. **Claude auth** -- uses `replay-claude-credentials.json` if available;
otherwise requires the user to complete the Claude OAuth flow inside the
VS Code iframe.
If bootstrap fails on auth, the fix is usually: log in again via the profile
menu login page, then re-run bootstrap from the Adom menu.
---
## Control API Auth Endpoints
| Method | Path | Description |
|--------|------|-------------|
| GET | `/auth-token` | Read the saved Adom session token |
| POST | `/claude-auth` | Start Claude Code OAuth (PKCE) |
| GET | `/oauth/callback` | Receives OAuth redirect (auto, do not call) |
| POST | `/start-oauth-proxy` | Spin up temp TCP listener for OAuth callback |
| POST | `/auth/proxy` | Proxy request to Carbon API with session token |
See the **hd-settings** skill (API Explorer section) for the
full endpoint reference.
---
## Cross-references
- **hd-ui** -- menu layout, where profile menu lives
- **hd-setup** -- bootstrap steps that depend on auth
- **hd-settings** -- API Explorer (auth endpoints tab)
- **hd-networking** -- control API port (default 47084; dynamic — read ~/.adom/hd-control-url)
- **carbon-preferences** -- user preferences that require auth