bootstrap
Adom Hydrogen (platform base)
Public Made by Adomby adom
Layer Hydrogen's workspace onto a standard Adom workstation.
← Commit history
skills/hd-adom-auth/SKILL.mddeleted−326@@ -1,326 +0,0 @@-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