A secure, private, self-hosted navigation page system designed for personal use.
A full-stack navigation/portal page that supports login, navigation management, and card editing. It uses a single Node.js process to serve both the frontend build (dist/) and the API (/api).
- Secure authentication: single access-key login with JWT access and refresh tokens (HttpOnly cookie).
- Private access: only visitors with the access key can enter.
- Login protection:
/api/auth/loginhas lightweight IP-based brute-force protection. 10 failed attempts within 10 minutes trigger a 10-minute block. - Categorized navigation: organize links by categories.
- Search: real-time filtering of navigation links.
- Auto metadata: auto fetch title/description/favicon when editing a URL.
- JSON storage: flat-file storage without an external database.
| Login | Home |
|---|---|
![]() |
![]() |
| Manage navigation | Edit card |
|---|---|
![]() |
![]() |
- Frontend: React + Vite + Tailwind CSS
- Backend: Node.js + Express
- Auth: JWT access/refresh tokens
- Storage: JSON files in
api/data/
npm installCreate your .env:
cp .env.example .envBefore starting the server, replace these example values in .env:
JWT_SECRETREFRESH_TOKEN_SECRETACCESS_KEY
Run in dev mode (frontend + API):
npm run devnpm install
npm run build
npm startDefault port is PORT in .env (default: 3000).
- The project now supports plain HTTP access and no longer forces HTTPS.
- If you deploy behind HTTPS via Nginx, the refresh cookie is still marked
Secureautomatically. - If you deploy over plain HTTP, the refresh cookie still works without extra code changes.
- Before deployment, it is recommended to remove old
build/anddist/outputs, then runnpm run buildagain in the target environment.
- Required for runtime:
api/src/public/scripts/package.jsonpackage-lock.jsontsconfig*.jsonvite.config.tstailwind.config.jspostcss.config.js.env.env.example
- Required data:
api/data/navigations.json
- Build outputs:
build/dist/
- Test code:
tests/test-helmet.js
- Design docs:
docs/superpowers/
- Access key in
.env:ACCESS_KEY
- Data files:
api/data/navigations.json
GET /api/health→{"success":true,"message":"ok"}


