Read-only settings and stream URLs (vrek gol-2nxgqjn), shaped by a
read-only probe of the real cameras (findings fnd-*) and decisions
dec-s205dvs and dec-gtw493s.
- src/lib/camera-settings.ts: reads streams, image, time/NTP and
network over ONVIF behind a CameraSettingsSource interface, so an
ISAPI source can be added later. Sections are read independently
and fail on their own with fixed text. RTSP URLs have credentials
stripped and use the registry host. camera.ts exports its cached
connect() and the profile helpers it shares with it (iss-7eaywtf).
- /cameras/[id]: server-rendered page with an access check, 404 for
unknown ids or non-private hosts, and a streamed Suspense section.
Shows streams (with copyable RTSP URLs and an ONVIF-login note),
image, time/NTP with a drift warning, and network, plus Refresh
and back. Copy falls back to execCommand over plain LAN HTTP,
where the clipboard API is unavailable (iss-s935a4n).
- Dashboard card: the name links to the camera page; a main-stream
summary line ("Main 4096×1860 · H.264 · 20 fps"); a manufacturer
of "ONVIF" is hidden (iss-pv2bvzj).
522 tests, 99.9% line coverage. Checked by the user in a browser
against both cameras. Refreshes the vrek export (the icon principle
now covers the page's new icons).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .idea | ||
| .vrek | ||
| public | ||
| scripts | ||
| src | ||
| test | ||
| .gitignore | ||
| .mcp.json | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| eslint.config.mjs | ||
| next.config.ts | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.mjs | ||
| README.md | ||
| tsconfig.json | ||
| vitest.config.mts | ||
Cameras
A web interface for discovering and managing Hikvision and Annke IP cameras on your local network.
Securing the web interface
The dashboard and its API are protected by a single admin login. The login is stored in a file, never in plain text: the password is hashed with scrypt (random salt, N=65536, r=8, p=1), so it can't be recovered from the file.
| Setting | Default |
|---|---|
ADMIN_AUTH_FILE (environment variable, e.g. in .env.local) |
./.data/admin.json |
If the file doesn't exist, the app starts unsecured: anyone who can reach it can view your cameras and change their logins. To avoid ever running it that way, create the admin file before the first start (below).
Setting it up from the browser instead
With no admin file, the server prints a one-time setup code in its console at startup:
====================================================================
No admin login is set up: the camera dashboard is NOT secured.
To secure it from a browser, open /setup and enter this one-time code:
XLZ7-Q4MB
...
Opening the app sends you to /setup, which asks for that code plus the new admin username
and password. Only someone who can see the server console can claim the login. After 5 wrong
codes a new one is printed.
You can also choose Skip for now and run unsecured. Every page then shows a red warning banner, the API stays open to anyone on the network, and the setup prompt returns the next time the browser is restarted.
Signing in
Once an admin exists, every page and API call needs a session: pages redirect to /login,
and the API answers 401. A session lasts 12 hours from your last activity. After 10
wrong passwords from one address (or 100 from all addresses) within 15 minutes, logins are
paused for up to 15 minutes. Sign out (top right) ends the session in that browser; to
sign out everywhere, change the password.
Create the admin login (recommended)
npm run admin:create
It asks for a username and a password (at least 12 characters; typing is hidden, and you
confirm it), then writes the file with owner-only permissions (0600). Options:
npm run admin:create -- --username admin # only ask for the password
npm run admin:create -- --force # replace the existing admin (reset the password)
printf '%s\n' "$PW" | npm run admin:create -- --username admin # non-interactive, e.g. provisioning
Without npm: a Node one-liner
Any Node 24+ can produce the same file. The password is read from the terminal without echoing, so it never appears in your shell history or the process list:
(read -rs PW && export PW && umask 077 && mkdir -p .data && node -e '
const c = require("node:crypto"), N = 65536, r = 8, p = 1, salt = c.randomBytes(16);
const key = c.scryptSync(process.env.PW, salt, 64, { N, r, p, maxmem: 256 * N * r });
const passwordHash = ["scrypt", N, r, p, salt.toString("base64"), key.toString("base64")].join("$");
console.log(JSON.stringify({ version: 1, username: "admin", passwordHash }, null, 2));
' > .data/admin.json)
The parentheses run it in a subshell, so the password variable and the stricter umask end
with it.
Change username: "admin" to taste. Usernames are 1–64 letters, digits, or . _ @ -.
File format
{
"version": 1,
"username": "admin",
"passwordHash": "scrypt$65536$8$1$<salt, base64>$<64-byte key, base64>"
}
A file that exists but is malformed stops the app from authenticating anyone rather than quietly turning security off; fix it or delete it.
Forgot the password?
Run npm run admin:create -- --force (or delete the file and create it again). Changing the
password signs out every existing session.
This is a Next.js project bootstrapped with create-next-app.
Getting Started
First, run the development server:
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
Open http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying app/page.tsx. The page auto-updates as you edit the file.
This project uses next/font to automatically optimize and load Geist, a new font family for Vercel.
Learn More
To learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!
Deploy on Vercel
The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.
Check out our Next.js deployment documentation for more details.