A lightweight, self-hosted imageboard written in Haskell.
Simple to deploy. Easy to modify. Built for speed.
haskell · imageboard · chan · warp · sqlite · self-hosted · ffmpeg · imagemagick · tor · onion-service · captcha · image-upload · webm · mp4 · pdf-upload · moderation
Website · Source · Report a Bug
- Features
- Media Support
- Thumbnail Processing
- Requirements
- Installation
- Authentication and Security
- CAPTCHA
- Static File and MIME Type Support
- Configuration
- Production Deployment
- Tor Onion Service
- Updating Haskchan
- Development
- Contributing
- Reporting Bugs
- License
| Category | Details |
|---|---|
| Core | Imageboard-style threads and replies |
| Uploads | Images, PDFs, WebM/MP4/MOV/M4V, and audio |
| Thumbnails | Automatic WebP and JPEG generation |
| Anti-spam | CAPTCHA support with click-to-refresh |
| Accounts | User registration, authentication, session management |
| Moderation | Moderator and administrator tooling |
| Storage | SQLite database |
| Backend | Haskell + Warp web server |
| Theming | Custom Haskchan theme with dark/light mode |
| Auth | Encrypted cookies with expiration; revoked on account deletion |
| Access control | Registration can be disabled; board creation is globally configurable |
| Deployment | Self-hosted, with optional Tor onion-service support |
Haskchan detects uploaded files by inspecting their contents, not just the filename extension.
| Type | Formats |
|---|---|
| Image | JPEG, PNG, GIF, WebP |
| Video | WebM, MP4, QuickTime/MOV, M4V |
| Audio | OGG, FLAC, M4A, AAC, MP3 |
| Document | |
| Text | TXT |
Note: PDF files are detected via the
%PDF-file signature and stored with theapplication/pdfMIME type.
Haskchan uses FFmpeg for media decoding and ImageMagick for thumbnail encoding.
Uploaded image
│
▼
FFmpeg decoder
│
▼
Haskell image frame
│
▼
Temporary PNG
│
├──► ImageMagick ──► WebP thumbnail
│
└──► ImageMagick ──► JPEG thumbnail
This avoids the older ffmpeg-light encoder path, which can fail with newer FFmpeg versions.
Verify ImageMagick is installed:
which convertExpected output:
/usr/bin/convert
Haskchan currently works best on Linux (Ubuntu/Debian).
- GHC 8.10.7
- Stack
- SQLite
- ImageMagick
- FFmpeg 4.4.x libraries
- build-essential
- pkg-config
- zlib1g-dev
git clone https://github.com/konamicode01/haskchan.git
cd haskchanOn Ubuntu/Debian:
sudo apt update
sudo apt install -y \
build-essential \
pkg-config \
sqlite3 \
imagemagick \
zlib1g-devVerify ImageMagick:
convert -versionNote: Haskchan uses the
ffmpeg-lightHaskell package, which relies on the older FFmpeg 4.x API/ABI. Newer FFmpeg versions may not be compatible.
Download and extract:
cd /root
wget https://ffmpeg.org/releases/ffmpeg-4.4.4.tar.xz
tar -xf ffmpeg-4.4.4.tar.xz
cd ffmpeg-4.4.4Configure:
./configure \
--prefix=/opt/ffmpeg4 \
--libdir=/opt/ffmpeg4/lib \
--enable-shared \
--disable-static \
--disable-asm \
--disable-programs \
--disable-docBuild and install:
make -j"$(nproc)"
sudo make installRegister the libraries:
echo "/opt/ffmpeg4/lib" | sudo tee /etc/ld.so.conf.d/ffmpeg4.conf
sudo ldconfigSet the library path:
export LD_LIBRARY_PATH=/opt/ffmpeg4/libTo persist across sessions:
echo 'export LD_LIBRARY_PATH=/opt/ffmpeg4/lib' >> ~/.bashrc
source ~/.bashrccurl -sSL https://get.haskellstack.org/ | shVerify:
stack --versioncd /root/haskchan
export LD_LIBRARY_PATH=/opt/ffmpeg4/lib
stack buildNote: The first build may take a while — Stack downloads GHC and all Haskell dependencies. The repo contains a manually maintained
phi.cabal; Stack may report that it's ignoringpackage.yamlin favor of the Cabal file — this is expected.
export LD_LIBRARY_PATH=/opt/ffmpeg4/lib
stack runThe development server starts on the address configured by the application.
-
Encrypted authentication cookies with expiration
-
Authentication verifies the account still exists in the SQLite database
-
Deleting a user account invalidates that user's existing session
-
Registration can be fully disabled — when disabled, both the registration page and submission endpoint return:
403 Registration is closed -
Board creation is controlled separately via the global board-creation setting. Administrators can still create boards even when ordinary user board creation is disabled.
- Included for account creation and posting
- Click-to-refresh — click the CAPTCHA image to get a fresh challenge without reloading the page
- Served with
no-cacheheaders to prevent stale challenges from being reused by browsers or proxies
Uploaded file types are detected from content and served with the correct MIME type:
| Extension | MIME Type |
|---|---|
.webp |
image/webp |
.jpg / .jpeg |
image/jpeg |
.png |
image/png |
.gif |
image/gif |
.svg |
image/svg+xml |
.ico |
image/x-icon |
.css |
text/css |
.js |
text/javascript |
.mp4 |
video/mp4 |
.webm |
video/webm |
.mp3 |
audio/mpeg |
.ogg |
audio/ogg |
.flac |
audio/flac |
.pdf |
application/pdf |
.txt |
text/plain |
Project configuration lives in the Haskchan source tree. Before deploying publicly, configure:
- Database location
- Secret file
- Static file directories
- CAPTCHA directory
- Font path
- Server bind address
- Server ports
- Global site settings
Note: Make sure the user running Haskchan has write permission to the directories used for uploaded files and generated thumbnails.
Haskchan can run directly via Warp, or behind a reverse proxy such as nginx or Caddy.
Internet
│
▼
nginx / Caddy
│
▼
Haskchan
Or directly:
Internet
│
▼
Haskchan
For production, use a service manager such as systemd rather than running stack run in an interactive terminal.
Example systemd unit:
[Unit]
Description=Haskchan Imageboard
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/root/haskchan
Environment="LD_LIBRARY_PATH=/opt/ffmpeg4/lib"
ExecStart=/usr/local/bin/stack run
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl daemon-reload
sudo systemctl enable haskchan
sudo systemctl start haskchanCheck status:
sudo systemctl status haskchanView logs:
journalctl -u haskchan -fHaskchan can optionally expose a local HTTP listener for a Tor onion service:
Tor
│
▼
127.0.0.1:7000
│
▼
Haskchan
Example Tor configuration:
HiddenServiceDir /var/lib/tor/haskchan/
HiddenServicePort 80 127.0.0.1:7000
This allows Haskchan to be reached via a .onion address without requiring the onion service to use the public TLS certificate for the clearnet domain.
Pull the latest source:
git pull origin mainRebuild:
export LD_LIBRARY_PATH=/opt/ffmpeg4/lib
stack buildRestart the service:
sudo systemctl restart haskchanCheck status:
sudo systemctl status haskchan --no-pagerstack build # Build
stack run # Run
stack test # Run testsHaskchan is open source and contributions are welcome, including:
- Bug fixes
- Security improvements
- Performance improvements
- New media formats
- UI improvements
- Documentation
- Deployment improvements
- Haskell code improvements
- Tests
Before submitting changes, build the project and confirm existing functionality still works.
When reporting a bug, please include:
- Operating system
- GHC version
- Stack version
- FFmpeg version
- Relevant Haskchan logs
- Steps to reproduce the problem
Note: Do not post private keys, authentication secrets, or other sensitive server configuration.
See the repository for the project's current license.