Files
5ducky/README.md
T
kibiandClaude Opus 5.5 d0f2d526df Port 4ducky to Rust (Axum + SQLite)
Same design, layout, URLs and features as the PHP version: browse grid,
image view with AJAX comments, BBCode, likes, uploads (file, URL and
bookmarklet), quicktagger, profile, block list, inbox, invites, Hall of
Ducks, Truhe, abuse reports and the admin tools.

- Parameterized SQL, escaped templates (askama), argon2 passwords,
  server-side sessions, CSRF tokens on every write, strict script CSP
- SSRF-safe remote image fetching, magic-byte upload checks
- Self-hosted captcha replaces reCAPTCHA v1; analytics/social widgets removed
- Vanilla JS replaces jQuery and its plugins with the same DOM behaviour
- Reverse proxy support (frontend_path, trust_proxy, secure_cookies)
- Admin CLI: admin, settings, invites

See README.md for running it and the full list of changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 02:15:05 +02:00

92 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <name> [--revoke] # grant/revoke admin
fourducky settings --register on|off --upload on|off --message '<b>Dolan:</b> "..."'
fourducky invites <name> --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`/`<script>` became `data-*`
hooks in `media/4ducky.js`.
- Remote image fetching (URL upload, comment URLs, bookmarklet) blocks private/loopback/link-local
addresses, limits redirects, size (8 MB) and time.
- Uploads are checked by magic bytes, decoded with size limits, and get server-generated file names.
- Admin endpoints check permissions server-side (HoF/Ban only for `superadmins`); TimeBlock duration
is computed on the server.
- Login, register, abuse and PMs are rate limited per IP/user.
**Replaced or removed**
- reCAPTCHA v1 → self-hosted image captcha.
- Google Analytics, AddThis, Facebook like-box, coin widget → removed. `/chat`, `/forum`, `/shop` keep
their layout and take an optional embed URL from the config.
- No e-mail: accounts are active right after registering (as they were in practice).
- The bookmarklet opens a small first-party popup with a "Post" button instead of uploading from a
cross-site iframe (modern browsers block third-party cookies; the old flow was also a CSRF hole).
- jQuery slimScroll → native thin scrollbars.
**Bugs fixed on the way**
- White-style users couldn't comment (form was inside the black-style branch).
- Blocking a user from the profile page wiped your website field.
- Comments could still be posted to closed threads via AJAX.
- `.b2 { width: 64x }` CSS typo made guest teaser thumbnails overflow (fixed in `4ducky.css`).
- Quicktagger referenced a missing `#home` element and never worked.
The original content is kept verbatim, including rank badges and texts, for archival purposes.