5.0 KiB
AGENTS.md
Development Commands
# 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-unknowntarget dxCLI (cargo install dioxus-cli)tailwindcssCLI 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:
./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:
#[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 buildrunsnpm install && vite buildinsidelibs/tiptap-editorand renames the output file- The write page (
src/pages/admin/write.rs) initializes the editor viajs_sys::evaland pollswindow.__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_userreads thesessioncookie from the request headers and queriessessions+userstables. - 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
LazyLockglobal insrc/db/pool.rs, initialized fromDATABASE_URL - Max pool size: 10
Testing & Verification
# 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 Tailwindpublic/tiptap/— generated by Vite fromlibs/tiptap-editor//dist,/.dioxus,/targetnode_modules(insidelibs/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
imagecrate is configured without WebP support (default-features = false, features = ["jpeg", "png", "gif"]). All WebP encode/decode operations go throughzenwebpviasrc/webp.rs. This ensures consistent WebP handling across the codebase. Do not add WebP to the image crate features.
Notes
rand+getrandomwithjsfeature are required for Argon2 salt generation in WASM builds.- The
#[allow(unused_mut, unused_variables)]onWritecomponent is intentional — themutsignals are used inside#[cfg(target_arch = "wasm32")]blocks that are stripped in server builds.