Commit graph

104 commits

Author SHA1 Message Date
Samuel James
b499759936 modified files
Some checks are pending
Deploy to racknerd1 / validate (push) Waiting to run
Deploy to racknerd1 / deploy (push) Blocked by required conditions
2026-06-22 16:10:05 -06:00
Samuel James
08139ff831
Make Appearance light mode work (gray theme) + roadmap GNOME/KDE RDP (#50)
Appearance tab was local-state-only — light mode did nothing. Wire it up:
- index.css: theme color tokens are now CSS variables on :root (dark default)
  with a [data-theme="light"] override using a soft GRAY page background
  (#E4E6EB), not white. The @theme tokens + html/body + select reference the
  vars, so the app shell and any component using bg-page/text-text-primary/etc.
  themes automatically.
- New src/lib/theme.ts: localStorage-backed appearance prefs (theme/fontSize/
  radius/animations) + applyAppearance() toggling data-theme on <html>, mirroring
  the terminalPrefs pattern.
- main.tsx applies saved theme before first render (no flash).
- Settings AppearanceSection persists + applies on change; theme/fontSize/radius/
  animations are live. Dropped the non-functional "Sidebar Expanded by default"
  toggle. (accent color is still cosmetic-only — full token migration of
  hardcoded-hex pages is a separate task, noted in ROADMAP.)

Also adds the Remote Desktop GNOME & KDE support work to ROADMAP as an add-on
(XFCE confirmed working; GNOME needs a FreeRDP-3 guacd image, KDE via xrdp +
startplasma-x11). Full detail in docs/rdp-debug-handoff.md.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-22 16:39:50 -04:00
Samuel James
8ccc959dc9
Wire mouse + keyboard input for RDP sessions (#47)
The multi-session RemoteDesktop view rendered the remote display but never
captured input — no Guacamole.Mouse / Guacamole.Keyboard were created — so the
desktop showed but the mouse and keyboard did nothing.

Add per-session input:
- Guacamole.Mouse on the display element, forwarding mouse state via
  client.sendMouseState with coordinates divided by the current display scale so
  clicks land correctly on the scaled-down canvas.
- Guacamole.Keyboard on document, forwarding key events via client.sendKeyEvent,
  but only while that session is the active/visible tab so a background session
  can't steal keystrokes.
- Detach keyboard handlers on session close and on unmount.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-22 16:00:09 -04:00
Samuel James
58a46553d9
Fix RDP session drop/flicker: stop re-parenting Guacamole display (#45)
Symptom: desktop flickers in for a moment then goes blank while the tab still
says "connected"; guacd logs "User is not responding" and the client reconnects
in a loop.

Root cause: the multi-session view moved each session's display element between
DOM nodes on tab switch (host.innerHTML='' + appendChild). Detaching a
guacamole-common-js display from the DOM stalls its sync loop, so the client
stops echoing guacd's sync instructions and guacd drops it as unresponsive.
Proven out-of-band: a raw guacd client that echoes sync held a connection for
30s/116 syncs with no drop, while the browser dropped within seconds.

Fix: mount each session's container into the display host ONCE and never move
it; toggle visibility (display:none) to switch tabs so every session's display
stays in the DOM and its sync loop keeps running. Containers are absolutely
positioned in a relative host; close still removes the container.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-22 15:31:43 -04:00
Samuel James
a7fbbabeb2
Fix blank RDP screen in multi-session view: scale display + fit on activate (#44)
The multi-session RemoteDesktop tabs (05e78f0) appended the Guacamole display
into an off-DOM container before connecting, and never called display.scale().
Frames arrived while the canvas was detached/zero-sized, so the desktop rendered
but painted to an invisible area — connected-but-blank.

Fix: track the display per session, fit/scale it to the visible panel when a
session becomes active, on the display's own onresize, and on window resize.
Verified the VM/xrdp/guacd side streams frames fine (13-36 img frames in direct
guacd tests); this was purely the client-side mount/scale regression.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-22 15:12:00 -04:00
Claude
05e78f0fa5
Support multiple remote desktop sessions as tabs with disconnect controls 2026-06-22 18:44:42 +00:00
Claude
9578820bbd
Add RDP security mode override for hosts that reject auto-negotiated security 2026-06-22 13:26:06 +00:00
Claude
03159ecde4
Match SSH connection name to specific icon (e.g. Linode, Portainer) before falling back to generic Linux icon 2026-06-21 20:13:18 +00:00
Claude
437f14e330
Add icon candidates for proxmox, weather, remote_desktop integrations 2026-06-21 20:11:34 +00:00
Claude
ba8683f5a8
Add per-integration custom icon + multi-repo fallback chain for Node Status
Settings → Integrations now has a universal "Icon" field (any integration
type) accepting a pasted URL or an uploaded image, stored as config.iconUrl.
This overrides the built-in icon for that integration's Node Status tile.

Node Status tiles now resolve their icon through a priority chain: custom
iconUrl, then each built-in CDN candidate in order (assets-public first
where available, falling back to the existing dashboard-icons CDN), and
finally the generic per-kind Lucide icon if every candidate 404s. AWS now
tries samuelsjames.github.io/assets-public's aws-logo.svg before the
jsDelivr fallback; SSH gets a Linux logo from the same repo. Proxmox,
Weather, and Remote Desktop have no built-in candidates yet (no matching
assets in that repo) and fall back to the generic icon until added.
2026-06-21 20:11:34 +00:00
Samuel James
9f10e8ee6f
Group all integration node tiles by integration except Proxmox (#39)
Generalizes the Uptime Kuma monitor-grouping pattern to every integration:
Node Status now collapses each integration's resources into one tile (e.g.
30 EC2 instances under one "AWS" tile) instead of flooding the grid, with
members listed in Node Detail on selection. Proxmox stays ungrouped since
its VMs/LXCs are managed individually elsewhere in the app.

Adds integrationType to the /api/integrations/resources response so the
frontend can group/exclude by adapter type rather than resource kind (kind
alone can't distinguish Proxmox VMs from AWS VMs, for example).

Documents the grouping rule in HANDOFF.md and adds a paid-tier roadmap
entry for per-integration node tabs that will show every individual node.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-21 09:35:55 -04:00
Claude
d50ec0076e
Group monitoring-app resources into one Node Status tile per integration
Integrations whose resources represent many sub-items (Uptime Kuma's
monitors) now collapse into a single tile using the Uptime Kuma CDN
icon, instead of flooding Node Status with one tile per monitor.
Selecting that tile lists every underlying monitor's status in a
scrollable Node Detail panel, so hundreds of monitors stay manageable.

Also drops the temporary debug logging added while diagnosing the
listener-timing bug, now that real monitor/heartbeat data confirmed
coming through.
2026-06-21 13:00:50 +00:00
Claude
0b9acd32a5
Fix Node Status grid not scrolling when it overflows
The grid's flex column parent had no min-h-0, so it grew to fit all
content instead of being capped to the card height — overflow-y:auto
on the grid itself never had anything to scroll within.
2026-06-21 12:56:54 +00:00
Claude
58f007e6db
Add per-resource kind labeling and proper Node Status icons
Each adapter now tags its Resources with a kind (vm, container, app,
host, network) so Node Status tiles show the right icon instead of a
generic server glyph — Proxmox LXCs/Docker containers get a container
icon, VMs get a VM icon, Uptime Kuma monitors get an app icon, etc.

Also stops silently swallowing listResources() failures — they're now
logged as warnings, since a connected-but-empty integration (e.g.
Uptime Kuma reporting zero monitors) was previously indistinguishable
from a real adapter error.
2026-06-21 12:03:25 +00:00
Claude
8a60279720
Add selectable node status tiles and a Node Detail card
Clicking a tile in Node Status highlights it and surfaces its name,
source integration, status, and detail in a new Node Detail card on
the bottom row, between Integration Health and a now-narrower Recent
Activity card.
2026-06-21 11:43:10 +00:00
Claude
99ca1ba39c
Tighten Integration Health row spacing and make the list scrollable
Status text now sits close to the name instead of being pushed to the
far edge, and the list caps at a fixed height with scroll so it stays
usable as more integrations are added.
2026-06-21 11:38:16 +00:00
Claude
bcdecd86d6
Merge branch 'claude/youthful-cerf-ibvxfb' 2026-06-21 11:03:38 +00:00
Claude
bbb26dab0d
Implement real Uptime Kuma monitor reporting via Socket.IO
Uptime Kuma has no REST API for monitor data; connect over the same
Socket.IO session the web UI uses (login, then read monitorList and
heartbeat events) so connected monitors now surface as Resources.
Switches the integration's credentials from an API key to
username/password, matching what Uptime Kuma's session login expects.
2026-06-21 11:00:47 +00:00
Claude
d0cd9d83a7
Force explicit grid gap + clip panes to fix missing row gap in 4-pane view
Use an explicit inline gap instead of relying solely on a dynamically
templated Tailwind class, and clip each pane's content (overflow-hidden)
so a pane can't visually bleed into the row gap below it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hu9pZvJY4BgmcQeAw2ugk
2026-06-21 10:16:18 +00:00
Claude
f191119a28
Add breathing room around terminal pane borders and content
Wider gap between panes in split view (borders were crowding each other
in 4-up grid), more inset between the pane border and the connect/
disconnect header, and a small horizontal gutter around the xterm mount
so the prompt text doesn't sit flush against the border.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hu9pZvJY4BgmcQeAw2ugk
2026-06-21 09:54:40 +00:00
Claude
4674f4e209
Fix Help page scroll, widen Connected Integrations grid, color-code System Status ring
- Help page now scrolls (it sat under a clipped section with no overflow handling).
- Connected Integrations on Glance shows 5 per row in a scrollable area with the
  transparent ghost scrollbar, instead of growing the card unbounded.
- System Status KPI ring is now a thicker, vertically centered, multi-segment
  donut broken down by integration type, each type colored consistently from a
  shared first-come-first-served palette (src/lib/integrationColors.ts) so e.g.
  whichever type connects first always gets the same color everywhere it's used.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hu9pZvJY4BgmcQeAw2ugk
2026-06-21 09:21:56 +00:00
Claude
a964591431
Add in-app shell prompt picker: install Starship + Nerd Font on demand
Lets a user pick one of three Starship presets (Nerd Font Symbols,
Pastel Powerline, Tokyo Night) from the Terminal page and install
Starship + a Nerd Font on the active pane's SSH host with one click,
instead of running a script by hand. Idempotent on the host side, and
available to all authenticated users like the rest of the SSH/Docker
tooling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hu9pZvJY4BgmcQeAw2ugk
2026-06-21 09:10:30 +00:00
Claude
f41b700904
Add Nerd Font icon fallback to the Terminal so Starship-style prompts render correctly
Bundles Symbols Nerd Font Mono (MIT, ryanoasis/nerd-fonts) as a glyph-only @font-face and appends it to every Terminal font-family option, so distro icons / git branch glyphs / etc. from prompts like Starship show up instead of broken-glyph boxes. It carries no letterforms, so it never changes how normal text renders.
2026-06-21 09:00:39 +00:00
Claude
9372767443
Make Node Status card scrollable with a 5-column layout and invisible-by-default scrollbar 2026-06-21 08:52:58 +00:00
Claude
98b69e47d5
Improve Containers table/tab readability: bold centered headers, taller rows, filing-cabinet tabs 2026-06-21 08:47:52 +00:00
Claude
36a79abe94
Expand Help page with quick-start guide and real-world examples
Adds a quick-start ordering card and per-feature example callouts (with icons) so first-time users see concrete use cases, not just descriptions.
2026-06-21 08:28:54 +00:00
Claude
628187befb
Show a host-specific Docker remote-API setup script in Settings
When adding/editing a Docker integration with a tcp:// or http:// remote
URL, display a copyable systemd override + curl verification script
scoped to the entered host:port, so enabling the daemon's API doesn't
require looking up the steps separately.
2026-06-20 22:15:43 +00:00
Claude
4a4a5a01b3
Add Mesh section to Settings for configuring/testing the mesh gate
Admins can now toggle mesh.required, run verify/override, and see
current mesh status entirely from the app, without hitting the API
directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hu9pZvJY4BgmcQeAw2ugk
2026-06-20 21:44:27 +00:00
Claude
800072ffbb
Add reachability fallback for routed meshes (VPC peering, etc.)
A host can be on the mesh's "side" of a routed network (e.g. a VPC peered
into a NetBird/WireGuard mesh) without holding a local IP in the mesh's
own CIDR. Local-IP-in-CIDR stays the primary check; if it fails, the admin
can supply a known peer/gateway IP on the mesh and we verify by pinging
it instead. Adds iputils to the backend image for the ping binary.
2026-06-20 21:26:25 +00:00
Claude
0409159327
Make mesh verification universal (CIDR check, not NetBird-specific)
Replace the NetBird-adapter-based "reachable" check with a vendor-agnostic
one: the admin supplies the mesh's IP range (CIDR), and verification just
confirms this host has an address inside it. Works identically for
NetBird, WireGuard, ZeroTier, Tailscale, or any other mesh tech, with no
integration record or vendor API call required.
2026-06-20 21:22:06 +00:00
Claude
46d95fca61
Add mesh prerequisite gate (NetBird verification before app config)
Implements the design in docs/mesh-prerequisite-gate.md per the user's
DECIDE A-D answers: a permanent admin override, B1 (reachable) verification
with host mesh IP shown informationally, members allowed in with a notice
instead of being blocked, and mesh.required defaulting off so the live
production instance is unaffected.

- system_config kv table + getConfig/setConfig helpers
- /api/system/mesh-status, /mesh/verify, /mesh/override, /mesh/required
- AuthContext gains a 'needs-mesh' status (admins only) and exposes
  meshStatus for a member-facing banner
- MeshGate page reuses the integration create+test flow to connect NetBird
2026-06-20 21:15:55 +00:00
Samuel James
35fd7fc703
Add Docker-over-SSH management and push-agent monitoring (#31)
Expands the Containers feature with two new ways to see and manage Docker
containers without exposing the Docker Engine TCP socket, plus the docs and
roadmap entries that frame them.

Docker over SSH (management):
- Runs the `docker` CLI on a remote SSH host instead of talking to the Engine
  TCP API, reusing the existing SSH transport (jump-host chaining, host-key
  verification, key/password auth) via connectTarget + execCommand. No dockerd
  socket has to be exposed — the mesh + SSH auth are the gate.
- backend/src/ssh/docker.ts: list/logs/start/stop/restart/pause/unpause/remove
  and an interactive `docker exec` shell builder. Container refs are validated
  against a strict allowlist and single-quoted to prevent command injection;
  action verbs are whitelisted.
- backend/src/routes/dockerSsh.ts: REST routes mirroring the TCP Docker API
  shape (mutating actions gated by adminOnly) + a /api/docker-ssh/exec
  WebSocket modeled on the terminal PTY plumbing.
- Note: the SSH path uses the ssh2 key/password auth; it does not implement the
  OpenSSH-certificate (OPKSSH) fallback that the terminal route has.

Docker push-agent monitoring (self-hosted, read-only):
- A small bash agent (agent/archnest-docker-agent.sh) runs on each Docker VM,
  collects a rich snapshot (docker ps + inspect + a stats snapshot), masks
  secret-looking env values locally, and POSTs it to ArchNest. VMs need
  outbound-only mesh access — no exposed port, no SSH for monitoring.
- backend/src/routes/agents.ts: token-gated ingest
  (POST /api/agents/docker/report, ARCHNEST_AGENT_TOKEN, constant-time compare;
  503 when unset, so it is disabled by default) plus user-auth read endpoints
  (hosts list with staleness flag, per-host containers, single-container
  detail). New docker_agent_reports table (latest report per host).
- Ingest stores data only; it never executes anything from the agent.

Containers page:
- Host selector now spans Docker API, SSH, and Agent sources.
- Intra-page tabs: a Containers list plus dynamic, closeable per-container
  detail tabs opened by clicking a container name. Agent detail shows
  overview/state/stats/ports/networks/mounts/env(masked)/labels; docker/ssh
  degrade gracefully. Agent rows are read-only; docker/ssh keep management.

Docs/roadmap:
- docs/docker-agent-monitoring.md (design doc, written before implementation).
- ROADMAP.md: LXC management (paid), Docker monitoring agent tiering
  (push self-hosted now / pull-agent paid), terminal grid tiering.

Deferred (documented, not built here): the mesh-prerequisite setup gate, the
paid pull-agent (Option 2), per-host tokens, time-series metrics.

Requires ARCHNEST_AGENT_TOKEN in the backend env to enable agent ingest.
Verified: backend `tsc --noEmit` and frontend `tsc -b && vite build` both pass;
agent jq filters, byte conversion, and `bash -n` checked locally.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-20 16:24:57 -04:00
Samuel James
b836ac1a02
Keep SSH terminal sessions connected across page navigation (#30)
The Terminal page held all session state (xterm instances and their
WebSockets) in component-local React state. Because it renders as a
`<Route element={<Terminal />}>`, navigating away unmounted it and ran
the xterm cleanup (`term.dispose()` + `ws.close()`), tearing down every
SSH session. Returning to the page reconnected from scratch, losing
scrollback and any running work.

Lift terminal sessions into a `TerminalSessionProvider` mounted above the
router (in `main.tsx`, inside `AuthProvider`). The provider owns each
pane's xterm instance, fit addon, WebSocket, and a persistent wrapper DOM
node. Wrappers live in a hidden container at the app root; the Terminal
page re-parents them into its grid on mount and moves them back to the
hidden root on unmount instead of disposing — so the xterm + WebSocket
keep running in the background across route changes.

Disconnect semantics: closing a tab/pane (or shrinking the 1/2/4 grid)
destroys those sessions; logout tears down all sessions. A full browser
reload still drops connections (the WebSocket dies with the page) — this
persists across in-app navigation only.

Shared terminal constants/types/prefs are split into a non-component
module (`src/lib/terminalPrefs.ts`) so the context file stays a clean
component module.

Also document the terminal window grid-view tiering in ROADMAP.md
(self-hosted = 4-window cap, current; paid = as many as fit on screen,
planned for the AWS deployment), and realign HANDOFF/README/design-docs
to reflect that auth Phase 3 (multi-user) shipped and Phase 4 (SSO) is
deferred to a paid AWS add-on.

Verified with a clean `tsc -b && vite build` (frontend) and
`tsc --noEmit -p .` (backend).

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-20 15:02:50 -04:00
Samuel James
7f354e54ab
Fix missing ManagedUser type import breaking the frontend build (#29)
The Phase 3 Users section uses the ManagedUser type but it was never
added to the import from ../lib/api, which broke the CI frontend build
(`tsc -b && vite build`: "Cannot find name 'ManagedUser'"). A local
`tsc --noEmit` had passed off a stale incremental build cache and missed
it. Add the type to the existing import; verified with a clean
`tsc -b && vite build`.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-20 12:47:24 -04:00
Samuel James
d863448495
Add auth Phase 3: multi-user accounts with admin/member roles (#28)
Implements Phase 3 of the auth roadmap: multiple user accounts (cap 10),
an admin/member role model, and admin-only gating of config-mutating
routes. Dashboard data stays shared across all users (per the product
decision in HANDOFF.md — this is a household/self-hosted dashboard, not
a multi-tenant app), so there is no per-user data isolation.

Schema (backend/src/db/index.ts):
- Idempotent migration adds `role` (default 'admin') and `active`
  (default 1) columns to `users` when missing. The 'admin' default means
  the pre-existing single user is backfilled to admin on deploy and keeps
  full access; newly created users are inserted explicitly as 'member'.
  Verified against a production-like old schema (columns added, existing
  user backfilled to admin/active).

Auth + access control:
- `/api/setup` creates the first user as admin. Login enforces `active`
  (deactivated accounts get 403) and embeds the live role in the session.
- `app.authenticate` now reads role+active fresh from the DB on every
  request (not from the possibly-stale JWT claim), rejects inactive
  accounts, and stashes the role on req.user.
- New `requireAdmin` (auth + role check) and `adminOnly` (role check for
  routes already behind the plugin-level authenticate hook) decorators.

User management (admin-only, in auth.ts):
- GET/POST/PUT/DELETE /api/users — list, create (admin sets a temp
  password; no public signup), change role, activate/deactivate, delete.
- 10-user cap enforced server-side; guard rails prevent removing the last
  active admin (demote/deactivate/delete) and deleting your own account;
  deactivating or deleting a user drops their sessions immediately.

Admin-only route gating (members get 403):
- integrations create/update/delete/test, tunnels create/delete, data
  export/import. Read routes and tunnel connect/disconnect stay open to
  all authenticated users, as do all the SSH/Docker/RDP tools and
  bookmarks (members are trusted to use the tooling, per product decision).

Frontend:
- api.ts: listUsers/createUser/updateUser/deleteUser + ManagedUser type;
  role+active added to AuthUser.
- Settings: new admin-only "Users" section (create form, role toggle,
  activate/deactivate, delete, 10-cap indicator). Nav filters the Users
  tab by role and guards ?tab= deep-links. Data & Backup shows an
  admin-only notice for members; Integrations shows a read-only banner
  for members. (Backend remains the real enforcement boundary.)

Verified end-to-end against a throwaway backend: role assignment,
member 403s on every admin-only route + 200s on shared/read routes,
admin 200/201s, last-admin guards (409/400), deactivation killing an
active session and blocking re-login (then reactivation restoring it),
and the 10-user cap (409 on the 11th). Both frontend and backend
type-check clean.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-20 12:43:24 -04:00
Samuel James
2ccc7b82d7
Add auth Phase 2: password change, sessions, and login audit log (#27)
Builds out the Settings → Security tab (previously a "coming soon"
placeholder) and the backend behind it. Still single-user; multi-user
and SSO remain Phases 3-4.

Backend:
- New `sessions` table (id, user_id, user_agent, ip, created_at,
  last_seen_at) and `login_events` table (user_id, username, ip,
  user_agent, success, created_at).
- Login and setup now mint a session row and embed its id as a `sid`
  claim in the JWT. The `authenticate` hook validates that the session
  still exists (and bumps last_seen_at), so revoking a session genuinely
  invalidates its token instead of relying on the JWT signature alone.
  Tokens minted before sessions existed have no `sid` and stay valid
  until expiry, for backward compatibility.
- Every login attempt (success and failure) is recorded in login_events
  for the audit trail.
- New endpoints: PUT /api/auth/password (verifies current via bcrypt,
  hashes new at cost 12, revokes all *other* sessions on success),
  GET /api/auth/sessions, DELETE /api/auth/sessions/:id (can't revoke
  the current one), POST /api/auth/logout (revokes current session),
  GET /api/auth/login-events?limit.
- AuthContext.logout() now calls POST /api/auth/logout best-effort so
  signing out revokes the server session, not just the local token.

Frontend:
- SecuritySection: change-password form (current/new/confirm with
  show/hide and client-side validation), active-sessions list (device
  description from user-agent, IP, last-seen relative time, per-session
  "Sign out" for non-current sessions), and a recent login-activity feed
  (success/failure dot, user, IP, relative time).
- api.ts: changePassword/listSessions/revokeSession/logout/
  listLoginEvents + AuthSession/LoginEvent types.

Verified end-to-end against a throwaway backend instance: session
creation, second-device session, failed-login logging, cross-session
revocation invalidating the revoked token, password change keeping the
current session alive while revoking others, and logout invalidating the
current session. Frontend + backend both type-check clean.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-20 11:50:56 -04:00
Samuel James
ea14486f6e
Wire up Profile/Appearance/Security in user menu (#21)
* Add editable display-name field to generic integrations

Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop,
Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate
from the host/IP field, mirroring the SSH host rename pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Surface the new-integration name field as a labeled input

The name field for new generic integrations was a faint header input
with only placeholder text, easy to miss. Move it into the form grid
as a proper labeled "Name" field next to the other connection fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Add file upload for SSH private key and certificate fields

Lets users pick a key file from disk (e.g. ~/.ssh) instead of pasting
its contents into the Private Key / OPKSSH Certificate fields.

* Fix SSH private key paste corrupting multi-line PEM format

Private Key and Certificate fields were single-line <input> elements,
which strip newlines on paste and corrupt PEM-formatted keys (causing
'Unsupported key format' errors). Render them as multi-line textareas
instead so pasted keys keep their line breaks.

* Add JSON-converted bookmark import file for Archnest data import

Converts homarr-bookmarks.md into the format expected by /api/data/import.

* Auto-populate bookmark icons via favicon service in import JSON

Each bookmark now points to Google's favicon endpoint for its domain
instead of having no icon at all.

* Wire up Profile/Appearance/Security in the user menu

Profile, Appearance, and Security were dead "#" links. Added URL-based
tab deep-linking to the Settings page (?tab=profile|appearance|security|...)
and pointed the menu items at it. Added a Security tab placeholder ahead
of password/sessions/login-log/SSO work.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 09:44:43 -04:00
Samuel James
d9d9f3f610
Add bulk delete-all for bookmarks (#20)
* Add editable display-name field to generic integrations

Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop,
Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate
from the host/IP field, mirroring the SSH host rename pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Surface the new-integration name field as a labeled input

The name field for new generic integrations was a faint header input
with only placeholder text, easy to miss. Move it into the form grid
as a proper labeled "Name" field next to the other connection fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Add file upload for SSH private key and certificate fields

Lets users pick a key file from disk (e.g. ~/.ssh) instead of pasting
its contents into the Private Key / OPKSSH Certificate fields.

* Fix SSH private key paste corrupting multi-line PEM format

Private Key and Certificate fields were single-line <input> elements,
which strip newlines on paste and corrupt PEM-formatted keys (causing
'Unsupported key format' errors). Render them as multi-line textareas
instead so pasted keys keep their line breaks.

* Add JSON-converted bookmark import file for Archnest data import

Converts homarr-bookmarks.md into the format expected by /api/data/import.

* Auto-populate bookmark icons via favicon service in import JSON

Each bookmark now points to Google's favicon endpoint for its domain
instead of having no icon at all.

* Add bulk delete-all for bookmarks

Adds DELETE /api/bookmarks to clear every bookmark in one request, and a
"Delete All" button (with confirmation) on the BookNest page so re-imports
don't require deleting dozens of entries one at a time.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 09:09:44 -04:00
Samuel James
ad2cfe808c
Default-collapse already-configured SSH hosts on page load (#19)
* Add editable display-name field to generic integrations

Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop,
Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate
from the host/IP field, mirroring the SSH host rename pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Surface the new-integration name field as a labeled input

The name field for new generic integrations was a faint header input
with only placeholder text, easy to miss. Move it into the form grid
as a proper labeled "Name" field next to the other connection fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Add file upload for SSH private key and certificate fields

Lets users pick a key file from disk (e.g. ~/.ssh) instead of pasting
its contents into the Private Key / OPKSSH Certificate fields.

* Fix SSH private key paste corrupting multi-line PEM format

Private Key and Certificate fields were single-line <input> elements,
which strip newlines on paste and corrupt PEM-formatted keys (causing
'Unsupported key format' errors). Render them as multi-line textareas
instead so pasted keys keep their line breaks.

* Default-collapse already-configured SSH hosts on page load

Previously every SSH host card reset to expanded on each page visit,
showing blank secret fields that looked like saved keys had been
deleted. Hosts with saved secrets now start collapsed on first load.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 09:00:04 -04:00
Samuel James
63adccb1c7
Show saved indicator for secret fields instead of appearing deleted (#18)
* Add editable display-name field to generic integrations

Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop,
Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate
from the host/IP field, mirroring the SSH host rename pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Surface the new-integration name field as a labeled input

The name field for new generic integrations was a faint header input
with only placeholder text, easy to miss. Move it into the form grid
as a proper labeled "Name" field next to the other connection fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Add file upload for SSH private key and certificate fields

Lets users pick a key file from disk (e.g. ~/.ssh) instead of pasting
its contents into the Private Key / OPKSSH Certificate fields.

* Fix SSH private key paste corrupting multi-line PEM format

Private Key and Certificate fields were single-line <input> elements,
which strip newlines on paste and corrupt PEM-formatted keys (causing
'Unsupported key format' errors). Render them as multi-line textareas
instead so pasted keys keep their line breaks.

* Show saved indicator for secret fields instead of appearing blank/deleted

GET /api/integrations never returns decrypted secret values (by design),
so after navigating away and back, secret/key fields rendered empty -
looking exactly like the saved key had been deleted, even though it was
still intact and encrypted in the database. Expose which secret keys
exist (names only, never values) via secretKeys, and use it to label
fields as "saved" with an appropriate placeholder instead of blank.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 08:53:56 -04:00
Samuel James
5a3e4c51f9
Fix integration save data loss; add SSH host card collapse (#16)
* Add editable display-name field to generic integrations

Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop,
Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate
from the host/IP field, mirroring the SSH host rename pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Surface the new-integration name field as a labeled input

The name field for new generic integrations was a faint header input
with only placeholder text, easy to miss. Move it into the form grid
as a proper labeled "Name" field next to the other connection fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Add file upload for SSH private key and certificate fields

Lets users pick a key file from disk (e.g. ~/.ssh) instead of pasting
its contents into the Private Key / OPKSSH Certificate fields.

* Fix SSH private key paste corrupting multi-line PEM format

Private Key and Certificate fields were single-line <input> elements,
which strip newlines on paste and corrupt PEM-formatted keys (causing
'Unsupported key format' errors). Render them as multi-line textareas
instead so pasted keys keep their line breaks.

* Fix integration save wiping untouched config fields

The PUT /api/integrations/:id route fully overwrites config_json with
whatever config object is sent (no merge), but buildPayload only
included fields the user had actually edited. Saving after editing
just one field (e.g. pasting a new SSH key) silently dropped every
other config field. Merge the existing integration's config into the
payload before sending.

* Add collapse/expand for SSH host cards

Click the chevron to collapse a host's card once it's configured.
Collapsed cards keep all field state in memory (just hidden), and
auto-collapse after a successful Save.

* Install openssh-client in backend image for certificate-auth SSH

Certificate-based SSH connections shell out to the system ssh binary
via node-pty (ssh2 has no OpenSSH certificate support), but the
alpine runtime image never installed openssh-client. This caused
'execvp(3) failed: No such file or directory' for any host with an
OPKSSH certificate configured.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 08:30:21 -04:00
Samuel James
b2b4709abe
Add file upload for SSH private key and certificate fields (#15)
* Add editable display-name field to generic integrations

Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop,
Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate
from the host/IP field, mirroring the SSH host rename pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Surface the new-integration name field as a labeled input

The name field for new generic integrations was a faint header input
with only placeholder text, easy to miss. Move it into the form grid
as a proper labeled "Name" field next to the other connection fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Add file upload for SSH private key and certificate fields

Lets users pick a key file from disk (e.g. ~/.ssh) instead of pasting
its contents into the Private Key / OPKSSH Certificate fields.

* Fix SSH private key paste corrupting multi-line PEM format

Private Key and Certificate fields were single-line <input> elements,
which strip newlines on paste and corrupt PEM-formatted keys (causing
'Unsupported key format' errors). Render them as multi-line textareas
instead so pasted keys keep their line breaks.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 08:11:32 -04:00
Samuel James
c23724bade
Add explicit Host Name field for SSH hosts (#14)
* Add editable display-name field to generic integrations

Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop,
Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate
from the host/IP field, mirroring the SSH host rename pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Surface the new-integration name field as a labeled input

The name field for new generic integrations was a faint header input
with only placeholder text, easy to miss. Move it into the form grid
as a proper labeled "Name" field next to the other connection fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Add explicit Host Name field for SSH hosts

The SSH host name was only editable via a small inline header input,
not a clearly labeled form field like the other options (Host / IP,
Port, etc). Add a proper "Host Name" labeled field to both existing
and new SSH host forms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 07:48:22 -04:00
Samuel James
39c94e4045
Add editable name field to generic integrations (#13)
* Add editable display-name field to generic integrations

Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop,
Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate
from the host/IP field, mirroring the SSH host rename pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

* Surface the new-integration name field as a labeled input

The name field for new generic integrations was a faint header input
with only placeholder text, easy to miss. Move it into the form grid
as a proper labeled "Name" field next to the other connection fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 07:40:03 -04:00
Samuel James
7de7c1c84e
Allow renaming SSH hosts in Settings (#12)
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 07:23:22 -04:00
Samuel James
20ebd3891b
Use network-traffic background for Integration Health card (#11)
* Use archnest-network-traffic-bg for Connected Integrations card background

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF

* Use archnest-network-traffic-bg for Integration Health card

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 07:15:32 -04:00
Samuel James
a453342de4
Use archnest-network-traffic-bg for Connected Integrations card background (#10)
Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 07:02:38 -04:00
Samuel James
b11150d286
Add background images to Connected Integrations and Integration Health cards; fix Node Status scroll clipping (#9)
Both cards were flat with no background frame. Node Status used a centered grid inside an overflow-hidden card, which clipped rows beyond the visible area instead of letting them scroll into view.


Claude-Session: https://claude.ai/code/session_01BbJV5nm8KPVH1oNJYKpnoF

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 06:54:21 -04:00
Samuel James
0cf53fc1f1
Merge pull request #6 from SamuelSJames/claude/wonderful-faraday-qxym5t
Support multiple integration instances and add credential field hints
2026-06-20 06:20:28 -04:00
Claude
9157a2647a
Support multiple integration instances and add credential field hints
Proxmox, Docker, AWS, and Remote Desktop now allow adding multiple instances, each with its own Save/Test/Remove. Credential fields like Proxmox's API token now show inline guidance on the required format instead of a bare label.
2026-06-20 10:18:04 +00:00