# ArchNest — Open-Source Release Readiness (v1) This document is the checklist + plan for publishing ArchNest as an open-source project. It is an internal planning doc — **do not copy this file into the public repo.** It covers what to copy, what to scrub, licensing, repo structure, README/screenshots, the release cadence, and resume/LinkedIn framing. The public OSS repo should be a **fresh repository with a clean history** (see "Why a fresh repo" below), not a fork of the working repo. --- ## 1. Security sweep result (done 2026-06-22) A full secret/credential sweep of tracked files **and entire git history** (`git log --all -p`) was run. Result: **clean — no real secrets are committed.** - No private keys, no hardcoded secret assignments in tracked source. - No real `.env`, `.pem`, `.key`, or `.db` was ever committed at any point in history. The only `BEGIN RSA PRIVATE KEY` / `AKIA…` matches are documentation prose and AWS's official `AKIAIOSFODNN7EXAMPLE` placeholder. - `.gitignore` correctly excludes `backend/.env`, `backend/data`, `*.db`. - Both `.env.example` files contain only placeholders (empty / `change-me-…`). ### Code-level security review (solid) - JWT auth (Fastify `@fastify/jwt`). Every route group registers a blanket `addHook('onRequest', app.authenticate)` before its routes; the three WebSocket routes (`terminal`, `docker`, `guacamole`) verify `app.jwt.verify(query.token)` explicitly (WS can't use header hooks). - Mutating shared-config endpoints (integrations, tunnels, data export/import, user management) are gated by `adminOnly` / `requireAdmin`. `authenticate` re-reads `role`/`active` from the DB each request, so demote/deactivate takes effect immediately even with an older token. - Integration secrets: `serialize()` returns only secret **key names** (`secretKeys`), never values. Secrets are encrypted at rest (AES-256-GCM, `backend/src/db/crypto.ts`). - Docker agent ingest is a **separately registered** route with a constant-time bearer-token check; returns 503 when `ARCHNEST_AGENT_TOKEN` is unset (disabled by default), and is NOT behind the user-auth hook (by design). - Command-injection surfaces are guarded: tmux session names validated against `^[A-Za-z0-9_-]{1,64}$` before interpolation; `system.ts` uses `execFile` (no shell); Docker-over-SSH single-quotes container refs. ### Things to improve / note for OSS (not leaks) - **CORS default**: `server.ts` does `origin: process.env.ARCHNEST_CORS_ORIGIN ?? true`. `true` reflects any origin. Fine for a self-hosted single-origin deploy, but the OSS README should tell users to set `ARCHNEST_CORS_ORIGIN` in production, and the default `.env.example` should point at `http://localhost:5173` (it already does for the backend example). - Default JWT/secret env values in `backend/.env.example` are `change-me-…` — the README must stress generating real ones (`openssl rand -hex 32`). The server already refuses to boot without `ARCHNEST_SECRET_KEY` + `ARCHNEST_JWT_SECRET`. --- ## 2. What to SCRUB / NOT copy to the public repo None of these are security leaks, but they are personal/infra-specific or internal working notes that don't belong in a public project: | Item | Why | Action | |---|---|---| | `docs/rdp-debug-handoff.md` | Contains lab creds (`sam` / `happy2026`) + private VM IP `192.168.122.55` + personal host names | **Exclude** (or heavily genericize into a "Remote Desktop setup" guide with no creds/IPs) | | `HANDOFF.md` | Internal session-to-session working notes | **Exclude** | | `docs/OPEN-SOURCE-RELEASE.md` (this file) | Internal release plan | **Exclude** | | `archnest.snsnetlabs.com` references in `.env.example`, `docker-compose.yml`, `.github/workflows/deploy.yml` | Personal domain/deploy target | **Genericize** to `example.com` / `localhost`; the deploy workflow should be removed or replaced with a generic CI (build + lint only, no SCP-to-my-server) | | `.github/workflows/deploy.yml` | SSHes/SCPs to the personal `racknerd1` server | **Remove**; replace with a generic build/test CI workflow | | `agent/` deploy specifics | Fine to include the agent script, but scrub any host-specific URLs/tokens in its README | **Review + genericize** | | `assets/` personal background images | Large PNGs; keep the ones the UI needs (hero banner, logo, KPI backgrounds), drop unused experiments (`opt1.bg`, `settings-custom-bg`, `pics/`) | **Trim to what's referenced** | | Test/scratch files | `backend/data/`, any `*.db`, session logs | Already gitignored — confirm none are force-added | Keep `ROADMAP.md` and `TERMIX_MIGRATION.md`? — `ROADMAP.md` yes (genericize: it's a fine public roadmap once paid-tier framing is softened). `TERMIX_MIGRATION.md` is build history; optional — can keep as `docs/HISTORY.md` or drop. --- ## 3. Why a fresh repo (recommended) The working repo's history contains personal commit author emails, the personal deploy workflow, the lab-cred debug doc, and the personal domain. The simplest clean cut for a public project: 1. Create a new empty public repo (e.g. `archnest` under the personal GitHub). 2. Copy the **working tree** (not `.git`) of the files in the "INCLUDE" list below. 3. Genericize the scrubbed items. 4. `git init`, single initial commit ("Initial public release — ArchNest v1"), author set to the public identity. 5. Add `LICENSE`, public `README.md`, `CONTRIBUTING.md`, screenshots. This avoids dragging history-scrubbing tooling (BFG/`git filter-repo`) and guarantees nothing personal leaks via an old commit. ### INCLUDE (the actual app) ``` src/ # React frontend backend/src/ # Fastify backend backend/package.json, backend/tsconfig*.json, backend/Dockerfile backend/.env.example # (placeholders only — already clean) package.json, package-lock.json, tsconfig*.json, vite.config.*, index.html Dockerfile, docker-compose.yml # genericized (no personal domain) .env.example # genericized .gitignore, .dockerignore public/ # fonts + static assets actually referenced assets/ # ONLY images the UI imports agent/archnest-docker-agent.sh + a genericized agent/README.md design-decisions.md, ROADMAP.md # genericized .kiro/steering/design-rules.md # optional — useful for contributors LICENSE, README.md, CONTRIBUTING.md, screenshots/ # new, written for OSS ``` ### EXCLUDE ``` .git/ # fresh history instead HANDOFF.md docs/rdp-debug-handoff.md docs/OPEN-SOURCE-RELEASE.md (this file) .github/workflows/deploy.yml (replace with generic CI) backend/data/, *.db, session logs, *.tsbuildinfo unused assets/ experiments + pics/ ``` --- ## 4. License Recommended: **MIT** or **Apache-2.0**. - **MIT** — shortest, most permissive, maximum adoption, easiest "I built this" story. Good default for a portfolio/resume project. - **Apache-2.0** — same permissiveness plus an explicit patent grant and a NOTICE mechanism; slightly more "enterprise-friendly." Given the goal (resume/LinkedIn showcase, broad adoption, simple), **MIT** is the recommendation. Add a `LICENSE` file with the chosen license and the author's name + year. Note third-party components keep their own licenses (Guacamole = Apache-2.0, the bundled Nerd Font has its own license already in `public/fonts/NERD-FONTS-LICENSE.txt`). --- ## 5. README.md (public) — outline 1. **Hero**: one-line pitch + a screenshot/GIF of the Glance dashboard. > "A self-hosted, web-based control panel for your homelab and cloud — SSH > terminal, file manager, Docker, tunnels, RDP/VNC, host metrics, and > integration dashboards, all in one browser tab." 2. **Screenshots** (see §6). 3. **Features** — bullet list grouped by page; mark paid add-ons / not-yet-done honestly (mirror the in-app Help "Not in the open-source version" notes). 4. **Architecture** — short: React + Vite + TS frontend, Fastify + SQLite backend, guacd sidecar for RDP/VNC. One diagram is plenty. 5. **Quick start** — `docker compose up` path + the required env vars (with `openssl rand -hex 32` generation), and the local-dev path (`npm install` / `npm run dev` in root and `backend/`). 6. **Configuration** — env var table (from `.env.example`), CORS note, first-run `/api/setup` admin creation, the 10-user cap. 7. **Security notes** — secrets encrypted at rest; set a real CORS origin in prod; it's designed to sit behind your own mesh/VPN, not be exposed raw to the internet (mesh prerequisite gate exists, defaults off). 8. **Roadmap** — link `ROADMAP.md` + the "updates ~every 3 months" promise. 9. **Contributing** — link `CONTRIBUTING.md`. 10. **License** — MIT. 11. **Credits / "Built with AI"** — see §7. --- ## 6. Screenshots to capture (for README + LinkedIn) Capture in the **default dark theme**, with demo/sanitized data (no real hostnames, IPs, or tokens — use the placeholder-y names): - Glance dashboard (hero shot) - Infrastructure → Node Status with a couple of integrations - Terminal with a split-pane / multiple tabs - Files (SFTP browser) + a host-to-host transfer in progress - Containers list + a container detail tab - Remote Desktop showing a live XFCE session - Host Metrics widgets - Settings → Integrations (shows the breadth) and the locked Appearance "Paid add-on" card (shows the free/paid split honestly) - Help page (shows the per-page docs + OSS-edition note) A short screen-recording GIF of opening a terminal or RDP session makes the strongest LinkedIn post. --- ## 7. Resume / LinkedIn framing Honest, specific, and ownership-forward. Suggested phrasing: > **ArchNest** — a self-hosted, web-based homelab/cloud control panel > (React + TypeScript + Fastify + SQLite, Dockerized). Single-pane access to SSH > terminals, SFTP, Docker, SSH tunnels, browser-based RDP/VNC (Apache Guacamole), > live host metrics, and pluggable infrastructure integrations (Proxmox, AWS, > Cloudflare, NetBird, Uptime Kuma). Built with AI-assisted development; I owned > the architecture, product decisions, security review, and integration/debugging > (e.g. root-caused and fixed a FreeRDP/NLA + Guacamole tunnel-keepalive issue > end-to-end across browser, proxy, and target VM). Open source under MIT, shipped > v1, ongoing ~quarterly releases. Notes for credibility: - It's fine and increasingly normal to say "AI-assisted." Pair it with the *engineering judgment* you provided (architecture, security, debugging) so it reads as "I directed and verified," not "I prompted and pasted." - The RDP debugging saga is a genuinely strong, concrete story — it shows multi-layer debugging (browser ↔ guacd/FreeRDP ↔ xrdp/desktop) and root-cause rigor. Worth a short LinkedIn write-up on its own. --- ## 8. Pre-publish checklist - [ ] Create fresh public repo, copy INCLUDE list, exclude EXCLUDE list. - [ ] Genericize personal domain → `example.com`/`localhost` in `.env.example`, `docker-compose.yml`. - [ ] Replace `.github/workflows/deploy.yml` with a generic build/lint CI (no SCP). - [ ] Add `LICENSE` (MIT), public `README.md`, `CONTRIBUTING.md`. - [ ] Capture + add screenshots (sanitized data, dark theme). - [ ] Re-run a secret scan on the NEW repo before first push (`git log -p | grep -iE 'AKIA|BEGIN .*PRIVATE KEY|password|secret'` plus a tool like `gitleaks detect` for good measure). - [ ] Confirm `npm run build` (root) and `npx tsc --noEmit` (backend) pass on the copied tree. - [ ] Confirm first-run works from a clean `docker compose up` with freshly generated secrets and no prior DB. - [ ] Tag `v1.0.0`. --- ## 9. Release cadence (commitment) Public promise: **updates approximately every 3 months.** Keep a short `CHANGELOG.md` in the public repo and cut a tagged release each cycle. The Help page's "Open-source edition" note and the README both reference this cadence — keep them in sync.