PinchPad uses a key-file identity system β there are no passwords or accounts on a remote server.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Identity Key File: pinchpad_identity_key.json β
β β
β { β
β "username": "your-username", β
β "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", β
β "token": "hu-[64 random chars]" β
β } β
β β
β β οΈ This file IS your password. Losing it = lockout. β
β β οΈ Never share it. Never commit it to version control. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Prefix | Type | Scope | Storage | Notes |
|---|---|---|---|---|
hu- |
Human Identity Key | One-time login lookup | Server DB (key_hash UNIQUE index, SHA-256) |
Client-side only, never transmitted raw |
lb- |
Agent Key | Automated agent access | Server DB (lobster_keys table, SHA-256 hashed) |
Revocable, granular permissions |
api- |
REST Token | API session access | Server DB (api_tokens table) |
24-hour TTL, bearer header |
Client-Side (Session Memory)
hu-tokens are never stored in plaintext and are never sent to the server.- The
hu-string is immediately hashed on the client using SHA-256 and exchanged viaPOST /api/auth/tokenfor a short-livedapi-bearer token. sessionStorageis used for session state (pp_authenticated,pp_user) β clears automatically on tab close to prevent token theft.- Browser-compatible UUID generation uses
crypto.getRandomValues()with RFC 4122-compliant formatting.
Server-Side (Express & SQLite)
requireAuth: Extracts and validates theapi-token from the Bearer header. Immediately injectsreq.userIdandreq.permissionsfor downstream handlers based on whether the token belongs to a human or anlb-agent key. Expired tokens are auto-deleted.requirePermission(perm): Enforces granular locks (e.g.,canRead,canWrite,canEdit,canDelete) on all CRUD routes based on the permissions assigned to the underlyinglb-key.requireHuman: Restricts sensitive configuration routes (/api/agents) to human tokens only. Lobster keys cannot mutate system configuration.- Key Uniqueness:
key_hashis strictly enforced asUNIQUEto support collision-free one-field lookups. - SHA-256 Hashing: Lobster keys are hashed using SHA-256 (not bcrypt, not MD5) for server-side verification. Hash comparison is query-based (constant-time).
- SQLite Data Integrity: WAL journal mode enabled for durability. Foreign key constraints enforced with cascade deletes.
- RNG Security: Unbiased random generation via rejection sampling (
crypto.randomInt()) for token generation.
Encryption (ShellCryptionΒ©β’)
- Algorithm: AES-256-GCM (Galois/Counter Mode)
- Key Derivation: Keys are derived from
hu-identity tokens (client-side) - Note Content: All note text is encrypted at rest in the SQLite database
- Authentication Tag: GCM mode provides integrity verification
- IV/Nonce: Unique per-encryption to prevent replay attacks
Docker Security
- The application runs on Node 22 β minimal attack surface.
- SQLite data is in a Docker bind mount (
./data/clawstack.db) β fully visible and controllable by the host operator. - API only exposes port
8282(prod) or8383(dev). The frontend never directly exposes the database. NODE_ENV=productiondisables development stack traces in API error responses.- No hardcoded API keys or secrets in the codebase β all sensitive values come from environment variables.
These are accepted trade-offs for the current development phase.
- Single-user only β no multi-user support per instance currently. Designed for individual sovereignty.
- No HTTPS enforcement β use a reverse proxy (Nginx + Caddy with TLS) for public deployments.
- No refresh token rotation β
api-tokens persist until manually revoked via/api/auth/logout. - Single-instance only β no built-in multi-node clustering. Designed for self-hosted single-server deployments.
Do NOT open a public GitHub issue for security vulnerabilities.
Instead, report privately:
- Email: Reach out to the maintainer directly (see GitHub profile for contact info).
- Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if known)
- Response time: We aim to acknowledge within 72 hours.
- Patch timeline: Security fixes are prioritized. Target: patch and release within 30 days.
- Credit: Reporters are acknowledged in release notes (anonymity respected on request).
Before exposing PinchPad to the public internet:
- Place the API behind Nginx or Caddy with TLS (HTTPS)
- Set
CORS_ORIGINto your specific frontend domain, not* - Restrict port
8282/8383to localhost, proxy via Nginx/Caddy - Enable HTTP/2 and modern TLS versions (1.2+) at the proxy layer
- Regularly rotate
api-tokens viaPOST /api/auth/logoutand re-auth - Revoke unused agent keys in the Dashboard
- Back up the
./data/clawstack.dbfile regularly (encrypt backups in transit) - Store
pinchpad_identity_key.jsonin a secure, offline location (USB key, vault, etc.) - Monitor the application logs for suspicious auth patterns (failed token validations)
- Run periodic security audits using tools like OWASP ZAP or Burp Suite Community
PinchPad adheres to OWASP Top 10 security guidelines:
- Broken Authentication β Mitigated by key-based auth, no password storage
- Sensitive Data Exposure β Mitigated by AES-256-GCM encryption at rest, TLS in transit
- Injection β Prevented by parameterized queries (better-sqlite3) and TypeScript type-safety
- Broken Access Control β Enforced by immutable middleware stack and granular permissions
- Security Misconfiguration β Hardened by strict TypeScript, environment-based secrets, helmet middleware
- Cross-Site Scripting (XSS) β Prevented by React's automatic escaping and Content Security Policy headers
Maintained by CrustAgentΒ©β’