# Claude AI Assistant Guidelines for Hemmelig.app Welcome to Hemmelig.app! This guide will help you navigate our codebase and contribute effectively. Think of this document as your onboarding buddy - it covers everything you need to know to maintain code quality, security, and architectural consistency. ## What is Hemmelig? Hemmelig.app is a secure secret-sharing application that lets users share encrypted messages that automatically self-destruct after being read. The name "Hemmelig" means "secret" in Norwegian - fitting, right? ### The Security Model You Must Understand **CRITICAL: Zero-Knowledge Architecture** This is the heart of Hemmelig. Before you write a single line of code, make sure you understand this: - All encryption/decryption happens **client-side only** using the Web Crypto API - The server **never** sees plaintext secrets - only encrypted blobs - Decryption keys live in URL fragments (`#decryptionKey=...`), which browsers **never send to servers** - This is our fundamental security promise to users - **do not compromise this under any circumstances** ### How Encryption Works | Component | Details | | ------------------ | ---------------------------------------------------------- | | **Algorithm** | AES-256-GCM (authenticated encryption) | | **Key Derivation** | PBKDF2 with SHA-256, 1,300,000 iterations | | **IV** | 96-bit random initialization vector per encryption | | **Salt** | 32-character random string per secret (stored server-side) | | **Implementation** | `src/lib/crypto.ts` | ## Technology Stack Here's what powers Hemmelig: | Layer | Technology | Notes | | -------------- | ---------------------------- | ------------------------------------------ | | **Runtime** | Node.js 25 | JavaScript runtime for dev and production | | **Frontend** | React 19 + Vite + TypeScript | All components use `.tsx` | | **Backend** | Hono (RPC mode) | Type-safe API client generation | | **Database** | SQLite + Prisma ORM | Schema in `prisma/schema.prisma` | | **Styling** | Tailwind CSS v4 | Class-based, light/dark mode support | | **State** | Zustand | Lightweight state management | | **Auth** | better-auth | Session-based with 2FA support | | **i18n** | react-i18next | All user-facing strings must be translated | | **Monitoring** | prom-client | Prometheus metrics | | **API Docs** | Swagger UI | OpenAPI documentation | ## Project Structure Here's how the codebase is organized: ``` hemmelig.app/ ├── api/ # Backend (Hono) │ ├── app.ts # Main Hono application setup │ ├── auth.ts # Authentication configuration │ ├── config.ts # Application configuration │ ├── openapi.ts # OpenAPI/Swagger spec & UI │ ├── routes.ts # Route aggregator │ ├── routes/ # Individual route handlers │ │ ├── secrets.ts # Secret CRUD operations │ │ ├── secret-requests.ts # Secret request management │ │ ├── account.ts # User account management │ │ ├── files.ts # File upload/download │ │ ├── user.ts # User management (admin) │ │ ├── instance.ts # Instance settings │ │ ├── analytics.ts # Usage analytics │ │ ├── invites.ts # Invite code management │ │ ├── api-keys.ts # API key management │ │ ├── setup.ts # Initial setup flow │ │ ├── health.ts # Health check endpoints │ │ └── metrics.ts # Prometheus metrics │ ├── lib/ # Backend utilities │ │ ├── db.ts # Prisma client singleton │ │ ├── password.ts # Password hashing (Argon2) │ │ ├── files.ts # File handling utilities │ │ ├── settings.ts # Instance settings helper │ │ ├── webhook.ts # Webhook dispatch utilities │ │ ├── analytics.ts # Analytics utilities │ │ ├── constants.ts # Shared constants │ │ └── utils.ts # General utilities │ ├── middlewares/ # Hono middlewares │ │ ├── auth.ts # Authentication middleware │ │ ├── ratelimit.ts # Rate limiting │ │ └── ip-restriction.ts # IP allowlist/blocklist │ ├── validations/ # Zod schemas for request validation │ └── jobs/ # Background jobs (cleanup, etc.) ├── src/ # Frontend (React) │ ├── components/ # Reusable UI components │ │ ├── Layout/ # Layout wrappers │ │ ├── Editor.tsx # TipTap rich text editor │ │ └── ... │ ├── pages/ # Route-level components │ │ ├── HomePage.tsx │ │ ├── SecretPage.tsx │ │ ├── SetupPage.tsx # Initial admin setup │ │ ├── Verify2FAPage.tsx # Two-factor verification │ │ ├── Dashboard/ # Admin dashboard pages │ │ └── ... │ ├── store/ # Zustand stores │ │ ├── secretStore.ts # Secret creation state │ │ ├── userStore.ts # Current user state │ │ ├── hemmeligStore.ts # Instance settings │ │ ├── themeStore.ts # Light/dark mode (persisted) │ │ └── ... │ ├── lib/ # Frontend utilities │ │ ├── api.ts # Hono RPC client │ │ ├── auth.ts # better-auth client │ │ ├── crypto.ts # Client-side encryption │ │ ├── hash.ts # Hashing utilities │ │ └── analytics.ts # Page view tracking │ ├── i18n/ # Internationalization │ │ └── locales/ # Translation JSON files │ └── router.tsx # React Router configuration ├── prisma/ │ └── schema.prisma # Database schema ├── scripts/ # Utility scripts │ ├── admin.ts # Set user as admin │ └── seed-demo.ts # Seed demo data ├── server.ts # Production server entry point └── vite.config.ts # Vite configuration ``` ## Getting Started ### Development Commands ```bash # Install dependencies npm install # Start frontend with hot reload npm run dev # Start API server (runs migrations automatically) npm run dev:api # Build for production npm run build # Run production server npm run start # Database commands npm run migrate:dev # Create and apply migrations npm run migrate:deploy # Apply pending migrations (production) npm run migrate:reset # Reset database (destructive!) npm run migrate:status # Check migration status # Utility scripts npm run set:admin # Promote a user to admin npm run seed:demo # Seed database with demo data # Code quality npm run format # Format code with Prettier npm run format:check # Check formatting npm run test:e2e # Run end-to-end tests with Playwright ``` ## Coding Guidelines ### Core Principles 1. **Make Surgical Changes** - Only change what's necessary. Don't refactor, optimize, or "improve" unrelated code. 2. **Follow Existing Patterns** - Consistency trumps personal preference. Match what's already in the codebase. 3. **Ask Before Adding Dependencies** - Never add, remove, or update packages without explicit permission. 4. **Security First** - Extra scrutiny for anything touching encryption, authentication, or data handling. --- ## Frontend Guidelines ### Component Structure Keep your components clean and consistent: ```tsx // Use functional components with hooks export function MyComponent({ prop1, prop2 }: MyComponentProps) { const { t } = useTranslation(); // Always use i18n for user-facing text const [state, setState] = useState(initialValue); const handleAction = () => { // Event handler logic }; return
{/* Always support light/dark mode */}
; } ``` ### Design System Our UI follows these principles: - **Compact design** with minimal padding - **Sharp corners** - no `rounded-*` classes - **Light and dark mode** support is mandatory - **Mobile-first** responsive design ### Styling with Tailwind ```tsx // GOOD: Light mode first, then dark variant, sharp corners className = 'bg-white dark:bg-dark-800 text-gray-900 dark:text-white border border-gray-200 dark:border-dark-600'; // BAD: Missing light mode variant className = 'dark:bg-dark-800'; // BAD: Using rounded corners className = 'rounded-lg'; // BAD: Using arbitrary values when design tokens exist className = 'bg-[#111111]'; // Use bg-dark-800 instead ``` ### Custom Color Palette ```javascript // tailwind.config.js defines these colors: dark: { 900: '#0a0a0a', // Darkest background 800: '#111111', // Card backgrounds 700: '#1a1a1a', // Input backgrounds 600: '#222222', // Borders 500: '#2a2a2a', // Lighter borders } light: { 900: '#ffffff', 800: '#f8fafc', 700: '#f1f5f9', 600: '#e2e8f0', 500: '#cbd5e1', } ``` ### State Management with Zustand ```typescript // src/store/exampleStore.ts import { create } from 'zustand'; interface ExampleState { data: string; setData: (data: string) => void; } export const useExampleStore = create((set) => ({ data: '', setData: (data) => set({ data }), })); ``` ### API Calls Always use the typed Hono RPC client - it gives you full type safety: ```typescript import { api } from '../lib/api'; // The client is fully typed based on backend routes const response = await api.secrets.$post({ json: { secret: encryptedData, expiresAt: timestamp }, }); const data = await response.json(); ``` ### React Router Loaders We use React Router v7 with data loaders. Here's the pattern: ```typescript // In router.tsx { path: '/dashboard/secrets', element: , loader: async () => { const res = await api.secrets.$get(); return await res.json(); }, } // In the component import { useLoaderData } from 'react-router-dom'; export function SecretsPage() { const secrets = useLoaderData(); // Use the pre-loaded data } ``` --- ## Internationalization (i18n) ### The Golden Rule **When you add or modify any user-facing string, you MUST update ALL translation files.** ### Supported Languages We currently support these languages: | Language | File Path | | --------- | ----------------------------- | | English | `src/i18n/locales/en/en.json` | | Danish | `src/i18n/locales/da/da.json` | | German | `src/i18n/locales/de/de.json` | | Spanish | `src/i18n/locales/es/es.json` | | French | `src/i18n/locales/fr/fr.json` | | Italian | `src/i18n/locales/it/it.json` | | Dutch | `src/i18n/locales/nl/nl.json` | | Norwegian | `src/i18n/locales/no/no.json` | | Swedish | `src/i18n/locales/sv/sv.json` | | Chinese | `src/i18n/locales/zh/zh.json` | ### How to Add New Strings 1. **Add to English first** - `src/i18n/locales/en/en.json` 2. **Add to ALL other locale files** - Even if you use English as a placeholder, add the key to every file 3. **Use nested keys** - Follow the existing structure (e.g., `secret_page.loading_message`) ```tsx // Using translations in components const { t } = useTranslation(); // GOOD

{t('secret_page.loading_message')}

// BAD: Hardcoded string

Loading...

``` ### Translation Checklist Before committing, verify: - [ ] Added key to `en/en.json` with proper English text - [ ] Added key to `da/da.json` - [ ] Added key to `de/de.json` - [ ] Added key to `es/es.json` - [ ] Added key to `fr/fr.json` - [ ] Added key to `it/it.json` - [ ] Added key to `nl/nl.json` - [ ] Added key to `no/no.json` - [ ] Added key to `sv/sv.json` - [ ] Added key to `zh/zh.json` > **Tip**: If you don't know the translation, use the English text as a placeholder and add a `// TODO: translate` comment in your PR description. --- ## Backend Guidelines ### Route Structure ```typescript // api/routes/example.ts import { Hono } from 'hono'; import { zValidator } from '@hono/zod-validator'; import { z } from 'zod'; const exampleRoute = new Hono().post( '/', zValidator( 'json', z.object({ field: z.string().min(1).max(1000), }) ), async (c) => { const { field } = c.req.valid('json'); // Handler logic here return c.json({ success: true }); } ); export default exampleRoute; ``` ### Database Operations ```typescript // Always use the Prisma client from api/lib/db.ts import prisma from '../lib/db'; // Use transactions for multiple operations await prisma.$transaction([ prisma.secrets.create({ data: { ... } }), prisma.file.createMany({ data: files }), ]); ``` ### Input Validation - **Always** validate input using Zod schemas - Place reusable schemas in `api/validations/` - Validate at the route level using `zValidator` ### Error Handling ```typescript // Return consistent error responses return c.json({ error: 'Descriptive error message' }, 400); // Zod automatically handles validation errors ``` ### Database Schema Changes 1. Modify `prisma/schema.prisma` 2. Run `npm run migrate:dev --name descriptive_name` 3. Test the migration locally 4. Commit both schema and migration files --- ## Health & Monitoring ### Health Endpoints Hemmelig provides Kubernetes-ready health checks: | Endpoint | Purpose | Checks | | ------------------- | --------------- | ------------------------- | | `GET /health/live` | Liveness probe | Process is running | | `GET /health/ready` | Readiness probe | Database, storage, memory | ### Metrics Endpoint Prometheus metrics are available at `GET /metrics`. This endpoint can be optionally protected with authentication. --- ## Security Checklist When modifying security-sensitive code, verify: - [ ] Encryption/decryption remains client-side only - [ ] Decryption keys never reach the server - [ ] Input validation is present on all endpoints - [ ] Authentication checks are in place where required - [ ] Rate limiting is applied to sensitive endpoints - [ ] No sensitive data in logs or error messages - [ ] File uploads are validated and sanitized --- ## Testing - End-to-end tests use [Playwright](https://playwright.dev/) - find them in `tests/e2e/` - Run e2e tests with `npm run test:e2e` - When adding new features, add corresponding test files --- ## Common Patterns ### Creating a New Page 1. Create component in `src/pages/` 2. Add route with loader in `src/router.tsx` 3. **Add translations to ALL locale files** 4. Ensure light/dark mode support ### Creating a New API Endpoint 1. Add route handler in `api/routes/` 2. Register in `api/routes.ts` 3. Add Zod validation schema 4. Frontend types update automatically via Hono RPC ### Adding a New Store 1. Create store in `src/store/` 2. Follow existing Zustand patterns 3. Export from the store file --- ## What NOT to Do These are hard rules - no exceptions: 1. **Never** modify encryption logic without explicit approval 2. **Never** log or store plaintext secrets server-side 3. **Never** send decryption keys to the server 4. **Never** bypass input validation 5. **Never** add dependencies without approval 6. **Never** modify unrelated code "while you're in there" 7. **Never** use `any` types in TypeScript 8. **Never** commit `.env` files or secrets 9. **Never** disable security features "temporarily" 10. **Never** add user-facing strings without updating ALL translation files --- ## Quick Reference ### Key File Locations | What | Where | | ---------------------- | ---------------------- | | Frontend components | `src/components/` | | Page components | `src/pages/` | | API routes | `api/routes/` | | API config | `api/config.ts` | | Database schema | `prisma/schema.prisma` | | Translations | `src/i18n/locales/` | | Stores | `src/store/` | | Client-side encryption | `src/lib/crypto.ts` | | API client | `src/lib/api.ts` | | Backend setup | `api/app.ts` | | Design tokens | `tailwind.config.js` | ### Environment Variables ```bash # Required DATABASE_URL= # SQLite connection (file:./data/hemmelig.db) BETTER_AUTH_SECRET= # Auth secret key (generate a random string) BETTER_AUTH_URL= # Public URL of your instance # Optional - Server HEMMELIG_PORT= # Port the server listens on (default: 3000) HEMMELIG_BASE_URL= # Public URL (required for OAuth callbacks) HEMMELIG_TRUSTED_ORIGIN= # Additional trusted origin for CORS # Optional - Analytics HEMMELIG_ANALYTICS_ENABLED= # Enable/disable analytics tracking (default: true) HEMMELIG_ANALYTICS_HMAC_SECRET= # HMAC secret for anonymizing visitor IDs # Optional - Managed Mode (all instance settings via env vars) HEMMELIG_MANAGED= # Enable managed mode (true/false) ``` See `docs/env.md` for the full environment variable reference. --- ## Managed Mode When `HEMMELIG_MANAGED=true`, all instance settings are controlled via environment variables instead of the database. The admin dashboard becomes read-only. ### Managed Mode Environment Variables | Variable | Description | Default | | ---------------------------------------- | ------------------------------------------- | ------- | | `HEMMELIG_MANAGED` | Enable managed mode | `false` | | `HEMMELIG_INSTANCE_NAME` | Display name for your instance | `""` | | `HEMMELIG_INSTANCE_DESCRIPTION` | Description shown on the homepage | `""` | | `HEMMELIG_INSTANCE_LOGO` | Base64-encoded logo image (max 512KB) | `""` | | `HEMMELIG_ALLOW_REGISTRATION` | Allow new user signups | `true` | | `HEMMELIG_REQUIRE_EMAIL_VERIFICATION` | Require email verification | `false` | | `HEMMELIG_DEFAULT_SECRET_EXPIRATION` | Default expiration in hours | `72` | | `HEMMELIG_MAX_SECRET_SIZE` | Max secret size in KB | `1024` | | `HEMMELIG_IMPORTANT_MESSAGE` | Alert banner shown to all users | `""` | | `HEMMELIG_ALLOW_PASSWORD_PROTECTION` | Allow password-protected secrets | `true` | | `HEMMELIG_ALLOW_IP_RESTRICTION` | Allow IP range restrictions | `true` | | `HEMMELIG_ALLOW_FILE_UPLOADS` | Allow users to attach files | `true` | | `HEMMELIG_ENABLE_RATE_LIMITING` | Enable API rate limiting | `true` | | `HEMMELIG_RATE_LIMIT_REQUESTS` | Max requests per window | `100` | | `HEMMELIG_RATE_LIMIT_WINDOW` | Rate limit window in seconds | `60` | | `HEMMELIG_REQUIRE_INVITE_CODE` | Require invite code for registration | `false` | | `HEMMELIG_ALLOWED_EMAIL_DOMAINS` | Comma-separated allowed domains | `""` | | `HEMMELIG_REQUIRE_REGISTERED_USER` | Only registered users create secrets | `false` | | `HEMMELIG_DISABLE_EMAIL_PASSWORD_SIGNUP` | Disable email/password signup (social only) | `false` | | `HEMMELIG_WEBHOOK_ENABLED` | Enable webhook notifications | `false` | | `HEMMELIG_WEBHOOK_URL` | Webhook endpoint URL | `""` | | `HEMMELIG_WEBHOOK_SECRET` | HMAC secret for webhook signatures | `""` | | `HEMMELIG_WEBHOOK_ON_VIEW` | Send webhook when secret is viewed | `true` | | `HEMMELIG_WEBHOOK_ON_BURN` | Send webhook when secret is burned | `true` | | `HEMMELIG_METRICS_ENABLED` | Enable Prometheus metrics endpoint | `false` | | `HEMMELIG_METRICS_SECRET` | Bearer token for `/api/metrics` | `""` | See `docs/managed.md` for full documentation. --- ## Feature Reference ### Organization Features Hemmelig supports enterprise deployments with: - **Invite-Only Registration** - Require invite codes to sign up - **Email Domain Restrictions** - Limit signups to specific domains (e.g., `company.com`) - **Instance Settings** - Configure `allowRegistration`, `requireInvite`, `allowedEmailDomains` ### Analytics System Privacy-focused analytics with: - HMAC-SHA256 hashing for anonymous visitor IDs (IPs never stored) - Automatic bot filtering using `isbot` - Tracks: Homepage (`/`) and Secret view page (`/secret`) - Admin dashboard at `/dashboard/analytics` ### Authentication better-auth provides: - Session-based authentication - Two-factor authentication (2FA) with TOTP - Custom signup hooks for email domain validation - Admin user management - Social login providers (GitHub, Google, Microsoft, Discord, GitLab, Apple, Twitter) ### Social Login Providers Social login can be configured via: 1. **Admin Dashboard** - Instance Settings > Social Login tab (when not in managed mode) 2. **Environment Variables** - Using `HEMMELIG_AUTH_*` variables (always available) Environment variable format: ```bash HEMMELIG_AUTH_GITHUB_ID=your-client-id HEMMELIG_AUTH_GITHUB_SECRET=your-client-secret HEMMELIG_AUTH_GOOGLE_ID=your-client-id HEMMELIG_AUTH_GOOGLE_SECRET=your-client-secret # ... etc for microsoft, discord, gitlab, apple, twitter ``` See `docs/social-login.md` for full setup instructions. --- ## Need Help? - Check existing patterns in the codebase first - Read related components/routes for context - When in doubt, ask for clarification rather than making assumptions --- _This document is the source of truth for development practices in this repository._