- Drop unused email module and media text extraction path - Remove 12 unused dependencies - Remove unused imports in server.ts - Add typecheck and test scripts - Rewrite AGENTS.md to describe this project Co-Authored-By: Claude Opus 5.5 <[email protected]>
8.6 KiB
Agent Instructions
WireGuard UI — a self-hosted web admin for WireGuard hosts and clients. The app runs as root and executes system commands (wg, ip, iptables, wg-quick, firewall managers). Treat every API route and every value that reaches a shell or a WireGuard config as security-sensitive.
Stack
- Runtime: Bun — not Node. Use
buncommands, nevernpm/npx. - Framework:
@moduletrace/bunext— not Next.js. Routing is filesystem-based undersrc/pages/, served byBun.serveinsrc/server.ts. - Database:
@moduletrace/bun-sqlite— SQLite. Schema indb/schema.ts, generated types indb/types/db.ts. Config inbun-sqlite.config.ts(DB namewguiin./db). Import its types from@moduletrace/bun-sqlite/types(not/dist/types). - UI: Tailwind CSS v4 + local
twuicomponents (src/components/twui). Global styles insrc/styles/main.css. - Icons:
lucide-react.
MCP Tool Usage Guidelines
When building, refactoring, styling, or auditing UI components and layouts, actively invoke these configured MCP servers:
1. design-mcp (UI/UX Engineering & Design Standards)
- When to call: Before constructing new page layouts, design systems, empty/loading states, or interactive UI flows.
- Instruction: Query
design-mcpto ensure accessibility (a11y), proper layout hierarchy, visual balance, and consistent interaction patterns across application workflows.
2. design-systems (Design System Specs & Reference Patterns)
- When to call: When creating or extending reusable component interfaces, verifying design token structures, or inspecting component composition patterns.
- Instruction: Query
design-systemsto pull canonical UI patterns, atomic component guidelines, and standard prop naming conventions before creating custom components.
Entrypoint
src/server.ts — runs bunextInit(), starts Bun.serve on 0.0.0.0 (port from SiteData.ServerPort), serves /media/* via handleMediaServer, then runs syncWireguardHosts() to bring host tunnels in line with the DB.
On-disk state: WireGuard keys/configs/iptables scripts live under /var/lib/wgui (grabDirNames() in src/utils/grab-dir-names.ts); uploaded media under DATA_DIR (default ./.data).
Commands
| Purpose | Command |
|---|---|
| Dev server | bun run dev (runs sudo bun src/server.ts) |
| Production | bun run start |
| Build | bun run build |
| Type-check | bun run typecheck (bunx tsc --noEmit) |
| Tests | bun run test (bun test) |
| DB schema | bun run db:schema |
| DB admin UI | bun run db:admin |
No linter or formatter configured. Type-check and bun test are the verification steps.
Architecture
src/pages/__root.tsx— picks the layout (src/layouts/mainorsrc/layouts/admin, orLoginShellfor auth pages) and providesAppContext.- Pages:
/+about,privacy,terms;/auth/{login,logout,signup};/admin(dashboard),/admin/setup,/admin/settings,/admin/clients,/admin/hosts/*(add/edit hosts,[host_id]/clients/*add/edit/view clients). - API:
src/pages/api/admin/*(host/client setup, network info, firewall status, service restart/update, media), generic CRUD atsrc/pages/api/admin/crud/[[...paths]].tswith per-table handlers incrud/(tables)/<table>/{get,post,put,del}.ts, plusapi/auth/login.tsandapi/create-first-user.ts. - Every
/api/admin/*route must authenticate — nothing guards the prefix globally. UseadminApiGuard({ req })(src/functions/backend/auth/admin-api-guard.ts) oruserAuth+checkUserAccess. - Shell safety: never interpolate request/DB values into
execSyncstrings. UseexecFileSyncwith an args array ornode:fsAPIs, and validate values before they reach a WireGuard config (newlines there can injectPostUpcommands). Validators live insrc/utils/(validate-ipv4,validate-ipv4-cidr,validate-allowed-ips,validate-endpoint-host,validate-interface-name).
Path Alias
@/* resolves from repo root. Import like @/src/components/general/admin-card, @/db/types/db.
Page Directory Rules
For route-local code under src/pages, use non-routed meta folders:
(sections)— page sections(partials)— components used by sections(data)— page-local data(functions)— page-local functions(utils)— page-local utilities(hooks)— page-local hooks
Keep route files (e.g. index.tsx) thin — compose from route-local meta folders.
Dynamic routes use [param] syntax (e.g. [id], [user_type], [key]). Catch-all routes use [[...paths]].
Server functions use index.server.ts alongside index.tsx (e.g. single-document/[id]/index.server.ts).
Shared Code Organization
src/functions/backend/— Server-only:auth/,db/,firewall/(firewalld/ufw rules, status),media/,setup/(host/client setup, iptables script builder, network detection).src/functions/frontend/— Client-safe: CRUD/fetch handlers, media upload/delete, client rule label formatting.src/functions/server/— Request-level server handlers (media server).src/hooks/—use-admin-crud-get,use-client-rules-map,use-fetch-get,use-form-init,use-users-get.src/utils/— Cookies, crypto, dir names, IP/CIDR/port/interface validators, WireGuard interface naming. Most have.test.tsfiles.src/dict/,src/data/— Constants (app-data.ts,site-data.ts), user types, client rules, links.src/scripts/— Shell scripts:install-wg-ui.sh,setup-wireguard.sh,wg-quick-manage.sh.src/components/general/— Shared app components (admin cards/buttons, clients table, host IP field, login shell, user form).
Conventions
- Prefer one function/component per file over monolithic files with multiple exports.
- Every rendered array must have a separate component for each array item. Always extract the item JSX into its own component and render it via
items.map((item) => <ItemComponent {...item} />)— never inline the item's JSX inside the map callback. - Always use object parameters (
{ foo, bar }) instead of comma-separated positional parameters. - Prefer existing patterns over new abstractions.
- Modularization is non-negotiable: prioritize modularization over monolithic functions/components. Repeated UI blocks must be extracted into their own reusable components driven by props (e.g. an info row component taking
{ icon, text }), never repeated inline markup. - UNCHANGEABLE RULE — twui components always win over raw JSX elements. ALWAYS use twui components (
H1–H5,P,Span,List,Img,Row,Stack,Container,Section,Border,Card,Button,Tag,Link, etc.) before basic JSX elements (<h1>–<h6>,<p>,<span>,<ul>,<li>,<img>,<a>,<button>,<div>-as-card, etc.). Never introduce or reintroduce a raw JSX element where a twui equivalent exists. Override twui defaults viaclassName(e.g.mb-0!) rather than reaching for the raw element. - UNCHANGEABLE RULE — twui components are preferred over shadcn/ui components. Use twui before
src/components/ui/*(shadcn) whenever a twui equivalent exists. The long-term goal is to phase out shadcn/ui entirely — do not introduce new shadcn usage where twui covers the need, and prefer migrating existing shadcn call sites to twui when touching them anyway. - LIMIT EDITS TO
src/components/twui/— it is a general, publishable library shared across projects (its ownpackage.json), not scoped to this repo. Do not modify its components unless strictly necessary. Customize/appearance overrides for this project belong insrc/styles/main.css(e.g. tweaking.twui-card,--color-*tokens, font/type styles) rather than editing twui source files. - twui
Input/Textarea: preferdefaultValueovervalue. The component manages internal state and patches the DOM directly (src/components/twui/form/Input/index.tsx), sovalueis not a true controlled prop — a staticvaluegets reverted on every re-render, wiping in-progress edits.valueonly works well when the parent updates it on each change (live display), not for pre-filling an editable field. UsedefaultValueto pre-fill editable inputs;onChange/changeHandlerstill fire. .envis gitignored but exists locally. Do not commit it.