# AGENTS.md ## Development Commands ```bash # Dev server — runs tailwindcss watch + dx serve (needs PostgreSQL running) make dev # Production build — builds editor, compiles CSS, then dx build --release make build # Just CSS make css # one-shot make css-watch # watch mode # Rust only (no CSS/editor) cargo build cargo clippy cargo test dx check # Dioxus type-check dx serve # Dev server without Tailwind watch ``` **Command order matters for production**: `make build` internally runs `build-editor` → `tailwindcss` → `dx build --release`. Do not run `dx build --release` alone if you need the editor or CSS. ## Prerequisites - Rust 1.95+ with `wasm32-unknown-unknown` target - `dx` CLI (`cargo install dioxus-cli`) - `tailwindcss` CLI v4 (standalone binary) - PostgreSQL running locally ## Environment Create `.env` (not committed — see `.env.example`): ``` DATABASE_URL=postgres://postgres:postgres@localhost:5432/yggdrasil RUST_LOG=info ``` Run migrations before first dev server start: ```bash ./migrate.sh # preferred: auto-creates DB, runs all migrations in order # or manually: psql $DATABASE_URL -f migrations/001_init.sql ``` ## Architecture: Conditional Compilation This is a Dioxus 0.7 fullstack project with **two independent gates**. Getting this wrong is the most common source of compilation errors. | Gate | Applies to | Used for | |------|-----------|----------| | `#[cfg(feature = "server")]` | Server binary only | Database, env loading, background tasks, server functions body | | `#[cfg(target_arch = "wasm32")]` | WASM frontend only | localStorage, DOM APIs, cookie manipulation, web_sys calls | **Critical**: `feature = "server"` and `target_arch = "wasm32"` are **mutually exclusive** at build time, but both default features (`web` + `server`) are enabled in `Cargo.toml`. The `dx` CLI handles feature selection during builds. **Stub pattern**: `src/db/mod.rs` provides a `DummyPool` when the `server` feature is disabled so the crate still compiles for WASM. Do not remove this. **Dead code allowances**: Auth utilities (`password.rs`, `session.rs`) carry `#[allow(dead_code)]` because they are imported by `api/auth.rs` but the compiler sees them as unused in WASM builds where server function bodies are stripped. ## Server Functions Server functions live in `src/api/auth.rs` and use the macro: ```rust #[server(Name, "/api")] pub async fn fn_name(...) -> Result<..., ServerFnError> ``` These are callable from both client and server Rust code; Dioxus handles HTTP transport automatically. ## Tiptap Editor Subproject The rich-text editor is a separate Vite project in `libs/tiptap-editor/`. - Built as an **IIFE** library exposing `window.TiptapEditor` - Output goes to `public/tiptap/` - `make build` runs `npm install && vite build` inside `libs/tiptap-editor` and renames the output file - The write page (`src/pages/admin/write.rs`) initializes the editor via `js_sys::eval` and polls `window.__tiptap_ready` Do not edit files in `public/tiptap/` directly — they are build artifacts. ## Auth & Session - **Registration**: first user becomes `admin`; subsequent registrations are rejected with `"Registration is closed"`. - **Login**: sets an **HttpOnly** cookie via `FullstackContext::add_response_header` (server-side). - **Session validation**: `get_current_user` reads the `session` cookie from the request headers and queries `sessions` + `users` tables. - **Background cleanup**: `tasks::session_cleanup::run_cleanup()` deletes expired sessions every hour on a tokio task. ## Database - PostgreSQL via `tokio-postgres` + `deadpool-postgres` - Pool is a `LazyLock` global in `src/db/pool.rs`, initialized from `DATABASE_URL` - Max pool size: 10 ## Testing & Verification ```bash # Standard Rust test suite cargo test # Dioxus type-check (catches component/Router issues) dx check # Lint cargo clippy ``` There are currently no integration tests requiring a database connection. ## Build Artifacts to Ignore The following are generated and gitignored: - `public/style.css` — generated by Tailwind - `public/tiptap/` — generated by Vite from `libs/tiptap-editor/` - `/dist`, `/.dioxus`, `/target` - `node_modules` (inside `libs/tiptap-editor/`) ## rust-analyzer `rust-analyzer.toml` excludes generated directories (`node_modules`, `public`, `.dioxus`, `.omc`, `dist`). If rust-analyzer shows spurious errors in generated files, check that the exclusion list is respected by your editor. ## Image Processing - **WebP handling**: The `image` crate is configured without WebP support (`default-features = false, features = ["jpeg", "png", "gif"]`). All WebP encode/decode operations go through `zenwebp` via `src/webp.rs`. This ensures consistent WebP handling across the codebase. Do not add WebP to the image crate features. ## Notes - `rand` + `getrandom` with `js` feature are required for Argon2 salt generation in WASM builds. - The `#[allow(unused_mut, unused_variables)]` on `Write` component is intentional — the `mut` signals are used inside `#[cfg(target_arch = "wasm32")]` blocks that are stripped in server builds.