cameras/README.md
Michael Mainguy 462141aa35 Add admin login foundations: scrypt admin file, CLI, sessions
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>
2026-09-19 09:46:40 -05:00

109 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.