The Forgejo container registry now lives on a dedicated unproxied
(DNS-only) host, registry.snsnetlabs.com, so large image layers bypass
Cloudflare's ~100 MB request-body cap (the backend image's 262 MB and
317 MB layers previously hit 413 Payload Too Large through the proxied
forgejo.snsnetlabs.com host). The web UI / packages list stays on
forgejo.snsnetlabs.com behind Cloudflare Access SSO.
- build.yml: REGISTRY -> registry.snsnetlabs.com
- deploy/docker-compose.yml: image refs -> registry.snsnetlabs.com
- deploy/README.md: push/pull/login host -> registry.snsnetlabs.com
(packages web UI URL kept on forgejo.snsnetlabs.com)
Also record the versioning convention in HANDOFF + steering: development
happens on even major versions, releases on odd; currently developing v2
(prior released line is v1, see the v1.0 git tag). package.json and the
About panel are not yet bumped to v2.
Validated end to end: built both images on the runner host, pushed to
registry.snsnetlabs.com (backend included, no 413), pulled on racknerd2,
brought the stack up, /api/health returns {"ok":true} over the mesh IP.
Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
4.3 KiB
4.3 KiB
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. Deployed at archnest.snsnetlabs.com via Docker Compose on
racknerd1. Private Forgejo repo (never public).
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), GitHub Actions → racknerd1 |
Git Workflow
- Remote:
origin→ private Forgejo instance (SSH via ProxyJump) - Never commit on
main. Always createkiro/<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) - Stage specific files — never
git add -Ablindly - PR flow:
git push -u origin <branch>→gh pr create→ squash-merge
Code Patterns to Follow
Frontend
- One page component per route in
src/pages/ - All backend calls go through
src/lib/api.ts(typedapiFetchwrapper) - 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 inapi.ts, sidebar link
Backend
- One route file per feature in
backend/src/routes/ - Integration adapters in
backend/src/integrations/(must implementtestConnection()) - SSH-based features use
backend/src/ssh/connect.tsshared transport - Request validation with zod schemas
- Audit logging via
logEvent()fromdb/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
- Create adapter in
backend/src/integrations/<name>.ts - Register in
backend/src/integrations/registry.ts - Add type to
IntegrationTypeunion - Add route if needed in
backend/src/routes/ - Add
api.tsfunctions + TS interfaces on frontend - 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.0git 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>.mdbefore 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
README.md— architecture overviewHANDOFF.md— current state + standing rulesdesign-decisions.md— visual conventions + per-page implementation notesROADMAP.md— deferred/tiered workdocs/— 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 linode→ jump host at 172.238.163.85