Full-stack implementation: FastAPI backend with PAM auth, WebSocket stats/terminal, and vanilla JS frontend with tiling desktop shell. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
23 KiB
Atlus — Project Context
What Is Atlus?
Atlus is a lightweight, web-based desktop environment designed for headless single-board computers (SBCs) such as the Orange Pi, Raspberry Pi, and similar ARM-based Linux systems. It provides a browser-accessible interface that replaces the need for SSH or VNC for everyday system management tasks.
The goal is not to replicate a full desktop environment — it is to provide the right tools for managing a headless SBC in a way that feels natural, fast, and accessible from any device on the local network.
Problem Statement
Headless SBCs are increasingly used as always-on servers, astrophotography rigs, NAS boxes, home automation hubs, and more. Current management options are:
- SSH — powerful but unfriendly for non-developers and tedious for routine tasks
- VNC / noVNC — requires a full desktop environment, heavy, slow, not purpose-built
- Cockpit / Webmin — generic server admin tools, not tailored to SBC use cases, complex UI
There is no lightweight, purpose-built web desktop for SBCs that feels like an OS rather than a server admin panel.
Core Philosophy
- Web-native first — no X server required for core functionality
- Purposefully minimal — only the tools that matter for headless SBC management
- Locally hosted — runs entirely on the SBC, no cloud dependency
- Accessible from anything — phone, tablet, laptop — any browser on the local network
- Tiling over floating — apps open in the center stage, not as draggable windows
- Extensible — apps/widgets can be added as plugins
Target Users
- Makers and hobbyists running headless SBCs for specific purposes (astrophotography, NAS, IoT, etc.)
- Home lab users who want lightweight management without full desktop overhead
- Anyone who currently lives in SSH and wishes there was something better
✅ Finalized Layout — Three-Column Shell
The Atlus UI is a fixed three-column layout inspired by tiling window managers (awesomewm, i3). There are no draggable or floating windows. Everything is tiled, cascaded, or tabbed within the center stage.
┌────────┬─────────────────────────────┬────────────────┐
│ │ Stage Tab Bar │ │
│ Left ├─────────────────────────────┤ Right Panel │
│ Dock │ │ │
│ │ Center Stage │ - Hostname │
│ App │ (active app view) │ - Date/Time │
│ Icons │ │ - Network │
│ │ One app at a time, │ - CPU/RAM/ │
│ (vert) │ or tiled side-by-side │ Disk/Temp │
│ │ │ - Services │
│ Gear ├─────────────────────────────┤ (toggles + │
│ (foot) │ Layout Controls (tab bar) │ open → ) │
└────────┴─────────────────────────────┴────────────────┘
Left Dock
- Vertical strip of app launcher icons — no labels, tooltip on hover
- Top: Atlus "A" logo — opens a system menu (shutdown, restart, about)
- Core apps listed in order (configurable in settings)
- Separator line between core apps and plugins
- Bottom: Settings gear icon (always pinned)
- Active app highlighted with accent border/background
- Badge dot on plugin icons when they have status to report (e.g. ASI Bridge syncing)
Center Stage
- The primary content area — full height, fills remaining width
- One app visible at a time by default
- Tab bar at top shows all open apps; click to switch (previous app minimizes)
- Layout toggle buttons (top-right of tab bar) switch between:
- Single pane (full width)
- Horizontal split (two panes side-by-side, 50/50 default)
- Vertical split (stacked, stretch goal)
- Each pane has its own macOS-style titlebar (traffic light dots + title)
- When a second app is opened in split mode, it takes the secondary pane
Right Panel
- Always visible, fixed width (~220px), never replaced by an app
- Top section: Hostname, date, time (live), WiFi name + status, Ethernet status
- Middle section: Live system stats — CPU %, Memory used/total, Storage %, CPU temp
- Each stat: compact uppercase label + current value + colored progress bar
- Colors: green (healthy), amber (moderate), red (high)
- Bottom section: Services list
- Each service: toggle (on/off), name, ↗ open button
- Toggle maps to
systemctl start/stop - ↗ opens that service's detail/control view in the center stage
- Services list is configurable per deployment
Architecture Overview
Backend Daemon
- Language: Python (FastAPI + asyncio)
- Runs as a systemd service on boot
- Exposes a REST API and WebSocket endpoint
- Responsibilities:
- File system access and management
- Systemd service control (start/stop/restart/status)
- Process/task enumeration
- System stats (CPU, RAM, disk, network, temperature) via psutil
- Notification/event bus (watches logs, mount points, service states)
- X11 app process spawner (Xvfb + x11vnc per app — Phase 4 only)
Frontend
- Language: Vanilla JS + CSS (no framework — keep it lightweight and portable)
- No build step — served directly as static files by the FastAPI backend
- Communicates via REST for actions, WebSocket for live stat updates
- CSS custom properties for theming
- IBM Plex Mono for system data; Inter for UI chrome
X11 App Embedding (Phase 4 — Optional/Advanced)
- For GUI apps that require X (e.g., Nextcloud desktop client)
- Each app spawns its own Xvfb virtual framebuffer
- x11vnc streams that framebuffer over WebSocket via noVNC
- Embedded as a pane in the center stage — not a floating window
- Isolated per-app — no shared display, no full desktop environment needed
Built-in Apps (Core)
⚙️ System Settings
The unified configuration hub for the SBC. Organized into sections:
- General — System name/hostname, date & time (manual or NTP), timezone
- Users — User management, password changes, add/remove users
- Security — Screen lock timeout, session timeout, auth settings
- Applications — Choose which apps appear in the dock; enable/disable on startup
- Services — Which systemd services appear in the right panel; autostart configuration
- Network — Per-interface settings (WiFi SSID/password, static IP vs DHCP, DNS); shown for all detected interfaces (eth0, wlan0, etc.)
- Storage / Automount — USB automount rules, mount point configuration, eject behavior
- About — Atlus version, system info, OS details
📟 Terminal
A fully functional web-based terminal. No restrictions, no simplifications — behaves identically to SSH or a local terminal session. Powered by xterm.js on the frontend and ptyprocess on the backend for a real PTY.
Custom On-Screen Keyboard (Critical)
The system keyboard (iOS/Android) is never triggered. Atlus uses its own purpose-built on-screen keyboard for all terminal input. This is non-negotiable — the native keyboard is hostile to terminal use (autocorrect, autocapitalize, no Escape, no function keys, unpredictable behavior).
How it works technically:
- The xterm.js terminal canvas is focused but never triggers a system keyboard (it is not an
<input>or<textarea>) - All key input is intercepted via the custom keyboard UI — keys send characters directly to the terminal via
terminal.input(char)and escape sequences for special keys - The terminal pane never calls
.focus()on any element that would summon the system keyboard inputmode="none"set on any focusable elements within the terminal pane as a belt-and-suspenders measure
Custom keyboard layout — three rows:
Row 1 (special/modifier):
[ Esc ] [ Tab ] [ Ctrl ] [ Alt ] [ ` ] [ ~ ] [ | ] [ \ ] [ / ]
Row 2 (function keys, scrollable):
[ F1 ] [ F2 ] [ F3 ] [ F4 ] [ F5 ] [ F6 ] [ F7 ] [ F8 ] [ F9 ] [ F10 ] [ F11 ] [ F12 ]
Row 3 (navigation):
[ ↑ ] [ ↓ ] [ ← ] [ → ] [ Home ] [ End ] [ PgUp ] [ PgDn ] [ Del ]
Modifier key behavior:
- Ctrl, Alt are sticky — tap once to arm, next key sends the combo (e.g. Ctrl → C sends
\x03) - Armed modifier keys are visually highlighted
- Double-tap a modifier to lock it (Ctrl+lock for vim, etc.)
Standard alphanumeric input:
- A compact QWERTY layout below the special rows handles letter/number/symbol input
- Numbers row across the top of the QWERTY section
- Shift key for capitals — also sticky
- Common terminal symbols promoted to easy reach:
$,#,>,-,_,.
Keyboard can be:
- Toggled show/hide via a keyboard icon button in the terminal pane titlebar
- Resized (compact vs. full) — compact shows only the special row + QWERTY condensed
- On physical keyboard connected to iPad: custom keyboard hides, physical keys work natively through xterm.js
Other terminal features:
- Full color output, cursor control, scrollback
- Resize-aware (terminal resizes with the pane, keyboard height accounted for)
- Copy: long-press selection in terminal → copy action
- Paste: paste button on keyboard toolbar injects clipboard content via
terminal.input() - Multiple terminal tabs within the terminal pane (Phase 2)
🗂 File Manager
Unique dual-pane design. Key characteristics:
- Single or dual folder view — toggle between one panel (full width) and two side-by-side panels
- List view only — no icon grid; each row shows: icon type indicator, filename, size, modified date, permissions
- Drag and drop — files can be dragged between panels or into folders within a panel
- Sidebar — shows filesystem tree: home, root, and all detected mounts (USB, SMB, etc.)
- Features per file/selection:
- Preview (text, image, FITS header for astrophotography files)
- Compress / extract (zip, tar.gz)
- Copy, move, rename, delete
- Properties (permissions, owner, size on disk)
- Mount management — right-click a mount point to unmount; USB devices show an eject option
- Path bar — editable breadcrumb path at the top of each panel
- No thumbnails — performance-first; list only
📊 Task Manager
htop-inspired, but scoped and web-native. Shows:
- Process list: PID, name, CPU %, MEM %, status, user
- Sortable columns
- Kill / signal process action
- Filter to show only Atlus-relevant processes (or toggle to show all)
- Summary bar at top: total CPU, RAM, load average — mirrors the right panel stats but with more detail
- Auto-refreshes every 2 seconds via WebSocket
🌐 Network Monitor
Per-interface detail view:
- Interface name, MAC, state (up/down)
- IP addresses (IPv4 + IPv6)
- Gateway, DNS servers
- Live throughput (bytes in/out per second)
- For WiFi: SSID, signal strength, frequency band
🔧 Settings
(see System Settings above — Settings is the dock entry point into the System Settings app)
Plugin Apps (Installable)
| App | Dock Icon | Description |
|---|---|---|
| ASI Bridge | 🔭 |
ASI Air mount status, transfer log, Nextcloud sync, session summary |
| Log Viewer | 📝 |
Real-time tail of any systemd service log via WebSocket |
| INDI Control | (future) | INDI server management for astrophotography rigs |
Design Language
Aesthetic
- Dark, utilitarian, refined — high-end terminal meets modern OS
- Inspired by: awesomewm, macOS terminal, Linear app
- Flat — no gradients, no heavy shadows, no decorative elements
Typography
- IBM Plex Mono — all system data, file paths, IPs, timestamps, terminal output
- Inter — UI chrome, labels, app names, navigation
- Two weights only: 400 regular, 500 medium
Color System
- Background layers (dark → light):
#0d0f14→#111318→#161b27 - Borders:
#1e2130(structural),#2a2d36(component-level) - Text:
#c8ccd8(primary),#8891a8(secondary),#4a5068(muted),#2e3348(ghost) - Accent (active/selected):
#6ea6f0 - Status:
#3ab86a(ok/green),#e09a2a(warning/amber),#e05a4a(error/red)
Motion
- Subtle only: tab switches, service toggle state, stat bar transitions
- Status values update live via WebSocket — no manual refresh needed
✅ Touch-First Design — Primary Target: iPad & Tablets
Atlus is touch-first. Desktop mouse/keyboard is secondary. Every interaction must be comfortable with a finger on a tablet screen. This affects every component.
Touch Target Sizing
- Minimum tap target: 48×48px for all interactive elements (Apple HIG / Material guidelines)
- Dock icons: 56×56px minimum hit area
- File rows: minimum 48px tall
- Settings rows: minimum 48px tall
- Service toggles: at least 44px tall hit area (even if visually smaller)
- Tab bar items: minimum 44px tall
- All buttons: minimum 44px tall
No Hover Dependency
- No tooltips as primary labels — dock icons must show a label below the icon (not on hover)
- No hover-only states for anything critical
- All context menus triggered by long-press, not right-click
- Hover styles may exist as a progressive enhancement for mouse users, but never as the only way to discover functionality
Gesture Support
- Swipe left/right on the center stage to switch between open apps (like iOS app switching)
- Swipe right from left edge to reveal/toggle the dock on smaller screens
- Swipe left from right edge to reveal/toggle the right panel on smaller screens
- Long-press on files for context menu (cut, copy, rename, delete, compress, properties)
- Long-press on dock icon for app options (remove from dock, move)
- Pull down on file list to refresh directory
- Pinch to zoom in terminal and file preview — browser native
Layout Adaptations for Touch
Landscape (primary tablet orientation):
- Three-column layout as designed — dock (72px wide), stage (flex), right panel (240px)
- Dock shows icon + label underneath (not tooltip)
- All rows/items generously spaced
Portrait / Narrow screens:
- Dock collapses to a slide-in drawer (swipe from left or tap hamburger)
- Right panel collapses to a slide-in drawer (swipe from right or tap info button in tab bar)
- Center stage goes full-width
- Tab bar remains visible at top
Phone (stretch goal, not v1):
- Bottom tab bar replaces left dock
- Right panel accessible via bottom sheet
File Manager — Touch Adaptations
- Row height: 52px minimum
- Drag and drop uses touch drag (touchstart/touchmove/touchend events) not mouse drag
- Selection via long-press + drag, or tap checkbox mode
- Checkbox mode: tap a file to enter selection mode, then tap others to multi-select
- Context menu on long-press: open, preview, copy, move, rename, compress, delete
- No right-click context menu (or: right-click works as fallback for mouse users)
Terminal — Touch Adaptations
- Software keyboard appears automatically on tap
- Toolbar of common keys above the software keyboard: Escape, Tab, Ctrl, arrow keys, pipe, tilde
- Pinch to adjust font size
- Two-finger scroll for scrollback
Settings — Touch Adaptations
- All form rows 52px+ tall
- Toggle switches 51×31px (standard iOS-style)
- Sidebar nav items 48px tall minimum
- Save/cancel buttons large and thumb-reachable (bottom of content, full-width or right-aligned with generous padding)
Right Panel — Touch Adaptations
- Service rows 52px tall — the toggle and ↗ button are both easy targets
- On narrow screens: collapses, accessible via swipe or button
General Touch Rules
- No double-click interactions — everything single tap or long-press
- Scroll areas must be
-webkit-overflow-scrolling: touch(momentum scrolling) touch-actionset appropriately on draggable items- Input fields:
font-size: 16pxminimum to prevent iOS auto-zoom on focus - Avoid
position: fixedwhere possible — useposition: stickyfor headers/toolbars - All modals/dialogs centered and thumb-reachable, not tiny desktop-style popups
Astrophotography Reference Deployment
The initial real-world deployment target:
[ASI Air] ──ethernet──▶ [Orange Pi 5 Max / Atlus] ──nextcloud──▶ [Home Server]
192.168.10.120 192.168.10.121 (eth to ASI Air)
192.168.1.121 (wifi to home network)
The ASI Bridge plugin panel shows:
- ASI Air SMB mount status (connected / disconnected / error)
- Nextcloud client sync status (idle / syncing / error)
- Live transfer log (filename, size, timestamp, sync result)
- Session summary (frames captured tonight, total GB transferred)
- Quick actions: remount share, restart sync service, clear log
Tech Stack
| Component | Technology |
|---|---|
| Backend | Python 3.11+, FastAPI, asyncio, uvicorn |
| WebSockets | FastAPI WebSocket |
| System stats | psutil |
| Terminal | xterm.js (frontend) + ptyprocess (backend) |
| File ops | Python pathlib / os |
| Service mgmt | subprocess + systemctl |
| X11 embedding | Xvfb + x11vnc + noVNC (Phase 4 only) |
| Frontend | Vanilla JS, CSS custom properties, no build step |
| Fonts | IBM Plex Mono + Inter (Google Fonts CDN or self-hosted) |
| Packaging | Single systemd service + install.sh bootstrap script |
| Target OS | Debian/Ubuntu ARM64 (DietPi, Armbian, Raspberry Pi OS) |
Project Phases
Phase 1 — Core Shell (start here)
- Repo and project structure setup
- FastAPI backend —
/api/statsendpoint (CPU, RAM, disk, temp, network IPs) - WebSocket endpoint — push live stats every 2s
- Static frontend served by FastAPI
- Three-column shell layout (dock, stage, right panel) — HTML/CSS skeleton
- Right panel wired to live WebSocket stats + clock
- Left dock with app icons and active state
- Terminal app — xterm.js frontend + ptyprocess PTY backend
- App switching — clicking dock icon loads app into center stage
Phase 2 — File Manager + Service Control
- File Manager — directory listing, navigate, breadcrumb path, sidebar mounts
- Service Manager — list systemd units, start/stop/restart via API
- Right panel service toggles wired to real systemctl calls
- Log Viewer — real-time tail of service logs via WebSocket stream
- Split pane layout — two apps tiled side by side
Phase 3 — Plugin System + ASI Bridge
- Plugin architecture — self-contained module (backend route + frontend panel JS)
- ASI Bridge plugin — mount watcher, transfer log, Nextcloud sync status
- Settings app — configure dock items, services list, hostname display
Phase 4 — X11 App Embedding (advanced)
- Xvfb process spawner via API call
- x11vnc per-app streaming to WebSocket
- noVNC embedded as a center stage pane
- Nextcloud Qt client embedded as live X11 panel
✅ Finalized Project Decisions
| Decision | Answer |
|---|---|
| Project type | Single monorepo — frontend served by FastAPI backend |
| Framing | Full desktop environment — not a server admin panel |
| Authentication | Username + password (session token after login) |
| Default port | 7779 |
| Installation | curl | bash installer script |
| License | GPL |
| Theming | Dark only for v1 — light mode planned, CSS variables structured for it from day one |
| Target OS | DietPi (Debian ARM64) — installer targets DietPi, Armbian, Ubuntu Server as fallbacks |
| Terminal sessions | Multiple — tabbed terminals within the terminal pane |
| Boot / login | Branded login/boot screen before the desktop loads |
| Filesystem access | Full — same as any desktop environment |
| Terminal user | Runs as the logged-in system user |
| Multiple clients | Yes — multiple browsers/devices can connect simultaneously, all receive live WebSocket updates |
| File operations | Confirm destructive actions only (delete, overwrite) |
| Service detection | Auto-detect all systemd services — user manages visibility in Settings |
| ASI Bridge when disconnected | Show disconnected badge — still openable, shows a disconnected state screen |
| Plugin (ASI Bridge) | Built-in from day one, bundled with Atlus core |
| Dock order | Hard-coded for v1, configurable in Settings v2 |
| Right panel width | Fixed 240px for v1 |
Project Structure (Monorepo)
atlus/
├── README.md
├── LICENSE # GPL
├── install.sh # curl | bash installer
├── atlus.service # systemd unit file template
│
├── backend/
│ ├── main.py # FastAPI app entry point
│ ├── config.py # Config, paths, constants
│ ├── auth.py # Username/password auth, session tokens
│ ├── routers/
│ │ ├── stats.py # /api/stats — CPU, RAM, disk, temp, network
│ │ ├── files.py # /api/files — filesystem operations
│ │ ├── services.py # /api/services — systemd management
│ │ ├── processes.py # /api/processes — task manager
│ │ ├── terminal.py # /api/terminal — PTY websocket
│ │ ├── settings.py # /api/settings — read/write config
│ │ └── plugins/
│ │ └── asi_bridge.py # ASI Air bridge plugin routes
│ ├── ws/
│ │ └── manager.py # WebSocket connection manager (multi-client broadcast)
│ └── requirements.txt
│
├── frontend/
│ ├── index.html # Login/boot screen
│ ├── desktop.html # Main desktop shell
│ ├── css/
│ │ ├── variables.css # CSS custom properties (colors, spacing, typography)
│ │ ├── shell.css # Three-column layout
│ │ ├── dock.css
│ │ ├── panel.css
│ │ ├── stage.css
│ │ ├── keyboard.css # Custom on-screen keyboard
│ │ └── apps/
│ │ ├── terminal.css
│ │ ├── files.css
│ │ ├── settings.css
│ │ └── tasks.css
│ ├── js/
│ │ ├── atlus.js # Core shell, app switching, WebSocket client
│ │ ├── auth.js # Login screen logic
│ │ ├── keyboard.js # Custom on-screen keyboard
│ │ └── apps/
│ │ ├── terminal.js # xterm.js + PTY integration
│ │ ├── files.js # File manager
│ │ ├── settings.js # Settings app
│ │ ├── tasks.js # Task manager
│ │ ├── services.js # Service manager
│ │ └── asi_bridge.js # ASI Bridge plugin panel
│ └── assets/
│ └── atlus-logo.svg
│
└── plugins/ # Future third-party plugins live here
Open Questions (Deferred — Not Blocking v1)
- Portrait collapse behavior: Slide-in drawers vs. bottom tab bar — decide when building responsive CSS
- Terminal keyboard quick-key row: Exact key selection — decide during keyboard build
Last updated: 2026-03-13 Project status: Planning complete ✅ — all decisions finalized — ready to build Phase 1