Make the automated pipeline the documented "setup moving forward" and finish scrubbing the last stale GitHub-Actions/racknerd1 references that never reached main. - HANDOFF.md: refresh the stale 2026-06-21 snapshot. New "CI/CD & deploy" section (push to main -> build + push to registry.snsnetlabs.com -> auto-deploy to racknerd2 over SSH, SHA-pinned, /api/health gate), racknerd2 validation-host + SSH-tunnel access notes, Forgejo workflow rule, and a current Deployment + orientation section. - .kiro/steering/project-guide.md: Forgejo-only Git workflow (no gh), CI/CD row, registry host, racknerd2 + forgejo-runner SSH entries, and a CI/CD pipeline section. - .kiro/hooks/tunnel-racknerd2-8080.kiro.hook: the "View ArchNest on racknerd2" hook (ssh -L 8080:localhost:8080 -N) to view the deployed site at http://localhost:8080 (racknerd2's edge only allows port 22). - src/pages/Settings.tsx: About panel repo URL -> Forgejo. - .dockerignore: .github -> .forgejo. - TERMIX_MIGRATION.md / docs/OPEN-SOURCE-RELEASE.md: drop stale .github/workflows + "GitHub Actions deploy" references. Co-authored-by: Samuel James <ssamjame@amazon.com> Co-authored-by: Kiro <noreply@kiro.dev>
116 lines
6.1 KiB
Markdown
116 lines
6.1 KiB
Markdown
# ArchNest — Project Guide for Kiro
|
|
|
|
> Steering file for AI sessions working on this repo. Covers architecture
|
|
> decisions, workflow rules, and patterns to follow. Read alongside
|
|
> `design-rules.md` (visual conventions) which is injected separately.
|
|
|
|
---
|
|
|
|
## Quick Context
|
|
|
|
ArchNest is a **self-hosted ops dashboard** — live infrastructure monitoring,
|
|
SSH terminal/tunnels/files, Docker container management, remote desktop, and
|
|
bookmarks. **Private Forgejo repo (never public) — no GitHub.** CI/CD is
|
|
Forgejo Actions: push to `main` builds images, pushes to
|
|
`registry.snsnetlabs.com`, and auto-deploys to **racknerd2** (validation/preview
|
|
host) over SSH. See `HANDOFF.md` → "CI/CD & deploy" and `deploy/README.md`.
|
|
|
|
## Tech Stack (exact versions matter)
|
|
|
|
| Layer | Tech |
|
|
|-------|------|
|
|
| Frontend | React 19, Vite 8, TypeScript 6, Tailwind CSS v4, React Router 7 |
|
|
| Charts | Recharts 3 |
|
|
| Icons | Lucide React (verify exports exist at runtime, not just TS types) |
|
|
| Terminal | xterm.js 6 (`@xterm/xterm` + `@xterm/addon-fit`) |
|
|
| Backend | Fastify 5, TypeScript 5.7, ESM (`tsx` dev, `tsc -b` build) |
|
|
| DB | better-sqlite3 (SQLite) |
|
|
| Auth | `@fastify/jwt` + bcryptjs + server-tracked sessions |
|
|
| Validation | zod |
|
|
| SSH | ssh2 library |
|
|
| AWS | `@aws-sdk/client-ec2`, `@aws-sdk/client-sts` |
|
|
| Deploy | Docker Compose (Alpine images) |
|
|
| CI/CD | Forgejo Actions (`.forgejo/workflows/`): `ci.yml` validate; `build.yml` build+push to `registry.snsnetlabs.com` then auto-deploy to racknerd2. No GitHub. |
|
|
|
|
## Git Workflow
|
|
|
|
- **Remote**: `origin` → private Forgejo `forgejo.archnest.local:3000/sam/dev_arc_aws` (SSH via ProxyJump). **Forgejo-only — no GitHub, no `gh` CLI.**
|
|
- **Container registry**: `registry.snsnetlabs.com` (user `sam`, package token). Unproxied host so large layers bypass Cloudflare's body cap; web UI/packages stay on `forgejo.snsnetlabs.com`.
|
|
- **Never commit on `main`**. Always create `kiro/<feature>` branches.
|
|
- **Commit style**: imperative title + body explaining why, with trailers:
|
|
```
|
|
Co-authored-by: Samuel James <ssamjame@amazon.com>
|
|
Co-authored-by: Kiro <noreply@kiro.dev>
|
|
```
|
|
- **Before committing**: `npm run build` (frontend) + `cd backend && npx tsc --noEmit` (backend). Forgejo CI runs the same.
|
|
- **Stage specific files** — never `git add -A` blindly
|
|
- **PR flow**: `git push -u origin <branch>` → open a PR on Forgejo (web UI/API) → merge to `main`. **Merging to `main` auto-builds + auto-deploys to racknerd2** (build.yml). `deploy.yml` is a manual dispatch for deploying/rolling back any tag.
|
|
|
|
## Code Patterns to Follow
|
|
|
|
### Frontend
|
|
- One page component per route in `src/pages/`
|
|
- All backend calls go through `src/lib/api.ts` (typed `apiFetch` wrapper)
|
|
- No global state library — plain React state + localStorage for prefs
|
|
- Auth via `src/lib/AuthContext.tsx` (JWT in localStorage)
|
|
- New pages need: route in `App.tsx`, entry in `api.ts`, sidebar link
|
|
|
|
### Backend
|
|
- One route file per feature in `backend/src/routes/`
|
|
- Integration adapters in `backend/src/integrations/` (must implement `testConnection()`)
|
|
- SSH-based features use `backend/src/ssh/connect.ts` shared transport
|
|
- Request validation with zod schemas
|
|
- Audit logging via `logEvent()` from `db/index.ts`
|
|
- Secrets encrypted at rest (AES-256-GCM via `db/crypto.ts`)
|
|
- Never expose secret values to frontend — only `secretKeys: string[]`
|
|
|
|
### Adding a New Integration
|
|
1. Create adapter in `backend/src/integrations/<name>.ts`
|
|
2. Register in `backend/src/integrations/registry.ts`
|
|
3. Add type to `IntegrationType` union
|
|
4. Add route if needed in `backend/src/routes/`
|
|
5. Add `api.ts` functions + TS interfaces on frontend
|
|
6. Add card in Settings integrations section
|
|
|
|
## Policies
|
|
|
|
- **Versioning**: development happens on **even** major versions; **odd** majors
|
|
are released/stable lines. We are currently developing **v2** (the prior
|
|
released line is v1, see the `v1.0` git tag). Image/version tags should
|
|
reflect this — dev builds carry the even (v2) version.
|
|
- **Zero mock data** — every number comes from a live API/SSH/DB call
|
|
- **Design-first for big features** — write a `docs/<feature>.md` before coding
|
|
- **No footer** on any page
|
|
- **Primary target**: 1920px+ viewport, should feel spacious
|
|
- **Mesh gate** defaults OFF — never lock the live instance
|
|
- **OpenSSL legacy provider** in backend Dockerfile — don't remove (needed for old PEM keys)
|
|
|
|
## Environment
|
|
|
|
- Required env vars: `ARCHNEST_SECRET_KEY`, `ARCHNEST_JWT_SECRET`
|
|
- Optional: `ARCHNEST_DB_PATH`, `PORT`, `ARCHNEST_GUAC_CRYPT_KEY`,
|
|
`ARCHNEST_CORS_ORIGIN`, `ARCHNEST_AGENT_TOKEN`, `ARCHNEST_AGENT_STALE_MS`
|
|
- Frontend dev proxies `/api` → `http://localhost:4000`
|
|
|
|
## Key Files to Read First
|
|
|
|
1. `README.md` — architecture overview
|
|
2. `HANDOFF.md` — current state + standing rules
|
|
3. `design-decisions.md` — visual conventions + per-page implementation notes
|
|
4. `ROADMAP.md` — deferred/tiered work
|
|
5. `docs/` — subsystem design documents
|
|
|
|
## SSH Config (for reference)
|
|
|
|
- `ssh forgejo` → Git operations (User: forgejo, via ProxyJump linode)
|
|
- `ssh forgejo-admin` → root shell on Forgejo host (for admin tasks)
|
|
- `ssh forgejo-runner` → host running the Forgejo Actions runner (has Docker; builds images). Runner config `/opt/config.yaml` sets `container.docker_host: automount`.
|
|
- `ssh racknerd2` → validation/preview host (root). Runs the deployed stack from `/opt/archnest/`. Mesh IP `100.96.217.250`. Edge only allows port 22 — view the site via the SSH tunnel hook (`-L 8080:localhost:8080`) at `http://localhost:8080`.
|
|
- `ssh linode` → jump host at 172.238.163.85
|
|
|
|
## CI/CD pipeline (full detail in `deploy/README.md`)
|
|
|
|
- Push to `main` → `build.yml`: job `build` (build + push `:latest` and `:<sha>` to the registry) → job `deploy` (needs build; SSH to racknerd2, `docker compose pull && up -d` pinned to `<sha>`, `/api/health` gate).
|
|
- Required Forgejo Actions secrets: `FORGEJO_REGISTRY_TOKEN`, `RACKNERD2_SSH_KEY`.
|
|
- The build job installs **`docker-ce-cli` from Docker's apt repo** (Debian's `docker.io` is too old for the host daemon). Don't switch it back to `docker.io`.
|
|
- racknerd2 `/opt/archnest/docker-compose.yml` PULLS registry images; the repo-root `docker-compose.yml` BUILDS locally (dev/manual).
|