# 4ducky.com Rust port of the original 4ducky.com image board (a Picturelicious fork, PHP 5 / MySQL, ~2012–2016). Design, layout, URLs and features are kept 1:1. The original PHP source is preserved untouched in [`legacy/`](legacy/). **Stack:** Rust · Axum · SQLite (sqlx) · Askama templates · argon2 · vanilla JS ## Running ```sh cp config.example.toml config.toml # optional, defaults work for local use cargo run --release # http://127.0.0.1:8080 ``` Run it from the repository root: the server serves `media/`, `images/` and `favicon.ico` from there. The SQLite database and all uploads live in `data_dir` (default `data/`, git-ignored). Migrations run automatically on start. ### Behind a reverse proxy Set these in `config.toml`, otherwise form posts are rejected as cross-site and every visitor looks like `127.0.0.1` (which closes registration via the one-account-per-IP lock): ```toml frontend_path = "https://your-domain.tld/" # the public URL, exactly as in the address bar trust_proxy = true # take the client IP from X-Forwarded-For secure_cookies = true # when the proxy terminates HTTPS ``` The proxy must append the client address to `X-Forwarded-For` (nginx: `proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`, Caddy does it by default). ### Admin CLI ```sh fourducky admin [--revoke] # grant/revoke admin fourducky settings --register on|off --upload on|off --message 'Dolan: "..."' fourducky invites --count 5 # create invite codes (was runfav.php) ``` Register an account on the site first, then make it admin. User id 1 is the "superadmin" by default (HoF / Ban links), see `superadmins` in the config. ## Layout | Path | What | |---|---| | `src/` | server: routing (`main.rs`), sessions/CSRF (`app.rs`, `auth.rs`), pages (`browse.rs`, `view.rs`, `account.rs`, `inbox.rs`, `misc.rs`), uploads (`uploader.rs`, `imaging.rs`, `fetch.rs`), `grid.rs`, `bbcode.rs`, `captcha.rs` | | `templates/` | Askama templates, ported markup from `legacy/templates/*.tpl.php` | | `media/` | original CSS/images, plus `4ducky.js` (replaces jQuery + picturelicious.js/functions.js/jqpr.js/lightbox.js) and `4ducky.css` (small additions) | | `migrations/` | SQLite schema (from `legacy/dumb.sql`) | | `legacy/` | the original PHP site, unchanged | ## Differences to the PHP version Behaviour and look are the same; these are the deliberate changes: **Security** - All SQL is parameterized (the inbox had SQL injection; the DB layer used `preg_replace /e`). - Output is escaped by default; BBCode runs on escaped text and only allows http(s) URLs. User websites must be http(s) (no more `javascript:` links). Abuse report links are only clickable when http(s). - Passwords: argon2id instead of unsalted md5. Sessions are random tokens stored hashed server-side. - CSRF tokens on every form, AJAX call and state-changing link (logout, reset unread, manage actions); cross-site POSTs are rejected. - Strict Content-Security-Policy (`script-src 'self'`): inline `onclick`/`