docs: realign design docs with deployed app, consolidate, rewrite README (#24)
* 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. * docs: realign design docs with the deployed app, consolidate, rewrite README README.md was badly stale (listed Terminal as "pending/on hold" and only 5 pages, when all 11 pages are built and live). Rewrote it as a detailed, accurate map of the architecture, every page, every backend route, every integration adapter, and the SSH subsystem, written explicitly for this repo's actual audience (the owner + future AI sessions, never the public). Deleted archnest-blueprint.md and glance.md: both were pre-backend mockup specs describing fictional config files (systems.config, infra.config, fail2ban-driven security scoring) and placeholder data that never matched the real implementation, and conflicted with the deployed app's actual page count/nav/data sources. Their still-true content (color palette, dropdown menu shape, card styling) was folded into design-decisions.md. Rewrote design-decisions.md's "Page-Specific Notes" into a full "Page Notes" section covering all 11 pages plus Login/Enrollment (previously only 4 pages had notes, and those didn't reflect later changes like Files/Tunnels/Containers/Host Metrics/Remote Desktop shipping). Each section now states the real data source per page so it can't drift from the code silently again. 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>
This commit is contained in:
parent
2229a8254a
commit
d8286ac42b
4 changed files with 429 additions and 984 deletions
200
README.md
200
README.md
|
|
@ -1,23 +1,128 @@
|
|||
# ArchNest
|
||||
|
||||
A self-hosted ops dashboard — infrastructure monitoring, a bookmark hub for your homelab/cloud links, an embedded terminal, and system settings, all in one place. Real backend, real integrations, no mock data.
|
||||
A self-hosted ops dashboard for a homelab/cloud setup: live infrastructure
|
||||
monitoring across 9 real integration types, a categorized bookmark hub, a
|
||||
full SSH suite (terminal, tunnels, file manager, host-to-host transfer, live
|
||||
host metrics), Docker container management, and RDP/VNC/Telnet remote desktop
|
||||
— all in one app, with zero mock data anywhere.
|
||||
|
||||
Frontend: React 19 + TypeScript + Vite, styled with Tailwind CSS v4, charts via Recharts, icons via Lucide React.
|
||||
Backend: Fastify + TypeScript + SQLite (`better-sqlite3`), JWT auth, AES-256-GCM encrypted integration secrets.
|
||||
**This repo is private and will never be public.** This README is written for
|
||||
the owner and for any AI session picking up the project cold — it should be
|
||||
detailed enough that neither needs to re-derive context from scratch.
|
||||
|
||||
**For a full handoff/status writeup (what's done, what's not, how to resume), see [`HANDOFF.md`](./HANDOFF.md).**
|
||||
## What this is, in one paragraph
|
||||
|
||||
ArchNest replaced a Homarr-style bookmark dashboard plus a handful of
|
||||
disconnected admin tools (Proxmox UI, Portainer, separate SSH terminals,
|
||||
WinSCP-equivalents) with one app that talks directly to the underlying
|
||||
systems. It started as a 6-page mockup/portfolio piece and has since grown
|
||||
into an 11-page real tool with a real Fastify backend, real SSH/Docker/cloud
|
||||
integrations, and no synthetic data — every number on every page comes from
|
||||
a live API call, a SQLite-backed table, or an SSH command run against a
|
||||
managed host.
|
||||
|
||||
## Current state & direction
|
||||
|
||||
**Live and deployed** at `archnest.snsnetlabs.com`, auto-deploying on every
|
||||
merge to `main` via `.github/workflows/deploy.yml`. All 11 pages and their
|
||||
backend routes are built and working — there is no pending/on-hold page.
|
||||
The active area of work is **the auth system**: the user menu's
|
||||
Profile/Appearance/Security links were fixed in Phase 1; Phases 2-4
|
||||
(password change + sessions + login audit log, multi-user accounts,
|
||||
Authentik SSO) are planned but not started — see `HANDOFF.md` for the full
|
||||
phase breakdown and exactly where to resume.
|
||||
|
||||
If you're a fresh AI session: read this file, then `HANDOFF.md` (current
|
||||
task state + standing workflow rules), then `design-decisions.md` (visual
|
||||
conventions + accurate per-page implementation notes), then
|
||||
`TERMIX_MIGRATION.md` (history of how the SSH/Docker/Guacamole feature set
|
||||
was built) if you need historical context on a specific feature.
|
||||
|
||||
## Pages
|
||||
|
||||
| Page | Route | Status |
|
||||
|------|-------|--------|
|
||||
| Glance | `/` | Done — real backend data (system status, resource overview, alerts, network traffic) |
|
||||
| Infrastructure | `/infrastructure` | Done — resource distribution, node status grid, cost/trend breakdown, all from real integration data. "Network" sub-tab planned as a future addition. |
|
||||
| BookNest | `/booknest` | Done — categorized bookmark hub wired to the real bookmarks API |
|
||||
| Terminal | `/terminal` | **Pending / on hold** — will be based on a fork of the (archived) Termix project; user has the fork and intends to hand this off to another AI session to integrate. Do not start this without explicit instruction. |
|
||||
| Settings | `/settings` | Done — Profile (real user identity + avatar, editable via API), Appearance, Integrations (8 real adapters), Notifications, Data & Backup, About |
|
||||
| Page | Route | What it does |
|
||||
|------|-------|---------------|
|
||||
| Glance | `/` | Home dashboard — system/integration health, resource overview, recent activity, shortcuts |
|
||||
| Infrastructure | `/infrastructure` | Resource inventory across all integrations — distribution donut, per-resource status grid, integration health, activity |
|
||||
| BookNest | `/booknest` | Categorized bookmark hub — quick access, favorites, link health, full CRUD |
|
||||
| Terminal | `/terminal` | Web SSH terminal — multi-tab, split panes, tmux attach, cert auth (OPKSSH) |
|
||||
| Tunnels | `/tunnels` | SSH tunnel manager — local/remote/dynamic (SOCKS5) forwarding, auto-start, live status |
|
||||
| Files | `/files` | SFTP file browser/editor over managed SSH hosts, with host-to-host transfer |
|
||||
| Containers | `/containers` | Docker container management — start/stop/restart/pause/remove, logs, interactive exec |
|
||||
| Remote Desktop | `/remote-desktop` | RDP/VNC/Telnet sessions via a Guacamole sidecar |
|
||||
| Host Metrics | `/host-metrics` | Live CPU/memory/disk/network/processes/ports/firewall/login-activity per SSH host, polled every 5s |
|
||||
| Settings | `/settings` | Profile, Appearance, Security, Integrations, Notifications, Data & Backup, About — deep-linkable via `?tab=` |
|
||||
| Help | `/help` | Static guided tour of every page above |
|
||||
| Login / Enrollment | `/login`, `/enrollment` | Auth entry points — not in the sidebar nav |
|
||||
|
||||
See `archnest-blueprint.md` for the original per-page design spec and `design-decisions.md` for the visual/UX conventions and lessons learned while building each page — read that file before making layout changes, it documents *why* things are built the way they are (hero banner layering, card blend techniques, icon library gotchas, etc.).
|
||||
See `design-decisions.md`'s "Page Notes" section for a detailed, per-page
|
||||
breakdown of layout, real data sources, and known quirks — it's kept in sync
|
||||
with the actual code, not a spec written before the page existed.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Frontend (`/src`)
|
||||
- React 19 + Vite + TypeScript, Tailwind CSS v4, Recharts (donuts/area
|
||||
charts), Lucide React icons, React Router.
|
||||
- `src/lib/api.ts` — typed fetch wrapper (`apiFetch`) + one function per
|
||||
backend endpoint + matching TS interfaces. This is the contract between
|
||||
frontend and backend; any new backend route needs a matching entry here.
|
||||
- `src/lib/AuthContext.tsx` — auth state backed by `localStorage` (single JWT,
|
||||
no session tracking yet — Phase 2 of the auth roadmap).
|
||||
- `src/pages/` — one file per route (see table above), plus `Login.tsx` /
|
||||
`Enrollment.tsx` for the unauthenticated/first-run flows.
|
||||
- `src/components/` — `TopBar.tsx` (title, global search across pages/
|
||||
integrations/bookmarks, user dropdown), `Sidebar.tsx` (nav + system-health
|
||||
rollup widget).
|
||||
- `App.tsx` — route table, plus per-route hero-banner config (`showHero`,
|
||||
`heroPaddingTop`, `heroObjectPosition` lookup maps) and `topBarHeight`
|
||||
lookup for pages with a subtitle (currently only BookNest).
|
||||
|
||||
### Backend (`/backend`)
|
||||
- Fastify 5, TypeScript, ESM (`tsx` for dev, `tsc -b` for build), entrypoint
|
||||
`src/server.ts`.
|
||||
- `backend/src/db/index.ts` — SQLite schema + `logEvent()` audit log.
|
||||
Single-user schema today (no `role` column, no multi-account concept —
|
||||
see `HANDOFF.md` Phase 3).
|
||||
- `backend/src/db/crypto.ts` — AES-256-GCM `encryptSecret`/`decryptSecret`,
|
||||
keyed by `ARCHNEST_SECRET_KEY`.
|
||||
- `backend/src/routes/` — one file per feature area:
|
||||
- `auth.ts` — setup, login, profile (`/api/setup`, `/api/auth/login`,
|
||||
`/api/auth/me`)
|
||||
- `integrations.ts` — integration CRUD + connection testing
|
||||
- `bookmarks.ts` — bookmarks + categories CRUD
|
||||
- `events.ts` — activity log retrieval
|
||||
- `terminal.ts` — SSH terminal WebSocket (`connect`/`input`/`resize`/
|
||||
`list_tmux`/`disconnect`)
|
||||
- `tunnels.ts` — SSH tunnel CRUD + connect/disconnect
|
||||
- `files.ts` — SFTP list/read/write/mkdir/rename/delete/chmod/download/upload
|
||||
- `docker.ts` — Docker exec WebSocket (interactive container shell)
|
||||
- `guacamole.ts` — Guacamole WebSocket proxy for remote desktop
|
||||
- `metrics.ts` — live host metrics endpoint
|
||||
- `transfer.ts` — host-to-host file transfer orchestration (start/poll/cancel)
|
||||
- `data.ts` — full backup export/import (integrations + secrets + bookmarks + tunnels)
|
||||
- `backend/src/integrations/` — one adapter per type, all real (none are
|
||||
stubs): `proxmox.ts`, `docker.ts`, `netbird.ts`, `cloudflare.ts`, `aws.ts`,
|
||||
`uptimeKuma.ts`, `weather.ts`, `ssh.ts`, `remoteDesktop.ts`. Each implements
|
||||
`testConnection()` (required) and `listResources()` (optional);
|
||||
`registry.ts` maps `IntegrationType` → adapter.
|
||||
- `backend/src/ssh/` — the shared SSH transport layer used by Terminal,
|
||||
Files, Tunnels, Transfers, and Host Metrics:
|
||||
- `connect.ts` — jump-host chaining, host-key verification, certificate auth
|
||||
- `sftp.ts` — ephemeral SFTP connections for file ops
|
||||
- `transfer.ts` — streamed host-to-host copy/move with progress + cancel
|
||||
- `metrics/` — 10 sequential collectors (cpu, memory, disk, uptime,
|
||||
network, system, processes, ports, firewall, login-stats) — sequential
|
||||
on purpose, to stay under OpenSSH's `MaxSessions` limit per host.
|
||||
- Docker images run on Alpine; **OpenSSL legacy provider is enabled** in
|
||||
`backend/Dockerfile` (`OPENSSL_CONF=/etc/ssl/openssl-legacy.cnf`) so
|
||||
old-format encrypted PEM keys (`BEGIN RSA PRIVATE KEY` + `DEK-Info`) still
|
||||
decrypt under OpenSSL 3 — don't remove this without understanding why.
|
||||
- **Required env vars, no defaults**: `ARCHNEST_SECRET_KEY`,
|
||||
`ARCHNEST_JWT_SECRET`. The server refuses to start without both. Optional:
|
||||
`ARCHNEST_DB_PATH`, `PORT`, `ARCHNEST_GUAC_CRYPT_KEY` /
|
||||
`ARCHNEST_GUACD_HOST` / `ARCHNEST_GUACD_PORT`, `ARCHNEST_CORS_ORIGIN`,
|
||||
`ARCHNEST_SESSION_LOG_DIR` (optional terminal session logging).
|
||||
|
||||
## Development
|
||||
|
||||
|
|
@ -34,23 +139,26 @@ npm install
|
|||
ARCHNEST_SECRET_KEY=$(openssl rand -hex 32) ARCHNEST_JWT_SECRET=$(openssl rand -hex 32) npm run dev
|
||||
```
|
||||
|
||||
Both `ARCHNEST_SECRET_KEY` (encrypts integration secrets at rest) and `ARCHNEST_JWT_SECRET` (signs auth tokens) are required env vars with no defaults — the server will refuse to start without them. `ARCHNEST_DB_PATH` optionally overrides the SQLite file location (defaults to a local path under `backend/`). `PORT` overrides the listen port (default 4000-range, check `server.ts`).
|
||||
`ARCHNEST_DB_PATH` optionally overrides the SQLite file location (defaults to
|
||||
a local path under `backend/`). `PORT` overrides the listen port (check
|
||||
`server.ts` for the default).
|
||||
|
||||
Type-check both before committing:
|
||||
Type-check both before committing — this is the minimum bar, not a substitute
|
||||
for testing in a browser:
|
||||
```bash
|
||||
npx tsc --noEmit # from repo root, frontend
|
||||
cd backend && npx tsc --noEmit # backend
|
||||
```
|
||||
Vite/the browser surface some runtime errors (e.g. missing icon exports) that the type-checker won't catch, so also smoke-test pages in a browser.
|
||||
Vite/the browser surface some runtime errors (e.g. missing icon exports —
|
||||
see the lucide-react gotcha in `design-decisions.md`) that the type-checker
|
||||
won't catch.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
**Frontend**
|
||||
- React 19 + Vite + TypeScript
|
||||
- React Router for routing
|
||||
- Tailwind CSS v4
|
||||
- Recharts (donuts, line/area charts)
|
||||
- Lucide React (icons)
|
||||
- React 19 + Vite + TypeScript, React Router, Tailwind CSS v4
|
||||
- Recharts (donuts, line/area charts), Lucide React (icons)
|
||||
- xterm.js (Terminal page terminal rendering)
|
||||
|
||||
**Backend**
|
||||
- Fastify 5 + TypeScript, `tsx` for dev, `tsc -b` for build
|
||||
|
|
@ -58,16 +166,56 @@ Vite/the browser surface some runtime errors (e.g. missing icon exports) that th
|
|||
- `@fastify/jwt` for auth tokens, `bcryptjs` for password hashing
|
||||
- `zod` for request validation
|
||||
- AES-256-GCM (Node `crypto`) for encrypting integration secrets at rest
|
||||
- Integration adapters: Proxmox, Docker, NetBird, Cloudflare, AWS, Uptime Kuma, Weather, SSH (see `backend/src/integrations/`)
|
||||
- SSH client library powering the SSH transport layer (`backend/src/ssh/`)
|
||||
- Guacamole Lite protocol for RDP/VNC/Telnet, proxied to a `guacd` sidecar
|
||||
|
||||
**Deploy target:** Docker on `racknerd1` → NPM (Nginx Proxy Manager) proxy at `archnest.snsnetlabs.com`.
|
||||
**Integrations**: Proxmox, Docker, NetBird, Cloudflare, AWS, Uptime Kuma,
|
||||
Weather (wttr.in), SSH, Remote Desktop (RDP/VNC/Telnet via Guacamole) — see
|
||||
`backend/src/integrations/` for adapter implementations.
|
||||
|
||||
**Deploy target:** Docker on `racknerd1` → Nginx Proxy Manager at
|
||||
`archnest.snsnetlabs.com`.
|
||||
|
||||
## Deployment
|
||||
|
||||
**Live and deployed** at `archnest.snsnetlabs.com`. The pipeline at `.github/workflows/deploy.yml` triggers on every push to `main`: it copies the repo to `racknerd1` over SCP and runs `docker compose up -d --build` there, gated on an `/api/health` check. No further setup is needed — merging a PR to `main` redeploys automatically.
|
||||
**Live and deployed.** `.github/workflows/deploy.yml` triggers on every push
|
||||
to `main`: builds, SCPs the repo to `racknerd1`, and runs
|
||||
`docker compose up -d --build` there, gated on an `/api/health` health check.
|
||||
No further setup is needed — merging a PR to `main` redeploys automatically.
|
||||
|
||||
If a deploy fails, check the workflow run's `deploy` job steps in order: `Pre-flight` (confirms host `.env` exists), `Copy repo to racknerd1`, `Build, restart, and clean up`, `Health check (backend /api/health)`.
|
||||
`docker-compose.yml` runs 3 services: `archnest` (frontend), `archnest-backend`,
|
||||
and `guacd` (remote desktop sidecar).
|
||||
|
||||
For reference, the one-time setup that was done to stand this up: host provisioning (Docker/Compose on `racknerd1`, deploy SSH user, `/opt/archnest` directory), `/opt/archnest/.env` populated from `.env.example` with real secrets, `RACKNERD_HOST`/`RACKNERD_USER`/`RACKNERD_SSH_KEY` added as GitHub Actions secrets, and DNS/Nginx Proxy Manager pointed at the host.
|
||||
If a deploy fails, check the workflow run's `deploy` job steps in order:
|
||||
`Pre-flight` (confirms host `.env` exists) → `Copy repo to racknerd1` →
|
||||
`Build, restart, and clean up` → `Health check (backend /api/health)`.
|
||||
|
||||
See `HANDOFF.md` for current feature work in progress (multi-user accounts, SSO, security settings).
|
||||
One-time setup already done (reference only, shouldn't need repeating): host
|
||||
provisioning (Docker/Compose on `racknerd1`, deploy SSH user, `/opt/archnest`
|
||||
directory), `/opt/archnest/.env` populated from `.env.example` with real
|
||||
secrets, `RACKNERD_HOST`/`RACKNERD_USER`/`RACKNERD_SSH_KEY` added as GitHub
|
||||
Actions secrets, DNS/Nginx Proxy Manager pointed at the host.
|
||||
|
||||
## Documentation map
|
||||
|
||||
- **`README.md`** (this file) — architecture, tech stack, deployment, page list.
|
||||
- **`HANDOFF.md`** — current task state, standing workflow rules (git workflow,
|
||||
mock-data policy, secrets discipline), and the auth/SSO roadmap. Read this
|
||||
before starting any new work session.
|
||||
- **`design-decisions.md`** — visual/UX conventions (colors, typography, card
|
||||
style, animations) plus a detailed, accurate-as-of-now "Page Notes" section
|
||||
per page — what's actually rendered and where its data comes from. This is
|
||||
the file to update whenever a page's layout or data source changes.
|
||||
- **`TERMIX_MIGRATION.md`** — phase-by-phase history of how the SSH/Tunnels/
|
||||
Files/Containers/Remote Desktop/Host Metrics/Transfer/Data-export feature
|
||||
set was built (originally scoped as a migration from a forked Termix
|
||||
project, hence the name). Useful for historical "why was it built this
|
||||
way" context on those specific features.
|
||||
|
||||
Two older docs were deleted as part of a documentation cleanup
|
||||
(`archnest-blueprint.md`, the original 6-page mockup pitch with fictional
|
||||
config files and placeholder numbers; `glance.md`, an early Glance-only spec
|
||||
with the same problem). Their still-accurate content (color palette,
|
||||
dropdown menu shape, card styling) was folded into `design-decisions.md`,
|
||||
and everything else was superseded by the real, deployed implementation
|
||||
described above.
|
||||
|
|
|
|||
|
|
@ -1,405 +0,0 @@
|
|||
# ArchNest Dashboard — Blueprint
|
||||
|
||||
> Complete spec for a 6-page custom dashboard. Portfolio showcase piece.
|
||||
|
||||
---
|
||||
|
||||
## Global Design Language
|
||||
|
||||
### Color Palette
|
||||
| Role | Value | Usage |
|
||||
|------|-------|-------|
|
||||
| Background (page) | `#0D0E10` | Near-black, warm undertone |
|
||||
| Background (cards) | `#141518` | Slightly elevated surfaces |
|
||||
| Background (sidebar) | `#0A0B0D` | Darker than page |
|
||||
| Border (cards) | `#1E2025` | Subtle 1px card outlines |
|
||||
| Border (hover/active) | `#C8A434` | Gold highlight on interaction |
|
||||
| Accent (primary) | `#C8A434` | Gold — links, active states, progress bars |
|
||||
| Accent (success) | `#2ECC71` | Green — operational, running, completed |
|
||||
| Accent (warning) | `#E67E22` | Orange — warnings, medium alerts |
|
||||
| Accent (danger) | `#E74C3C` | Red — critical, high alerts, errors |
|
||||
| Text (primary) | `#E8E6E0` | Warm white/cream |
|
||||
| Text (secondary) | `#7A7D85` | Muted gray — labels, timestamps |
|
||||
| Text (accent) | `#C8A434` | Gold — active nav, status labels |
|
||||
|
||||
### Typography
|
||||
| Element | Style |
|
||||
|---------|-------|
|
||||
| Card titles | 12-13px, uppercase, letter-spacing 1px, secondary color |
|
||||
| Large numbers | 28-36px, bold, primary color |
|
||||
| Body text | 14px, primary color |
|
||||
| Labels/descriptions | 12-13px, secondary color |
|
||||
| Nav items | 14px, secondary (gold when active) |
|
||||
|
||||
### Card Style
|
||||
- Border radius: 12px
|
||||
- Background: `#141518`
|
||||
- Border: 1px solid `#1E2025`
|
||||
- Padding: 20-24px
|
||||
- No shadows (flat design)
|
||||
- Hover: border transitions to gold (0.2s ease)
|
||||
|
||||
---
|
||||
|
||||
## Shared Components (appear on every page)
|
||||
|
||||
### Sidebar (fixed left, ~80px)
|
||||
- **Logo**: ArchNest arch icon (gold gradient) + "ARCHNEST" text (gold, 10px caps)
|
||||
- **Nav items**: icon (18px) + label (14px), active = gold text + gold left border (3px)
|
||||
- **Bottom**: Green dot + "System Status" / "All Systems Operational" (gold)
|
||||
|
||||
**Final Nav (6 items):**
|
||||
|
||||
| # | Icon | Label | Route |
|
||||
|---|------|-------|-------|
|
||||
| 1 | grid | Glance | `/` |
|
||||
| 2 | server | Infrastructure | `/infrastructure` |
|
||||
| 3 | globe | Network | `/network` |
|
||||
| 4 | bookmark | BookNest | `/booknest` |
|
||||
| 5 | terminal | Terminal | `/terminal` |
|
||||
| 6 | settings | Settings | `/settings` |
|
||||
|
||||
### Top Bar (~56px height)
|
||||
- **Left**: Page title (bold, uppercase, 18px)
|
||||
- **Center-right**: Search bar (rounded, ~300px, placeholder varies by page)
|
||||
- **Right**: Bell icon (with badge) + User avatar (36px, gold ring) + "ArchNest Ops" / "Administrator" + chevron
|
||||
|
||||
### Hero Banner
|
||||
- Full width of main content area, 12px radius, overflow hidden
|
||||
- Image: `archnest-brand.png` (dark cityscape + gold arch)
|
||||
- Height varies: ~200px on Glance, ~120px on sub-pages
|
||||
- Not shown on Terminal or Settings pages
|
||||
|
||||
### Page Footer / Status Bar
|
||||
- Height: ~40px, subtle top border
|
||||
- Content: Contextual stats (varies per page) + "Last updated: 2m ago" (right)
|
||||
|
||||
---
|
||||
|
||||
## Interactions & Behaviors
|
||||
|
||||
- Sidebar nav: click navigates, gold highlight on active
|
||||
- Card hover: border → gold (0.2s)
|
||||
- Close buttons (X): hide/collapse cards
|
||||
- "View all" links: navigate to full views
|
||||
- Progress rings/bars: animated fill on load
|
||||
- Sparklines: subtle animation, periodic update
|
||||
|
||||
---
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
- **Desktop (>1200px)**: Full layout
|
||||
- **Tablet (768-1200px)**: Sidebar → icons only (~50px), cards 2-column
|
||||
- **Mobile (<768px)**: Sidebar → bottom nav, cards single column
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- React 19 + Vite + TypeScript
|
||||
- Zustand (state)
|
||||
- Chart.js or Recharts (sparklines, donuts, area charts)
|
||||
- Lucide React (icons)
|
||||
- xterm.js (terminal page)
|
||||
- CSS Modules or Tailwind
|
||||
- Deploy: Docker on racknerd1 → NPM proxy at archnest.snsnetlabs.com
|
||||
|
||||
---
|
||||
|
||||
## Assets
|
||||
|
||||
| Asset | CDN Path | Usage |
|
||||
|-------|----------|-------|
|
||||
| Logo | `logos/brands/archnest-logo.png` | Sidebar |
|
||||
| Banner | `backgrounds/archnest-brand.png` | Hero |
|
||||
| Network bg | `backgrounds/archnest-network-bg.jpg` | Network page alt |
|
||||
| Geometric bg | `backgrounds/archnest-geometric-bg.jpg` | Texture overlay |
|
||||
|
||||
CDN base: `https://raw.githubusercontent.com/SamuelSJames/assets-public/master/`
|
||||
|
||||
---
|
||||
|
||||
## Page 1: Glance (`/`)
|
||||
|
||||
> Main operations dashboard — system health at a glance
|
||||
|
||||
### Status Cards Row (4 cards)
|
||||
|
||||
| Card | Icon | Number | Subtitle | Extra |
|
||||
|------|------|--------|----------|-------|
|
||||
| System Status | checkmark | 100% | All Systems Operational | Circular progress ring + sparkline |
|
||||
| Infrastructure | server | 24 | Total Resources | 🟢24 Running 🟡0 Warning 🔴0 Critical |
|
||||
| Security | shield | 2 | Active Alerts | 🟡2 Low 🟡0 Medium 🔴0 High |
|
||||
| Network | nodes | 98.7% | Network Uptime | Sparkline |
|
||||
|
||||
### Middle Row (3 columns: 30% | 40% | 30%)
|
||||
|
||||
**Resource Overview** (left):
|
||||
- 5 progress bars: Compute 18/24, Storage 12.4/20 TB, Database 8/12, Network 98.7%, Containers 32/40
|
||||
|
||||
**Recent Activity** (center):
|
||||
- Backup completed (2m) | Security scan completed (8m) | Instance launched (15m) | Config updated (22m) | User login (35m)
|
||||
|
||||
**Top Alerts** (right):
|
||||
- 🔴 High CPU Usage – App Server 02 (2m)
|
||||
- 🟡 Disk Space Low – Database Cluster 01 (15m)
|
||||
- 🟡 Unauthorized Login – Web Frontend (32m)
|
||||
- 🟡 SSL Cert Expiring – api.archnest.io (1h)
|
||||
|
||||
### Bottom Row (2 columns: 65% | 35%)
|
||||
|
||||
**Network Traffic** (left):
|
||||
- Area chart (gold gradient) + stats: Incoming 1.23 Gbps (↓12.4%), Outgoing 1.08 Gbps (↑8.7%)
|
||||
|
||||
**Shortcuts** (right):
|
||||
- 4 icon buttons: Add Server | Create Backup | Deploy App | View Logs
|
||||
|
||||
### Footer Stats
|
||||
- System Status 100% | 24 Resources | 2 Alerts | 98.7% Uptime
|
||||
|
||||
---
|
||||
|
||||
## Page 2: Infrastructure (`/infrastructure`)
|
||||
|
||||
> Monitor and manage your cloud infrastructure
|
||||
|
||||
### Sub-Tabs
|
||||
Overview | Compute | Storage | Network | Database | Containers | Load Balancers | DNS | Backups | Tags
|
||||
|
||||
**Actions**: "+ Add Resource" button, gear icon
|
||||
|
||||
### Status Cards Row (5 cards)
|
||||
|
||||
| Card | Number | Subtitle | Trend |
|
||||
|------|--------|----------|-------|
|
||||
| Total Resources | 128 | Across all regions | ↑12.4% 7d |
|
||||
| Total Cost (MTD) | $24,560.75 | Across all services | ↓8.7% month |
|
||||
| Resource Health | 98.7% | Healthy | ↑2.1% 7d |
|
||||
| Active Alerts | 2 | Requires attention | ↓63% 7d |
|
||||
| Uptime | 99.98% | Global uptime | ↑0.01% 7d |
|
||||
|
||||
### Middle Row (30% | 40% | 30%)
|
||||
|
||||
**Resource Distribution** (left):
|
||||
- Donut chart: 128 Total — Compute 38 (29.7%), Storage 24 (18.8%), Database 16 (12.5%), Network 20 (15.6%), Containers 18 (14.1%), Others 12 (9.4%)
|
||||
|
||||
**Infrastructure Map** (center):
|
||||
- Dark world map with gold glowing dots (region markers), connection lines
|
||||
- Footer: 6 Regions | 18 AZs | 128 Resources | 🟢 Live
|
||||
|
||||
**Top Resources by Utilization** (right):
|
||||
- web-app-01 (m6i.large) 85% | api-server-02 (c6i.xlarge) 72% | db-primary (db.r6g.xlarge) 68% | cache-cluster 55% | worker-node-01 48%
|
||||
|
||||
### Bottom Row (35% | 35% | 30%)
|
||||
|
||||
**Resource Trend** (left):
|
||||
- Multi-line area chart (7 days): Compute, Storage, Database, Network lines
|
||||
|
||||
**Cost Breakdown MTD** (center):
|
||||
- Donut: $24,560.75 Total — Compute $12,450 (50.7%), Storage $5,213 (21.2%), Database $3,452 (14.1%), Network $2,134 (8.7%), Others $1,310 (5.3%)
|
||||
|
||||
**Recent Activity** (right):
|
||||
- EC2 web-app-01 created (2m) | RDS backup (15m) | SG updated (32m) | S3 bucket created (1h) | LB updated (2h)
|
||||
|
||||
### Footer Stats
|
||||
- AWS | 6 Regions | 18 AZs | 128 Resources | 98.7% Health | $24,560.75 MTD | 2 Alerts
|
||||
|
||||
---
|
||||
|
||||
## Page 3: Network (`/network`)
|
||||
|
||||
> Monitor and manage your network performance and security
|
||||
|
||||
### Sub-Tabs
|
||||
Overview | Traffic | Topology | Devices | Interfaces | VPN | DNS | Firewalls | Load Balancers | Alerts | Reports
|
||||
|
||||
**Actions**: "Last 24 Hours" dropdown, "Export Report" button
|
||||
|
||||
### Status Cards Row (6 cards)
|
||||
|
||||
| Card | Number | Trend |
|
||||
|------|--------|-------|
|
||||
| Network Health | 98.7% "Excellent" | ↑2.3% yesterday |
|
||||
| Total Traffic | 1.23 Tbps | ↑18.6% yesterday |
|
||||
| Packet Loss | 0.02% | ↓0.01% yesterday |
|
||||
| Active Connections | 28,457 | ↑6.7% yesterday |
|
||||
| Threats Blocked | 152 | ↑12.4% yesterday (⚠) |
|
||||
| Avg. Latency | 12.4 ms | ↓8.1% yesterday |
|
||||
|
||||
### Middle Row (25% | 50% | 25%)
|
||||
|
||||
**Top Talkers** (left):
|
||||
- web-app-01 203.4 Gbps | db-primary 156.7 Gbps | cache-cluster 98.3 Gbps | api-gateway 76.1 Gbps | backup-server 54.8 Gbps
|
||||
|
||||
**Network Topology** (center):
|
||||
- Dark world map with gold node dots, connection lines, glow effects
|
||||
- Legend: 🟢 Live | 🟡 Warning | 🔴 Critical
|
||||
- Controls: Zoom +/-, Fullscreen
|
||||
|
||||
**Interface Utilization + Alert Summary** (right, stacked):
|
||||
- Interfaces: ethernet1/1 85%, 1/2 72%, 1/3 68%, 1/4 55%, 1/5 48% (green/gold bars)
|
||||
- Alert Summary: 🔴 2 Critical | 🟡 5 Warning | 🟢 12 Info
|
||||
|
||||
### Bottom Row (35% | 35% | 30%)
|
||||
|
||||
**Traffic Over Time** (left):
|
||||
- Area chart (24h): Inbound (gold), Outbound (amber), Total (teal)
|
||||
- Y: 250 Gbps – 1.5 Tbps, X: 00:00–20:00
|
||||
|
||||
**Protocol Distribution** (center):
|
||||
- Donut: 1.23 Tbps Total — TCP 65.2%, UDP 18.7%, ICMP 7.3%, DNS 4.1%, Others 4.7%
|
||||
|
||||
**Recent Events** (right):
|
||||
- ethernet1/2 UP (2m) | High bandwidth web-app-01 (8m) | New device 10.0.1.45 (15m) | VPN tunnel us-west-2 (22m) | Blocked threat 185.199.108.153 (35m)
|
||||
|
||||
### Footer Stats
|
||||
- 6 Regions | 42 Sites | 248 Devices | 1,245 Interfaces | 28,457 Connections | 98.7% Health
|
||||
|
||||
---
|
||||
|
||||
## Page 4: BookNest (`/booknest`)
|
||||
|
||||
> Your Digital Library — categorized bookmarks with health monitoring
|
||||
|
||||
### Page Stats (below title)
|
||||
🔗 **312** Links | 📁 **18** Categories | ⭐ **12** Favorites
|
||||
|
||||
### Layout: Main (75%) + Right Sidebar (25%)
|
||||
|
||||
### Quick Access Row (5 cards)
|
||||
| Category | Icons shown | Count |
|
||||
|----------|------------|-------|
|
||||
| Infrastructure | Proxmox, Cloudflare, Portainer, Cockpit, CasaOS | 5 links |
|
||||
| Development | GitHub, GitLab, Gitea, VSCode, Docker | 5 links |
|
||||
| AI Tools | ChatGPT, Claude, AI icons, OpenWebUI | 5 links |
|
||||
| AWS | AWS service icons | 5 links |
|
||||
| Networking | NetBird, Wireshark, Cisco, Ubiquiti | 5 links |
|
||||
|
||||
### Bookmark Groups — Row 1 (4 columns)
|
||||
|
||||
**Infrastructure & Self Hosted** (10 links):
|
||||
CasaOS, Proxmox (pve1), Proxmox (mtr), Portainer, Cockpit, Cloudflare, NPM, NetBird, Linode, RackNerd
|
||||
|
||||
**Development & Code** (8 links):
|
||||
GitHub, Gitea, Trillium Notes, VSCode Web, Docker Hub, GitLab, Terraform Registry, Ansible Galaxy
|
||||
|
||||
**Lab & Networking** (8 links):
|
||||
GNS3, EVE-NG, OpenClaw, NetBird, Cisco Docs, Juniper Docs, Wireshark, iPerf3
|
||||
|
||||
**AWS** (8 links):
|
||||
AWS Console, IAM, EC2, S3, CloudFormation, Route 53, VPC, Billing
|
||||
|
||||
### Bookmark Groups — Row 2 (4 columns)
|
||||
|
||||
**AI Tools** (7 links):
|
||||
ChatGPT, Claude, Gemini, PartyRock, Perplexity, OpenWebUI, Ollama
|
||||
|
||||
**Learning** (6 links):
|
||||
WGU, Udemy, AWS Skill Builder, YouTube, LinkedIn Learning, Coursera
|
||||
|
||||
**Finance** (5 links):
|
||||
Bank, Budget, Investments, Retirement, Credit Cards
|
||||
|
||||
**Life** (6 links):
|
||||
Calendar, Email, Photos, Drive, Notes, Travel
|
||||
|
||||
### Right Sidebar Widgets
|
||||
|
||||
**Favorites**: Proxmox (pve1), GitHub, AWS Console, ChatGPT, NetBird
|
||||
|
||||
**Recently Used**: Proxmox 5m, GitHub 15m, AWS Console 1h, Trilium 2h, GNS3 3h
|
||||
|
||||
**Link Health** (donut): 304 Online, 6 Warning, 2 Offline (312 total)
|
||||
|
||||
**Category Breakdown** (donut): Infrastructure 32%, Development 24%, AI Tools 18%, Learning 10%, Finance 8%, Life 8%
|
||||
|
||||
### Link Item Design
|
||||
- Icon (colored, 16-20px) + Name (14px) + Star toggle (right, gold when favorited)
|
||||
|
||||
---
|
||||
|
||||
## Page 5: Terminal (`/terminal`)
|
||||
|
||||
> Embedded SSH terminal — no hero banner, no cards grid
|
||||
|
||||
### Layout: Full-height terminal viewport
|
||||
|
||||
### Tab Bar (top, 40px)
|
||||
- Tabs: host name + colored dot (🟢 connected, ⚪ disconnected)
|
||||
- Active tab: gold bottom border
|
||||
- "+" button: open new connection
|
||||
- Quick-connect dropdown with saved hosts
|
||||
|
||||
**Saved Hosts:**
|
||||
| Label | Connection |
|
||||
|-------|-----------|
|
||||
| Pre (Audio) | ssh pre |
|
||||
| Proxmox | ssh mtr |
|
||||
| Linode (NPM) | ssh linode |
|
||||
| RackNerd 1 | ssh racknerd1 |
|
||||
| RackNerd 3 (Forge) | ssh racknerd3 |
|
||||
| Studio (DevOps) | ssh studio |
|
||||
|
||||
### Terminal Viewport
|
||||
- Font: JetBrains Mono / Fira Code, 14px
|
||||
- Prompt: Gold | Commands: Warm white | Output: Secondary gray | Errors: Red | Dirs: Teal
|
||||
- Cursor: Gold block, blinking
|
||||
- Scrollback: 5000 lines
|
||||
|
||||
### Status Bar (bottom, 28px)
|
||||
- 🟢 Connected | Host: `pre` | Session: 12m 34s | Shell: bash
|
||||
- Right: Split pane | Fullscreen | Disconnect
|
||||
|
||||
### Implementation
|
||||
- xterm.js for browser terminal
|
||||
- Node.js WebSocket-to-SSH bridge on racknerd1
|
||||
|
||||
---
|
||||
|
||||
## Page 6: Settings (`/settings`)
|
||||
|
||||
> User preferences and system configuration — no hero banner
|
||||
|
||||
### Layout: Settings Nav (200px left) + Content (right, scrollable)
|
||||
|
||||
### Nav Sections
|
||||
1. Profile | 2. Appearance | 3. Integrations | 4. Notifications | 5. Data & Backup | 6. About
|
||||
|
||||
### Profile
|
||||
- Avatar (64px, gold ring) + Display Name + Role (badge) + Email + Timezone
|
||||
- "Save Changes" button (gold)
|
||||
|
||||
### Appearance
|
||||
- Theme: Dark/Light toggle
|
||||
- Accent: Color swatches (Gold active, Teal, Purple, Blue, Green, Red)
|
||||
- Font Size: 12-16px slider
|
||||
- Sidebar: Expanded/Collapsed toggle
|
||||
- Animations: On/Off
|
||||
- Card Border Radius: 4-16px slider
|
||||
|
||||
### Integrations
|
||||
| Service | Status | Config Fields |
|
||||
|---------|--------|---------------|
|
||||
| Proxmox | 🟢 | Host URL, API Token |
|
||||
| Docker | 🟢 | Socket/remote URL |
|
||||
| NetBird | 🟢 | API key |
|
||||
| Cloudflare | 🟢 | API token, Zone ID |
|
||||
| AWS | 🟢 | Access Key, Secret (masked), Region |
|
||||
| Uptime Kuma | ⚪ | URL, API key |
|
||||
| Weather API | 🟢 | Location, Units |
|
||||
|
||||
- Masked secrets with eye toggle, "Test Connection" per card
|
||||
|
||||
### Notifications
|
||||
- Enable toggle | Threshold dropdown (All/Critical/Warning+) | Email toggle | Browser push | Sound + volume
|
||||
|
||||
### Data & Backup
|
||||
- Export/Import Bookmarks (JSON) | Export Settings | Reset to Defaults (danger) | Clear Cache
|
||||
|
||||
### About
|
||||
- App: ArchNest Dashboard v1.0.0
|
||||
- Author: Samuel James
|
||||
- Repo: github.com/SamuelSJames/archnest
|
||||
- Stack: React 19, Vite, TypeScript
|
||||
- License: MIT
|
||||
|
|
@ -1,225 +1,283 @@
|
|||
# ArchNest — Design Decisions & Lessons Learned
|
||||
# ArchNest — Design Decisions & Page Reference
|
||||
|
||||
> This file captures all visual/UX decisions made during Glance page development.
|
||||
> Apply these consistently to ALL future pages to avoid repeated iteration.
|
||||
> Living reference for visual/UX conventions and what's actually built on each page.
|
||||
> Apply the Global Rules consistently to any new page. The Page Notes section
|
||||
> describes the **real, deployed** implementation — not a mockup. If you change a
|
||||
> page's layout or data source, update its section here in the same PR.
|
||||
>
|
||||
> This file replaces the old `archnest-blueprint.md` (original 6-page mockup
|
||||
> pitch, since deleted — superseded by the real 11-page app below) and
|
||||
> `glance.md` (original Glance spec with fictional config files like
|
||||
> `systems.config`/`infra.config` that were never built — also deleted). Anything
|
||||
> from those files still true (color palette, typography, card style, dropdown
|
||||
> menu shape) has been folded in below.
|
||||
|
||||
---
|
||||
|
||||
## Global Rules (Apply to Every Page)
|
||||
## Global Rules (apply to every page)
|
||||
|
||||
### Sidebar
|
||||
- **Expanded width**: 200px (matches mockup proportions — needs room for labels)
|
||||
- **Collapsed width**: 64px (icon only)
|
||||
- **User can manually collapse/expand** via toggle button (not just responsive)
|
||||
- **Main content margin-left** must match sidebar width exactly
|
||||
- Expanded width: 200px. Collapsed width: 64px (icons only).
|
||||
- User can manually collapse/expand via a toggle button (not just responsive breakpoints).
|
||||
- Main content `margin-left` must match the sidebar width exactly.
|
||||
- Bottom of sidebar: live "System Status" widget — green dot + "All Systems
|
||||
Operational" or "X Issue(s) Detected", driven by polling integration status
|
||||
(not the fictional ping-sweep config described in the old spec).
|
||||
|
||||
### Page Title (Top Bar)
|
||||
- **Color**: Gold (#C8A434) — NOT white. Use inline `style={{ color: '#C8A434' }}` if Tailwind class doesn't apply
|
||||
- **Font**: 18px, bold, uppercase, tracking-wide
|
||||
- **No border on top bar** — blends into the page background
|
||||
- Color: gold `#C8A434` — not white. Use inline `style={{ color: '#C8A434' }}`
|
||||
if a Tailwind class doesn't apply.
|
||||
- Font: 18px, bold, uppercase, tracking-wide.
|
||||
- No border on the top bar — it blends into the page background.
|
||||
- Pages with a `pageSubtitles` entry (currently only BookNest) render a larger
|
||||
28px title + subtitle line instead, and the top bar grows from 56px → 72px —
|
||||
see `TopBar.tsx`'s `pageSubtitles` map and `App.tsx`'s `topBarHeight` lookup,
|
||||
which must be kept in sync or the layout clips/gaps.
|
||||
|
||||
### Colors — Use Inline Styles When Tailwind Fails
|
||||
- Tailwind v4 `@theme` custom colors (text-gold, bg-card, etc.) may not always apply
|
||||
- If a color isn't rendering correctly, fall back to inline `style={{ color: '#C8A434' }}`
|
||||
- Always verify visually after changes
|
||||
### Colors
|
||||
| Role | Value |
|
||||
|------|-------|
|
||||
| Background (page) | `#0D0E10` |
|
||||
| Background (cards) | `#141518` |
|
||||
| Background (sidebar) | `#0A0B0D` |
|
||||
| Border (cards) | `#1E2025` |
|
||||
| Border/accent (hover/active) | `#C8A434` (gold) |
|
||||
| Success | `#2ECC71` |
|
||||
| Warning | `#E67E22` |
|
||||
| Danger | `#E74C3C` |
|
||||
| Text (primary) | `#E8E6E0` |
|
||||
| Text (secondary) | `#7A7D85` |
|
||||
|
||||
Tailwind v4 `@theme` custom colors (`text-gold`, `bg-card`, etc.) don't always
|
||||
apply reliably — fall back to inline `style={{ color: '#C8A434' }}` when a
|
||||
color isn't rendering, and verify visually after changes.
|
||||
|
||||
### Typography
|
||||
- Card titles: 10-11px, uppercase, tracking-[1.5px], secondary color, font-medium.
|
||||
- Large numbers: 24-28px, bold, primary color.
|
||||
- Subtitles/labels: 10-11px, secondary color.
|
||||
- Body text in lists: 13px, primary color.
|
||||
- Timestamps: 11px, secondary color.
|
||||
|
||||
### Card Style
|
||||
- Border radius: 12px. Background `#141518`. Border 1px solid `#1E2025`.
|
||||
- Padding: 16-24px depending on density. No shadows (flat design).
|
||||
- Hover: border transitions to gold, 0.2s ease.
|
||||
|
||||
### Content Alignment
|
||||
- **All rows must share the same horizontal padding** (`px-6` applied once at the parent container level)
|
||||
- **Do NOT** use different padding for different rows — this causes misalignment
|
||||
- The hero banner, status cards, middle row, and bottom row must all line up left and right edges
|
||||
|
||||
### Hero Banner + KPI Overlap
|
||||
- Banner height: 200px
|
||||
- Status cards overlap via negative margin: `-mt-12`
|
||||
- Banner image: `object-cover` with `object-position: center 25%` (show the top/skyline, not center)
|
||||
- Cards use `backdrop-blur-sm` and `bg-card/95` for glass effect over the banner
|
||||
|
||||
### KPI Card Sizing
|
||||
- KPI 1 (System Status) and KPI 4 (Network): **wider** — `1.3fr`
|
||||
- KPI 2 (Infrastructure) and KPI 3 (Security): **standard** — `1fr`
|
||||
- Grid: `grid-cols-[1.3fr_1fr_1fr_1.3fr]`
|
||||
- Cards have compact padding: `p-4` (not p-5 or p-6)
|
||||
|
||||
### No Footer
|
||||
- The mockup does NOT have a footer/status bar
|
||||
- Do not add one unless explicitly requested
|
||||
|
||||
### Target Display
|
||||
- Primary design target: **16-inch screen / 1920px width**
|
||||
- Lots of horizontal space available — don't constrain content width unnecessarily
|
||||
- Design should feel spacious, not cramped
|
||||
|
||||
### Typography Sizes (smaller than default)
|
||||
- Card titles: 10-11px, uppercase, tracking-[1.5px], secondary color, font-medium
|
||||
- Large numbers: 24-28px, bold, primary color
|
||||
- Subtitles/labels: 10-11px, secondary color
|
||||
- Body text in lists: 13px, primary color
|
||||
- Timestamps: 11px, secondary color
|
||||
- Breakdowns: 9-10px, secondary color
|
||||
- All rows in a page share the same horizontal padding (`px-6` at the parent
|
||||
container level) so the hero banner, status cards, and content rows line up
|
||||
left/right.
|
||||
|
||||
### Animations
|
||||
- Card hover: border → gold, 0.2s ease
|
||||
- Progress ring: animates from 0 to value in 1s
|
||||
- Sparklines: draw animation 1s
|
||||
- Progress bars: fill animation 0.8s
|
||||
- Card hover: border → gold, 0.2s ease.
|
||||
- Progress ring / sparkline: animate from 0 on load, ~1s.
|
||||
- Progress bars: fill animation ~0.8s.
|
||||
|
||||
### Icons
|
||||
- Source: Lucide React (imported per component, tree-shaken)
|
||||
- Size: 14-18px depending on context
|
||||
- Color: gold for active/accent, text-secondary for inactive
|
||||
- Gold glow on active sidebar items: `shadow-[0_0_6px_rgba(200,164,52,0.5)]`
|
||||
- Lucide React, 14-18px depending on context. Gold for active/accent, secondary
|
||||
color for inactive. Gold glow on active sidebar items:
|
||||
`shadow-[0_0_6px_rgba(200,164,52,0.5)]`.
|
||||
- **Gotcha**: the installed lucide-react version's TypeScript types list some
|
||||
brand/wordmark icons (`Github`, `Gitlab`, `Linkedin`, `Youtube`) that aren't
|
||||
actually exported at runtime — `tsc --noEmit` stays clean while the page
|
||||
renders blank with a runtime `SyntaxError` only visible in the Vite dev log.
|
||||
Verify icon names against the package's actual exports before importing
|
||||
anything brand-flavored.
|
||||
|
||||
### No Footer
|
||||
- No page has a fixed footer/status bar. Don't add one unless explicitly requested.
|
||||
|
||||
### Target Display
|
||||
- Primary design target: 16-inch / 1920px-wide screen. Don't constrain content
|
||||
width unnecessarily — design should feel spacious.
|
||||
|
||||
### User Avatar & Dropdown (TopBar, every page)
|
||||
- 36px circle, gold border + glow, shows uploaded avatar image or initials.
|
||||
- Adjacent: display name + "Administrator" role label, chevron (rotates on open).
|
||||
- Dropdown menu: header (name + email) → **Profile** (`/settings?tab=profile`)
|
||||
→ **Appearance** (`/settings?tab=appearance`) → **Security**
|
||||
(`/settings?tab=security`) → **Help & Support** (`/help`) → divider →
|
||||
**Sign Out** (red, danger styling). See `TopBar.tsx`.
|
||||
|
||||
---
|
||||
|
||||
## Page-Specific Notes
|
||||
## Page Notes (what's actually built, page by page)
|
||||
|
||||
### Glance Page
|
||||
- No footer
|
||||
- Status cards overlap hero banner
|
||||
- Middle row: 3 equal-ish columns (30/40/30)
|
||||
- Bottom row: 2 columns (65/35)
|
||||
- Network Traffic card has its own background image at low opacity
|
||||
- User avatar dropdown has: Profile, Appearance, Security, Help & Support, Sign Out
|
||||
All pages below consume real backend data via `src/lib/api.ts` — **zero mock
|
||||
data anywhere in the app.** Where a section says "real backend data," it means
|
||||
an actual SQL-backed or live-polled endpoint, not a config file or static array.
|
||||
|
||||
### Infrastructure Page
|
||||
- Hero banner rendered at the **App.tsx layout level** (not per-page), so it can extend
|
||||
behind the sticky TopBar — conditional via `showHero = location.pathname === '/infrastructure'`.
|
||||
TopBar and the search input are transparent on hero routes so the banner shows through.
|
||||
- Hero image: `object-position: center 5%` to reveal the full arch + sky; faded out via
|
||||
`linear-gradient` mask (vertical) + a `radial-gradient(ellipse 70% 100% at center, ...)`
|
||||
overlay (sides/corners) — borderless, blends into page background.
|
||||
- Sub-tabs trimmed to **Overview only** (Compute → Tags are future work, not built yet).
|
||||
- Status cards: `rgba(10,10,12,0.5)` background (more transparent than other pages),
|
||||
content centered (`justify-content/alignItems: center`) with a fixed row height (110px)
|
||||
so there's breathing room instead of empty space below left-aligned content.
|
||||
- Middle row (`grid-cols-[1fr_1.6fr]`): **Resource Distribution** (donut) and **Node Status**
|
||||
(server tile grid). Both use the `/blank-kpi-bg.png` background art with a `cardDim`
|
||||
(semi-transparent dark overlay) + `cardVignette` (radial-gradient `closest-side` blend)
|
||||
combo — keeps the background pattern visible but subdued, with borders blended rather
|
||||
than hard-edged. Card titles are rendered as our own text, NOT baked into the image
|
||||
(baked-in labels got covered by the dim overlay).
|
||||
- **Node Status** card: originally a world-map-style region dot plot, replaced with a
|
||||
4-column tile grid (one tile per server, colored status dot + name) — a world map
|
||||
didn't make sense for a small/single-site infra. Reuse this "small-scale" reasoning
|
||||
for any future map-like cards.
|
||||
- Bottom row (`grid-cols-[1.4fr_1fr_1fr]`): Resource Trend / Cost Breakdown / Recent
|
||||
Activity — **left plain/regular**, no dim/vignette blending (explicit user preference,
|
||||
only the middle row gets the hero-style blend). Resource Trend uses the
|
||||
`/archnest-network-traffic-bg.png` background (plain, no dim/vignette) with 4 trend
|
||||
lines: blue `#3B82F6` (compute), orange `#E67E22` (storage), green `#2ECC71` (database),
|
||||
brown `#8B5E3C` (network).
|
||||
- `cardVignette` radial-gradient must use the `closest-side` keyword (not a fixed `%`)
|
||||
— otherwise straight edges of the card don't reach full opacity and a hard border line
|
||||
remains visible (only corners fade correctly with a fixed percentage).
|
||||
### Glance (`/`)
|
||||
- Hero banner (image with radial-gradient fade mask) + KPI cards overlapping
|
||||
the bottom edge via negative margin.
|
||||
- 4 status cards: **System Status** (% of integrations reachable),
|
||||
**Infrastructure** (resource count + health breakdown from
|
||||
`api.listResources()`), **Integrations** (connected/total from
|
||||
`api.listIntegrations()`), **Bookmarks** (total + favorites from
|
||||
`api.listBookmarks()`).
|
||||
- Middle row (3 columns): Resource Overview (top 6 resources + status),
|
||||
Recent Activity (5 latest from `api.listEvents(5)`), problem/critical
|
||||
resources widget.
|
||||
- Bottom row: Connected Integrations grid + 4 Shortcuts buttons (to Settings,
|
||||
BookNest, Infrastructure).
|
||||
- No footer. Hides the hero gracefully if the banner image fails to load.
|
||||
- There is **no** `systems.config`/`infra.config`/ping-sweep/fail2ban
|
||||
machinery — that was speculative spec from before the backend existed.
|
||||
Health figures are derived directly from each integration adapter's
|
||||
`testConnection()` result, polled by the existing `/api/integrations` list.
|
||||
|
||||
### BookNest Page
|
||||
- Hero banner reused at the App.tsx layout level (`showHero` now includes `/booknest`),
|
||||
with page-specific tuning via small lookup maps in `App.tsx` keyed on `location.pathname`:
|
||||
`heroPaddingTop` (how far content sits below the hero top) and `heroObjectPosition`
|
||||
(horizontal/vertical crop of the arch image) — `70px` / `54% 8%` for BookNest vs.
|
||||
`72px` / `center 5%` for Infrastructure. Extend these maps rather than hardcoding a
|
||||
single value when a future page needs different hero framing.
|
||||
- **Large hero title + subtitle**: unlike other pages, BookNest's TopBar title is NOT the
|
||||
small 18px uppercase label — it's rendered at 28px with a subtitle line ("Your Digital
|
||||
Library") underneath, driven by a new `pageSubtitles` map in `TopBar.tsx`. When a page
|
||||
has a subtitle, the header height grows from 56px → 72px (`TopBar.tsx`), and `App.tsx`'s
|
||||
`topBarHeight` lookup keeps the content section's `calc(100vh - Npx)` in sync — both
|
||||
must be updated together or the layout will clip/gap.
|
||||
- **Stats row lives directly under the hero subtitle** (Links / Categories / Favorites),
|
||||
not in its own separate bar — matches the blueprint's hero-header block grouping.
|
||||
- **"Quick Access" section label** added above the 5 quick-access category cards (gold,
|
||||
same `sectionTitle` style) — the row is intentionally pulled up via a small negative
|
||||
hero-padding tune so it slightly overlaps the bottom edge of the hero, like the blueprint.
|
||||
- **"+ Add Bookmark" button**: same gold-fill button style as Infrastructure's "+ Add
|
||||
Resource", placed inline next to the "Quick Access" label rather than the page-stats row.
|
||||
- **Right sidebar spans both grid rows** (`gridRow: '1 / span 2'` in a `gridTemplateRows:
|
||||
'auto 1fr'` grid) so the Favorites card can rise up near the hero while the main column's
|
||||
page-stats row stays in row 1 of column 1 only — keeps the two from overlapping/clipping.
|
||||
Negative margins were tried first and discarded: content pushed above the scroll
|
||||
container's natural top edge gets clipped by `overflow-y-auto`, so prefer reshaping the
|
||||
grid/flow over negative-margin hacks when something needs to "reach upward."
|
||||
- **Sidebar cards stretch to match the main column's full height** so the last card's
|
||||
(Category Breakdown) bottom border lines up with the bottom of the bookmark groups grid:
|
||||
sidebar wrapper is `display:flex; flex-direction:column; height:100%` (grid's default
|
||||
`align-items: stretch` already gives it the matching height), and each card uses
|
||||
`flex: 1 0 auto` (Favorites gets `flex: 1.4 0 auto` to read as visibly taller, per
|
||||
explicit request) so they share the leftover vertical space instead of all packing tight
|
||||
at content height.
|
||||
- lucide-react gotcha (see Global Rules candidate): the installed version does **not**
|
||||
export brand/wordmark icons (`Github`, `Gitlab`, `Linkedin`, `Youtube`) even though
|
||||
TypeScript's type declarations list them — `tsc --noEmit` stays clean while the page
|
||||
renders blank with a runtime `SyntaxError` only visible in the Vite dev log. Verify
|
||||
icon names against `Object.keys(require('lucide-react'))` before importing anything
|
||||
brand-flavored; substitutes used here: `GitBranch`/`GitFork` (GitHub/GitLab/Gitea),
|
||||
`SquarePlay` (YouTube), `Briefcase` (LinkedIn Learning).
|
||||
### Infrastructure (`/infrastructure`)
|
||||
- Hero banner at the `App.tsx` layout level (`showHero` includes this route),
|
||||
extending behind the sticky, transparent-on-hero-routes TopBar.
|
||||
- Sub-tabs: **Overview** (only enabled tab) and **Network** (disabled,
|
||||
"Coming soon" — intentional, leave alone unless asked).
|
||||
- 4 status cards: Total Resources, Healthy, Warnings, Critical — from
|
||||
`api.listResources()`.
|
||||
- Middle row: Resource Distribution (donut by integration type) + Node Status
|
||||
(4-col tile grid, one tile per resource — replaced an earlier world-map
|
||||
concept since a map doesn't make sense for a small/single-site setup).
|
||||
- Bottom row: Integration Health list + Recent Activity (`api.listEvents(4)`).
|
||||
- Card backgrounds use a `cardDim` + `cardVignette` (radial-gradient,
|
||||
`closest-side` keyword — a fixed `%` leaves a visible hard edge on straight
|
||||
sides) combo on the middle row only, per explicit visual preference; bottom
|
||||
row stays plain/regular.
|
||||
|
||||
### Settings Page
|
||||
- No mockup image existed for this page — built directly from the blueprint's Page 6
|
||||
spec rather than iterating against a screenshot.
|
||||
- Layout: fixed-width (200px) left nav listing the 6 sections (Profile, Appearance,
|
||||
Integrations, Notifications, Data & Backup, About) + a scrollable content panel on
|
||||
the right showing the active section. No hero banner (not in the blueprint spec for
|
||||
this page, and a settings page doesn't need one).
|
||||
- Active section is local component state (a string id) mapped through a
|
||||
`sectionComponents` record to the corresponding section-renderer function — simplest
|
||||
approach for a page with no routing/deep-linking requirement.
|
||||
- Shared style helpers (`cardBase`, `sectionTitle`, `labelStyle`, `inputStyle`) plus two
|
||||
small reusable components defined in the same file: `Toggle` (on/off pill switch) and
|
||||
`GoldButton` (gold-filled primary / danger-outline variant) — kept local to
|
||||
`Settings.tsx` rather than extracted, since no other page needs them yet.
|
||||
- **Integrations** cards mask secret fields (API keys/tokens) behind dots with an eye
|
||||
icon to reveal/hide, plus a "Test Connection" button per card — matches the blueprint's
|
||||
explicit "masked secrets with eye toggle" instruction.
|
||||
- **Avatar upload** (Profile section): the avatar circle is clickable and opens a hidden
|
||||
`<input type="file" accept="image/*">` via a `useRef` + `.click()` call rather than a
|
||||
visible file input — keeps the round avatar as the only visible control. On change,
|
||||
`FileReader.readAsDataURL` converts the selected image to a base64 data URL stored in
|
||||
component state, which becomes the circle's `backgroundImage` (cover-fit), replacing
|
||||
the "AO" initials fallback. A hover-only camera-icon overlay (Tailwind `group` /
|
||||
`group-hover:opacity-100`) signals the circle is clickable without cluttering the
|
||||
default state. This is a frontend-only preview (no backend upload endpoint exists yet).
|
||||
### BookNest (`/booknest`)
|
||||
- Hero banner reused at layout level with page-specific tuning
|
||||
(`heroPaddingTop`/`heroObjectPosition` lookup maps in `App.tsx`).
|
||||
- Large 28px title + "Your Digital Library" subtitle (the one page using
|
||||
`pageSubtitles`), header grows to 72px.
|
||||
- Page stats row (Links / Categories / Favorites) + "Delete All" bulk action.
|
||||
- Main column: Quick Access (top 5 categories) + bookmark groups grid (4 cols,
|
||||
full CRUD with hover edit/delete/star).
|
||||
- Right sidebar (spans both grid rows via `gridRow: '1 / span 2'`): Favorites,
|
||||
Recently Added (5 newest), Link Health donut, Category Breakdown donut.
|
||||
- Add/edit bookmark modal supports auto-detected favicons via
|
||||
`guessServiceIconUrl()` or manual icon entry.
|
||||
- All data from `api.listBookmarks()` / `api.listBookmarkCategories()` with
|
||||
real create/update/delete calls — no local-only state.
|
||||
|
||||
### Terminal (`/terminal`)
|
||||
- Left sidebar: SSH hosts (integrations of type `ssh`), click to connect.
|
||||
- Tab bar + 1/2/4-pane split layout, each pane an independent xterm instance.
|
||||
- Preferences panel (theme: ArchNest Dark/Matrix/Solarized/Midnight Blue, font
|
||||
size 11-16px, font family) — stored in `localStorage`
|
||||
(`archnest-terminal-prefs`), not synced server-side.
|
||||
- WebSocket to `/api/terminal`: `connect`/`input`/`resize`/`list_tmux`/`disconnect`
|
||||
messages; supports attaching to an existing tmux session or starting a new one.
|
||||
- Certificate auth (OPKSSH) shells out to the system `ssh` binary under a pty
|
||||
rather than using the JS SSH client.
|
||||
- Session logging to `ARCHNEST_SESSION_LOG_DIR` is backend-supported but has
|
||||
no dedicated viewer UI yet.
|
||||
|
||||
### Tunnels (`/tunnels`)
|
||||
- List + create form for local/remote/dynamic (SOCKS5) SSH tunnels.
|
||||
- Create form fields conditionally show/hide based on mode (no endpoint
|
||||
host/port fields for dynamic mode).
|
||||
- Status color map: stopped `#7A7D85`, connecting `#C8A434`, retrying
|
||||
`#E0A030`, connected `#2ECC71`, error `#E74C3C`. Polls every 3s.
|
||||
- Backed by `api.listTunnels()/createTunnel()/connectTunnel()/disconnectTunnel()/deleteTunnel()`.
|
||||
|
||||
### Files (`/files`)
|
||||
- SSH host selector + breadcrumb-navigable SFTP directory browser.
|
||||
- File editor modal: plain textarea, files up to 50MB (anything larger is
|
||||
download-only). Heuristic binary detection (null bytes/control chars) decides
|
||||
base64 vs. text encoding.
|
||||
- Inline directory creation, rename, delete, upload, download.
|
||||
- Host-to-host transfer modal: pick source file + destination host/path,
|
||||
copy-or-move toggle, live progress bar fed by `api.getTransfer(id)` polling.
|
||||
|
||||
### Containers (`/containers`)
|
||||
- Docker host selector (integrations of type `docker`) + container list.
|
||||
- Per-container state badge (running/paused/exited/dead) with context-aware
|
||||
action buttons (Start/Stop/Restart/Pause/Unpause/Remove) — buttons disable
|
||||
themselves for invalid transitions (e.g. can't pause a stopped container).
|
||||
- Live CPU/memory stats polled only for running containers.
|
||||
- Logs modal (configurable tail count) and an exec modal (interactive shell via
|
||||
WebSocket to `/api/docker/exec`).
|
||||
|
||||
### Remote Desktop (`/remote-desktop`)
|
||||
- Left sidebar: hosts from integrations of type `remote_desktop`.
|
||||
- Main area is a Guacamole canvas tunneled over WebSocket to `/api/guacamole`,
|
||||
proxying to the `guacd` sidecar container (RDP/VNC/Telnet).
|
||||
- Requires `ARCHNEST_GUAC_CRYPT_KEY` — sessions fail without it (the backend
|
||||
warns on startup if it's missing).
|
||||
|
||||
### Host Metrics (`/host-metrics`)
|
||||
- Left sidebar: SSH hosts. Selecting one starts a 5-second polling loop against
|
||||
`api.getHostMetrics(integrationId)`, cleared on deselect.
|
||||
- Cards: CPU/Memory/Disk gauges (color thresholds: green <75%, yellow 75-90%,
|
||||
red ≥90%) + Uptime, then Network Interfaces, Processes (top 10),
|
||||
Listening Ports, Firewall Rules (UFW/iptables), Login Activity.
|
||||
- Backed by 10 sequential SSH-exec collectors under `backend/src/ssh/metrics/`
|
||||
(cpu, memory, disk, uptime, network, system, processes, ports, firewall,
|
||||
login-stats) — sequential on purpose, to avoid exceeding OpenSSH's
|
||||
`MaxSessions` limit per host.
|
||||
|
||||
### Settings (`/settings`)
|
||||
- Fixed 200px left nav, 7 sections: Profile, Appearance, Security,
|
||||
Integrations, Notifications, Data & Backup, About. URL-deep-linkable via
|
||||
`?tab=` (`useSearchParams`) — use this pattern for any future section.
|
||||
- **Profile**: display name, email, avatar upload (`FileReader.readAsDataURL`
|
||||
→ base64 data URL, persisted via `api.updateMe()` — this one *is* a real
|
||||
backend round-trip, unlike the rest of the page below).
|
||||
- **Appearance**: accent color swatches — **local-state only, doesn't persist
|
||||
or apply anywhere** (see Known Stubs in `HANDOFF.md`).
|
||||
- **Security**: password-change form + 2FA toggle — currently placeholder UI
|
||||
pending the Phase 2 auth work in `HANDOFF.md`.
|
||||
- **Integrations**: one card per type (Proxmox/Docker/NetBird/Cloudflare/AWS/
|
||||
Uptime Kuma/Weather/Remote Desktop/SSH), each with type-specific fields
|
||||
(secrets masked, eye-toggle to reveal, "Test Connection" button). Real CRUD
|
||||
via `api.createIntegration()/updateIntegration()/deleteIntegration()/testIntegration()`.
|
||||
- **Notifications**: toggles — placeholder, no delivery mechanism (see
|
||||
Known Stubs in `HANDOFF.md`).
|
||||
- **Data & Backup**: full JSON export/import of integrations + secrets +
|
||||
bookmarks + tunnels via `backend/src/routes/data.ts` — this is the one
|
||||
endpoint that intentionally round-trips decrypted secrets, by design, for
|
||||
backup portability.
|
||||
- **About**: static version/license/links info.
|
||||
|
||||
### Help (`/help`)
|
||||
- Fully static — no backend calls. 11 guide cards (one per real page) with a
|
||||
short description and tips. Update this page whenever a new page ships.
|
||||
|
||||
### Login (`/login`) / Enrollment (`/enrollment`)
|
||||
- `Login.tsx`: username/password form → `api.login()` → `AuthContext`.
|
||||
- `Enrollment.tsx`: two-step first-run flow — (1) create the admin account via
|
||||
`api.setup()`, gated to only work while the `users` table is empty; (2)
|
||||
optional "Connect Your Services" integration onboarding grid (skippable,
|
||||
same integration form as Settings). Routed to based on
|
||||
`GET /api/system/setup-status`.
|
||||
|
||||
---
|
||||
|
||||
## Backend (added once frontend reached "good enough" state)
|
||||
## Backend Architecture (current, not aspirational)
|
||||
|
||||
- New `backend/` package (separate `package.json`/`tsconfig.json`, own `node_modules`,
|
||||
own Dockerfile) — Fastify + TypeScript, `better-sqlite3` for storage, deployed as a
|
||||
second container alongside the existing frontend container (`docker-compose.yml` now
|
||||
has `archnest` + `archnest-backend`, with a named volume for the SQLite file).
|
||||
- Auth: single-user, JWT-based (`@fastify/jwt`). `POST /api/setup` creates the one user
|
||||
and only succeeds while the `users` table is empty — this is what powers the
|
||||
first-run enrollment page. `GET /api/system/setup-status` tells the frontend whether
|
||||
to show enrollment/login or the normal app.
|
||||
- Integration credentials are split across two tables: `integrations` (type, name,
|
||||
status, non-secret `config_json`) and `secrets` (per-key AES-256-GCM-encrypted
|
||||
values, key derived from the `ARCHNEST_SECRET_KEY` env var) — keeps secrets out of any
|
||||
generic "list integrations" query/response by construction, not by remembering to
|
||||
redact a field.
|
||||
- Each integration type has an adapter module under `backend/src/integrations/`
|
||||
exporting `testConnection(config, secrets)`; `registry.ts` maps `IntegrationType` →
|
||||
adapter. Only `uptime_kuma` and `docker` are real so far (simple HTTP health checks);
|
||||
the rest return a "not yet implemented" result until built out — this lets the
|
||||
Integrations UI and `POST /api/integrations/:id/test` endpoint work end-to-end for
|
||||
every type without blocking on every adapter being finished.
|
||||
- Vite dev server proxies `/api` → `http://localhost:4000` (`vite.config.ts`) so the
|
||||
frontend can call relative `/api/...` paths in both dev and prod (prod routes `/api`
|
||||
to the backend container via NPM).
|
||||
- Next steps (not yet done): build the enrollment/login frontend pages, strip the mock
|
||||
arrays out of Glance/Infrastructure/BookNest/Settings and replace with calls to this
|
||||
API, add bookmark category seeding.
|
||||
- `backend/` — Fastify 5 + TypeScript (ESM, `tsx` dev / `tsc -b` build),
|
||||
`better-sqlite3` for storage, deployed as its own Docker container alongside
|
||||
the frontend and a `guacd` sidecar.
|
||||
- Auth: single-user-schema JWT (`@fastify/jwt`) today — see `HANDOFF.md` for
|
||||
the multi-user/SSO roadmap (Phases 2-4, not yet built).
|
||||
- Integration credentials split across `integrations` (non-secret config) and
|
||||
`secrets` (AES-256-GCM-encrypted, keyed by `ARCHNEST_SECRET_KEY`) tables —
|
||||
secrets never appear in a generic "list integrations" response by
|
||||
construction, not by remembering to redact a field.
|
||||
- Every integration type has an adapter in `backend/src/integrations/`
|
||||
implementing `testConnection()` (required) and `listResources()` (optional);
|
||||
`registry.ts` maps `IntegrationType` → adapter. All 9 types
|
||||
(proxmox/docker/netbird/cloudflare/aws/uptime_kuma/weather/ssh/remote_desktop)
|
||||
are real, working adapters — none are stubs anymore.
|
||||
- `backend/src/ssh/` is the shared SSH transport layer powering Terminal,
|
||||
Files, Tunnels, Transfers, and Host Metrics: `connect.ts` (jump-host
|
||||
chaining, host-key verification, cert auth), `sftp.ts` (ephemeral SFTP),
|
||||
`transfer.ts` (host-to-host streamed copy/move with progress + cancel), and
|
||||
`metrics/` (the 10 collectors listed above).
|
||||
- Vite dev server proxies `/api` → `http://localhost:4000`; prod routes `/api`
|
||||
to the backend container via Nginx Proxy Manager.
|
||||
|
||||
## Future Integration Notes
|
||||
|
||||
### Live Provider Data (AWS, Linode, etc.)
|
||||
- All KPI/status card data (resource counts, health, pricing, budgets, cost breakdowns,
|
||||
utilization, regions/map data) is currently mocked/static.
|
||||
- The Infrastructure page (and likely Glance) should eventually integrate with real
|
||||
cloud provider APIs — AWS, Linode, or any other VPC/cloud provider — via user-supplied
|
||||
API keys, to pull live data such as:
|
||||
- Resource inventory/counts and health status
|
||||
- Pricing and budget/cost data (replacing the static Cost Breakdown numbers)
|
||||
- Resource utilization metrics
|
||||
- Region/datacenter info for the Infrastructure Map
|
||||
- Design the data layer so it's provider-agnostic (a common interface/adapter per
|
||||
provider) since users may connect more than one provider's API key.
|
||||
- AWS/Cloudflare/NetBird/Proxmox/Uptime Kuma adapters are real but currently
|
||||
surface basic resource inventory + health only — deeper cost/pricing/budget
|
||||
data (mentioned in the old blueprint) is not implemented and not currently
|
||||
planned; revisit only if explicitly requested.
|
||||
|
|
|
|||
356
glance.md
356
glance.md
|
|
@ -1,356 +0,0 @@
|
|||
# Glance Page — Detailed Specification
|
||||
|
||||
> Purpose: The Glance page is the operational heartbeat of ArchNest. It provides an at-a-glance view of system health, resource utilization, security posture, and network connectivity across your homelab infrastructure. Every element answers a simple question: "Is everything okay right now?"
|
||||
|
||||
---
|
||||
|
||||
## Layout Structure
|
||||
|
||||
The Glance page uses this vertical stack (top to bottom):
|
||||
1. **Top Bar** (sticky, 56px)
|
||||
2. **Hero Banner** (200px, rounded, with KPI cards overlapping the bottom ~25%)
|
||||
3. **Middle Row** (3 columns: 30% | 40% | 30%)
|
||||
4. **Bottom Row** (2 columns: 65% | 35%)
|
||||
|
||||
**No footer.** The page scrolls naturally without a fixed status bar at the bottom.
|
||||
|
||||
---
|
||||
|
||||
## Top Bar
|
||||
|
||||
**Display:**
|
||||
- Height: 56px, sticky at top, z-index above all content
|
||||
- Background: Page color (#0D0E10), no border
|
||||
- Left: Page title "GLANCE" (18px, bold, uppercase, primary text with subtle gold drop-shadow glow)
|
||||
- Center-right: Search bar (260px, rounded-full, placeholder "Search resources...", card background)
|
||||
- Right: Notification bell (with red badge count) + User avatar + dropdown trigger
|
||||
|
||||
### User Avatar & Dropdown Menu
|
||||
|
||||
**Avatar Display:**
|
||||
- 32px circle with 2px gold border and subtle gold glow
|
||||
- Shows initials "AO" in gold
|
||||
- Adjacent: "ArchNest Ops" (12px, primary) + "Administrator" (9px, secondary)
|
||||
- Chevron icon (rotates on open)
|
||||
|
||||
**Dropdown Menu (on click):**
|
||||
- Position: Below avatar, aligned right
|
||||
- Background: Card color with border, rounded-xl, shadow
|
||||
- Header section: User name + email
|
||||
- Menu items:
|
||||
- **Profile** — navigates to Settings > Profile section
|
||||
- **Appearance** — navigates to Settings > Appearance section
|
||||
- **Security** — navigates to Settings > Security/Integrations section
|
||||
- **Help & Support** — opens documentation/wiki link
|
||||
- Divider
|
||||
- **Sign Out** — logs out of session (red text, danger action)
|
||||
|
||||
---
|
||||
|
||||
## Hero Banner
|
||||
|
||||
**Display:**
|
||||
- Full width of content area, 200px height, 12px border radius, overflow hidden
|
||||
- Image: `archnest-hero-banner.png` from `/public` (local) or CDN
|
||||
- Image positioning: `object-cover` with `object-position: center 30%` — prioritizes showing the upper portion (skyline/arch) rather than center-cropping
|
||||
- The bottom ~25% of the banner is overlapped by the KPI status cards (negative margin)
|
||||
- If image fails to load: shows card background color (#141518), no broken icon
|
||||
|
||||
---
|
||||
|
||||
## Top Row — Status KPI Cards (4 cards)
|
||||
|
||||
### Card Grid Layout
|
||||
|
||||
**IMPORTANT — Asymmetric widths:**
|
||||
- KPI 1 (System Status): **1.3fr** — wider, has progress ring + sparkline
|
||||
- KPI 2 (Infrastructure): **1fr** — standard width
|
||||
- KPI 3 (Security): **1fr** — standard width
|
||||
- KPI 4 (Network): **1.3fr** — wider, has sparkline chart
|
||||
|
||||
Grid: `grid-cols-[1.3fr_1fr_1fr_1.3fr]` with 12px gap.
|
||||
|
||||
**Card styling (all 4):**
|
||||
- Background: `#141518` at 95% opacity with backdrop-blur (glass effect over banner)
|
||||
- Border: 1px solid #1E2025, 12px radius
|
||||
- Padding: 16px
|
||||
- Hover: border transitions to gold (0.2s)
|
||||
|
||||
---
|
||||
|
||||
### 1. System Status
|
||||
|
||||
**What it represents:** Overall system health — a composite "green light" that confirms all critical services are reachable and all local packages/security updates are current.
|
||||
|
||||
**How it gets its data:**
|
||||
- **Ping sweep**: Every 5 minutes, the backend pings all endpoints defined in a `systems.config` file (IP addresses of LXC containers, VMs, physical hosts, and key services). If all respond, health = 100%.
|
||||
- **Package currency check**: Queries `apt` (or equivalent) on monitored hosts for pending security updates. Any pending security update reduces the percentage.
|
||||
- **Calculation**: `(reachable_hosts / total_hosts) * weight_A + (up_to_date_hosts / total_hosts) * weight_B`. Default weights: 70% reachability, 30% package currency.
|
||||
|
||||
**Display:**
|
||||
- Card title: "SYSTEM STATUS" (10px, uppercase, tracking 1.5px, secondary color, font-medium)
|
||||
- Layout: Title top, then flex row with text left and ring right
|
||||
- Left content:
|
||||
- "All Systems" (13px, bold, primary)
|
||||
- "Operational" (13px, bold, gold, italic)
|
||||
- Right content: Progress Ring (44px diameter, 3px stroke, gold on dark track)
|
||||
- Divider: 1px border-top, border/60 opacity
|
||||
- Below divider: Sparkline (gold line chart, 20px height, no axes, showing last 12 check results)
|
||||
- Footer text: "Last checked: 2m ago" (9px, secondary)
|
||||
|
||||
**Thresholds:**
|
||||
- 100%: "All Systems Operational" (gold text)
|
||||
- 80–99%: "Degraded" (orange text)
|
||||
- Below 80%: "Critical" (red text)
|
||||
|
||||
---
|
||||
|
||||
### 2. Infrastructure
|
||||
|
||||
**What it represents:** Total count of managed resources (LXC containers, VMs, Docker containers, bare-metal hosts) and their operational state.
|
||||
|
||||
**How it gets its data:**
|
||||
- **Config-driven**: Reads from `infra.config` which contains API endpoints, SSH keys, and connection details for each resource (Proxmox API for LXC/VMs, Docker socket/API for containers, SSH for bare-metal).
|
||||
- **Status polling**: Every 5 minutes, queries each resource's API or SSH to determine if it's running, stopped, or unreachable.
|
||||
- **Disk usage check**: Pulls primary disk utilization from each resource. Any resource exceeding 70% disk usage triggers a warning signal.
|
||||
|
||||
**Display:**
|
||||
- Card title: "INFRASTRUCTURE" (10px, uppercase, tracking 1.5px, secondary color)
|
||||
- Icon + number row: Server icon (16px, gold) + "24" (24px, bold, primary)
|
||||
- Subtitle: "Total Resources" (10px, secondary)
|
||||
- Divider
|
||||
- Breakdown row (9px): green dot + "24 Running" | yellow dot + "0 Warning" | red dot + "0 Critical"
|
||||
|
||||
**Signal logic:**
|
||||
- 🟢 Running: Resource is responsive and disk < 70%
|
||||
- 🟡 Warning: Resource is responsive but disk ≥ 70%, or resource response is slow (>2s)
|
||||
- 🔴 Critical: Resource is unreachable or disk ≥ 90%
|
||||
|
||||
---
|
||||
|
||||
### 3. Security
|
||||
|
||||
**What it represents:** Active security alerts — failed intrusion attempts, brute-force attacks detected by fail2ban, and outdated security packages.
|
||||
|
||||
**How it gets its data:**
|
||||
- **Fail2ban logs**: Queries fail2ban on each monitored host for currently banned IPs and recent ban events (last 24h).
|
||||
- **Auth log monitoring**: Parses `/var/log/auth.log` (or equivalent) for failed login attempts exceeding a threshold (e.g., 5+ failures from same IP in 10 minutes).
|
||||
- **Security package check**: Counts hosts with pending security-specific updates (`apt list --upgradable` filtered to security repo).
|
||||
- **Alert count**: Sum of active fail2ban bans + hosts with outdated security packages.
|
||||
|
||||
**Display:**
|
||||
- Card title: "SECURITY" (10px, uppercase, tracking 1.5px, secondary color)
|
||||
- Icon + number row: Shield icon (16px, gold) + "2" (24px, bold, primary)
|
||||
- Subtitle: "Active Alerts" (10px, secondary)
|
||||
- Divider
|
||||
- Breakdown row (9px): green dot + "2 Low" | yellow dot + "0 Medium" | red dot + "0 High"
|
||||
|
||||
**Severity logic:**
|
||||
- Low (green): Informational — single failed login attempt, package update available
|
||||
- Medium (yellow): Multiple failed attempts from same IP, security package >7 days overdue
|
||||
- High (red): Active brute-force attack (10+ attempts in 5 min), critical vulnerability unpatched >14 days
|
||||
|
||||
---
|
||||
|
||||
### 4. Network
|
||||
|
||||
**What it represents:** Local network uptime — confirms internet connectivity and DNS resolution are functioning.
|
||||
|
||||
**How it gets its data:**
|
||||
- **Ping probes**: Every 60 seconds, pings multiple resolvers (e.g., 1.1.1.1, 8.8.8.8, and one internal DNS) from the ArchNest host.
|
||||
- **Uptime calculation**: Tracks successful/failed pings over a rolling 24-hour window. Uptime % = (successful_pings / total_pings) * 100.
|
||||
- **Sparkline data**: Stores the last 24 data points (one per hour) for the mini trend chart.
|
||||
|
||||
**Display:**
|
||||
- Card title: "NETWORK" (10px, uppercase, tracking 1.5px, secondary color)
|
||||
- Icon + number row: Network icon (16px, gold) + "98.7%" (24px, bold, primary)
|
||||
- Subtitle: "Network Uptime" (10px, secondary)
|
||||
- Divider
|
||||
- Below divider: Area sparkline chart (gold gradient fill, 24px height, no axes, 24 data points representing hourly uptime)
|
||||
|
||||
**Thresholds:**
|
||||
- ≥99%: Healthy (no indicator needed)
|
||||
- 95–99%: Minor instability (orange sparkline highlight)
|
||||
- <95%: Degraded (red sparkline highlight, triggers alert)
|
||||
|
||||
---
|
||||
|
||||
## Middle Row — Detail Panels (3 columns: 30% | 40% | 30%)
|
||||
|
||||
### 5. Resource Overview (left, 30%)
|
||||
|
||||
**What it represents:** Top 5 resource utilization metrics across your infrastructure — a quick view of where capacity is being consumed.
|
||||
|
||||
**How it gets its data:**
|
||||
- Pulls from the same infrastructure polling that feeds the Infrastructure KPI
|
||||
- **Compute**: Running LXC/VM count vs total allocated slots (from infra.config)
|
||||
- **Storage**: Sum of used disk across all storage pools vs total capacity (e.g., Proxmox storage API, `df` on hosts)
|
||||
- **Database**: Active database instances vs provisioned (PostgreSQL/MariaDB connection count or instance count)
|
||||
- **Network**: Same uptime % from the Network KPI (displayed as a bar for consistency)
|
||||
- **Containers**: Running Docker containers vs total defined in docker-compose or container configs
|
||||
|
||||
**Display:**
|
||||
- Card title: "RESOURCE OVERVIEW" (uppercase, secondary color)
|
||||
- Close button (X) in top-right corner
|
||||
- 5 rows, each containing:
|
||||
- Icon (category-specific, 16px, secondary color)
|
||||
- Label (e.g., "Compute", "Storage") — 13px, primary color
|
||||
- Progress bar (gold fill on dark track, rounded ends)
|
||||
- Value text (e.g., "18 / 24" or "12.4 / 20 TB") — 12px, secondary color
|
||||
|
||||
**Bar color logic:**
|
||||
- 0–69%: Gold (#C8A434)
|
||||
- 70–89%: Warning orange (#E67E22)
|
||||
- 90–100%: Danger red (#E74C3C)
|
||||
|
||||
---
|
||||
|
||||
### 6. Recent Activity (center, 40%)
|
||||
|
||||
**What it represents:** A chronological feed of the most recent system events — gives context on what just happened across the infrastructure.
|
||||
|
||||
**How it gets its data:**
|
||||
- **Event aggregation**: Collects events from multiple sources:
|
||||
- Backup completion notifications (from cron jobs or backup tools like restic/borgbackup)
|
||||
- Security scan results (from scheduled ClamAV/rkhunter scans)
|
||||
- Instance state changes (container start/stop/create from Proxmox/Docker APIs)
|
||||
- Configuration changes (git commits to infra-as-code repos, or config file modification timestamps)
|
||||
- Authentication events (successful logins from auth.log)
|
||||
- **Storage**: Events stored in a lightweight local database (SQLite or JSON file) with timestamp, type, title, source, and severity.
|
||||
|
||||
**Display:**
|
||||
- Card title: "RECENT ACTIVITY" (uppercase, secondary color)
|
||||
- Close button (X) in top-right corner
|
||||
- 5 items, each containing:
|
||||
- Icon (event-type-specific: checkmark for completion, shield for security, play for launch, gear for config, user for login) — 14px, in a small rounded container (28px, page background)
|
||||
- Event title (13px, bold, primary color) — e.g., "Backup completed"
|
||||
- Source subtitle (11px, secondary color) — e.g., "Database Cluster 01"
|
||||
- Relative timestamp (11px, secondary color, right-aligned) — e.g., "2m ago"
|
||||
|
||||
**Event ordering:** Newest first, max 5 displayed.
|
||||
|
||||
---
|
||||
|
||||
### 7. Top Alerts (right, 30%)
|
||||
|
||||
**What it represents:** The most urgent issues requiring attention — prioritized by severity then recency.
|
||||
|
||||
**How it gets its data:**
|
||||
- **Alert aggregation**: Combines alerts from:
|
||||
- High CPU/RAM usage events (from resource polling — any resource >85% CPU or >90% RAM for 5+ minutes)
|
||||
- Disk space warnings (from Infrastructure polling — any disk >70%)
|
||||
- Security events (from fail2ban/auth.log — active attacks)
|
||||
- SSL certificate expiry (checks cert expiry dates for tracked domains, alerts at 30/14/7 days)
|
||||
- Service down events (from System Status polling — any unreachable service)
|
||||
|
||||
**Display:**
|
||||
- Card title: "TOP ALERTS" (uppercase, secondary color)
|
||||
- "View all" link (gold, top-right) — navigates to a full alerts view
|
||||
- Up to 4 items, each containing:
|
||||
- Severity dot (🔴 red/large for high, 🟡 yellow for medium) — 6px diameter
|
||||
- Alert title (13px, primary color, font-medium) — e.g., "High CPU Usage"
|
||||
- Source subtitle (11px, secondary color) — e.g., "App Server 02"
|
||||
- Relative timestamp (11px, secondary color, right-aligned) — e.g., "2m ago"
|
||||
|
||||
**Sort order:** High severity first, then by recency within same severity level.
|
||||
|
||||
---
|
||||
|
||||
## Bottom Row — Charts & Actions (2 columns: 65% | 35%)
|
||||
|
||||
### 8. Network Traffic (left, 65%)
|
||||
|
||||
**What it represents:** Visual representation of network throughput over the last 24 hours — helps spot unusual spikes or drops in traffic.
|
||||
|
||||
**How it gets its data:**
|
||||
- **Interface monitoring**: Reads network interface stats (bytes in/out) from the primary gateway or router. Options:
|
||||
- SNMP polling from router/switch
|
||||
- `vnstat` on the ArchNest host or gateway
|
||||
- Proxmox node network stats API
|
||||
- **Sampling**: Records bytes in/out every 5 minutes, calculates Mbps/Gbps rate
|
||||
- **Current values**: Latest sample provides the "Incoming X.XX Gbps" and "Outgoing X.XX Gbps" figures
|
||||
- **Trend calculation**: Compares current hour's average to same hour yesterday for the percentage change (↑/↓)
|
||||
|
||||
**Display:**
|
||||
- Card title: "NETWORK TRAFFIC" (uppercase, secondary color)
|
||||
- Background: Custom background image (`archnest-network-traffic-bg.png`) rendered at ~20% opacity behind the chart, giving the card a unique visual identity
|
||||
- Area chart (overlaid on background):
|
||||
- Fill: Gold/amber gradient (top: #C8A434 at ~30% opacity, fading to transparent)
|
||||
- Line: Gold (#C8A434) for inbound, amber (#E67E22) for outbound
|
||||
- X-axis: 24-hour span (no visible labels)
|
||||
- Y-axis: Auto-scaled (no visible labels)
|
||||
- Stats (right side of chart, vertically stacked):
|
||||
- "Incoming" label (11px, secondary) + "1.23 Gbps" (18px, bold, primary) + "↓ 12.4%" (11px, red)
|
||||
- "Outgoing" label (11px, secondary) + "1.08 Gbps" (18px, bold, primary) + "↑ 8.7%" (11px, green)
|
||||
|
||||
---
|
||||
|
||||
### 9. Shortcuts (right, 35%)
|
||||
|
||||
**What it represents:** Quick-action buttons for common administrative tasks — one-click access to frequent operations.
|
||||
|
||||
**How it works:**
|
||||
- Each shortcut triggers a predefined action or navigates to a specific workflow:
|
||||
- **Add Server**: Opens a form/modal to add a new resource to `infra.config` (host IP, type, credentials)
|
||||
- **Create Backup**: Triggers an on-demand backup job for a selected resource or all resources
|
||||
- **Deploy App**: Opens a deployment workflow (e.g., pull latest docker-compose, restart containers)
|
||||
- **View Logs**: Navigates to a log viewer or opens a terminal session with log tailing
|
||||
|
||||
**Display:**
|
||||
- Card title: "SHORTCUTS" (uppercase, secondary color)
|
||||
- 4 buttons in a horizontal row:
|
||||
- Each button: Outlined/stroked icon inside a bordered rounded container (40px)
|
||||
- Icon style: Lucide outlined icons (18px), secondary color, gold on hover
|
||||
- Label below icon: 10px, secondary color, centered
|
||||
- Container: 1px border (#1E2025), 8px radius, hover → gold border + gold icon
|
||||
|
||||
---
|
||||
|
||||
## Data Refresh & Polling Summary
|
||||
|
||||
| Data Source | Poll Interval | Used By |
|
||||
|-------------|---------------|---------|
|
||||
| System ping sweep | 5 minutes | System Status KPI |
|
||||
| Package update check | 1 hour | System Status KPI |
|
||||
| Infrastructure resource status | 5 minutes | Infrastructure KPI, Resource Overview |
|
||||
| Disk usage per resource | 5 minutes | Infrastructure KPI, Resource Overview, Top Alerts |
|
||||
| Fail2ban / auth.log | 2 minutes | Security KPI, Top Alerts |
|
||||
| Security package check | 1 hour | Security KPI |
|
||||
| Network ping probes | 60 seconds | Network KPI |
|
||||
| Network interface throughput | 5 minutes | Network Traffic chart |
|
||||
| Event log (activity) | Real-time (push) or 1 minute | Recent Activity |
|
||||
| SSL cert expiry | 24 hours | Top Alerts |
|
||||
| CPU/RAM per resource | 5 minutes | Top Alerts |
|
||||
|
||||
---
|
||||
|
||||
## Configuration Dependencies
|
||||
|
||||
| Config File | Purpose |
|
||||
|-------------|---------|
|
||||
| `systems.config` | List of IPs/hosts to ping for System Status |
|
||||
| `infra.config` | Resource definitions with API endpoints, SSH keys, connection types |
|
||||
| `alerts.config` | Threshold definitions (disk %, CPU %, failed login count, cert days) |
|
||||
| `network.config` | Resolver IPs to ping, interface to monitor for traffic |
|
||||
|
||||
---
|
||||
|
||||
## Interaction Behaviors
|
||||
|
||||
| Element | Action | Result |
|
||||
|---------|--------|--------|
|
||||
| X button (Resource Overview) | Click | Hides the card for current session |
|
||||
| X button (Recent Activity) | Click | Hides the card for current session |
|
||||
| "View all" (Top Alerts) | Click | Navigates to full alerts list view |
|
||||
| Shortcut button | Click | Triggers associated action/workflow |
|
||||
| Status card | Hover | Gold border transition (0.2s) |
|
||||
| Progress ring | Page load | Animates from 0 to value (1s) |
|
||||
| Sparkline | Page load | Draws line animation (1s) |
|
||||
| Progress bars | Page load | Fill animation (0.8s staggered) |
|
||||
| User avatar/chevron | Click | Opens/closes user dropdown menu |
|
||||
| Dropdown: Profile | Click | Navigates to Settings > Profile |
|
||||
| Dropdown: Appearance | Click | Navigates to Settings > Appearance |
|
||||
| Dropdown: Security | Click | Navigates to Settings > Security |
|
||||
| Dropdown: Help & Support | Click | Opens docs/wiki link |
|
||||
| Dropdown: Sign Out | Click | Ends session, returns to login |
|
||||
| Click outside dropdown | Click | Closes the dropdown menu |
|
||||
Loading…
Add table
Reference in a new issue