Groundwork for securing the web front end (vrek gol-wqf95dq). The app does not enforce login yet. - src/lib/admin-file.ts: the admin file at ADMIN_AUTH_FILE (default .data/admin.json). scrypt hashing (N=2^16, random salt, bounded parameters, constant-time compare), zod-validated reads where a malformed file is an error, and atomic 0600 writes that won't replace an existing admin without overwrite. Plain Node, so the CLI can share it (iss-mffqscg). - src/lib/admin-auth.ts: server-only app layer; failed logins always cost one hash. - scripts/create-admin.mts + `npm run admin:create`: create or reset the admin outside the app, interactive (hidden, confirmed) or piped (iss-7xmka20). The README documents it, a no-npm Node one-liner, the file format, and password reset. - src/lib/session-token.ts and session.ts: stateless HMAC-signed session cookie, keyed from the password hash so a password change ends every session, with a 12 h sliding window (iss-e27nb70, dec-f0xar8r). 281 tests, 99.8% line coverage. Refreshes the vrek export. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
109 lines
4.0 KiB
Markdown
109 lines
4.0 KiB
Markdown
# 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](https://nodejs.org/api/crypto.html#cryptoscryptpassword-salt-keylen-options-callback)
|
||
(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.
|
||
|
||
### Create the admin login (recommended)
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
(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
|
||
|
||
```json
|
||
{
|
||
"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](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||
|
||
## Getting Started
|
||
|
||
First, run the development server:
|
||
|
||
```bash
|
||
npm run dev
|
||
# or
|
||
yarn dev
|
||
# or
|
||
pnpm dev
|
||
# or
|
||
bun dev
|
||
```
|
||
|
||
Open [http://localhost:3000](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`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||
|
||
## Learn More
|
||
|
||
To learn more about Next.js, take a look at the following resources:
|
||
|
||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||
|
||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||
|
||
## Deploy on Vercel
|
||
|
||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||
|
||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|