<div align="center">

# ✒️ Cruciverb

**Daily crosswords that respect you - and your handwriting.**

Handwriting-first grids, a community clue pool, and federation
between self-hosted instances. No ads. No tracking. No accounts required.

<br />

![License: CC0-1.0](https://img.shields.io/badge/license-CC0--1.0-bb4444?style=flat-square)
![Self-hosted](https://img.shields.io/badge/self--hosted-docker-2496ED?style=flat-square&logo=docker&logoColor=white)
![Built with Rust](https://img.shields.io/badge/built%20with-Rust-d4652a?style=flat-square)
![React + TypeScript](https://img.shields.io/badge/React_19%20%2B%20TS-61DAFB?style=flat-square&logo=react&logoColor=black)
![ActivityPub](https://img.shields.io/badge/federated-ActivityPub-8B5CF6?style=flat-square)
![WCAG 2.2 AAA](https://img.shields.io/badge/WCAG%202.2-AAA-228833?style=flat-square)
![No Telemetry](https://img.shields.io/badge/telemetry-none-778899?style=flat-square)

<br />

*Write the answer with your pen. Your instance, your rules.*
*No algorithmic feeds, no engagement tricks, no paywall. Just crosswords.*

</div>

---

## 🌍 What is Cruciverb?

Cruciverb is a self-hosted daily crossword server with a web frontend that treats the puzzle like something worth doing properly. Every day it generates a fresh set of grids - five difficulty levels from a tiny 5x5 warm-up to a punishing 21x21 - seeded so every solver gets the same puzzle, the same clues, the same bragging rights.

The twist is the input. You can type like everyone else, or you can draw the letters. Handwriting recognition runs in your browser through ONNX, the browser's own handwriting API, and a tesseract fallback, with an ink overlay that makes a touchscreen or stylus feel like a pen on paper.

Instances talk to each other over ActivityPub, and nobody stands in the middle. Your solves turn into signed receipts that survive when you move between instances. Clue submissions travel across the network. It's crosswords run the way a co-op should run: each community owns its own place, shares with its neighbors, and answers to no head office because there isn't one.

---

## 🧩 Everything in it

**Daily puzzles**
- Five presets each day - Glyph, Rune, Scroll, Codex, Grimoire - same seeds for the whole instance
- Streaks, solve history, and personalized ratings per puzzle
- A rotating daily challenge per preset: purist (no hints), speed demon (beat the speedrun clock), surgeon (no wrong letters), no regrets (no deleted cells), iron will (never pause), inkwell (no pencil marks), trust fall (no autocheck), and time-of-day challenges like night owl and early bird
- Speedrun mode with per-preset time limits, plus marathon mode: a stream of 120-second puzzles where one failed grid ends the run

**Inputs**
- Handwriting via ONNX, the browser handwriting API, and tesseract fallback, fused by an ensemble
- Stylus support with pressure, an ink overlay, a magnifying zoom strip, and a disambiguation popup when a letter is ambiguous
- Pencil marks, undo and redo stacks, cell flipping animations that reserve space instead of shifting the grid
- Blind mode for practice: clue text stays hidden until you fill the word

**Community**
- A clue pool where anyone can submit and vote; approved clues flow into future generations
- Community puzzles: build one in the editor, publish it, and see it on everyone's boards
- Clue trainer with per-difficulty practice rounds and a running accuracy score
- Ratings, daily leaderboards (global, local-clean, weekly, streaks, completionists), and reports with human moderation

**Play together**
- Multiplayer rooms sharing a grid live, with visible cursors for the people solving beside you
- Party games: submit a fake answer, vote on which one is real, then race to type the actual answer before your friends

**Around the puzzle**
- Portable solve receipts: signed, claimable records that follow you to another instance
- Achievements - 28 badges across speed, consistency, and community
- RSS and Atom feeds of your solves
- An embeddable player build, shareables with QR codes, and og-images for puzzle posts
- First-run onboarding that never asks for an email: pick a display name, optionally plug in a passkey, optionally import your history from another instance
- PWA offline support with an IndexedDB cache and offline progress sync

**Comfort and access**
- Light, dark, and system themes, warm palettes, and text scaling up to 200%
- A screen-reader native grid: every cell is focusable and announced with its number, letter, and state, and Alt+A / Alt+D read the across and down words aloud (/ or Alt+P reads the clue, space reads the cell, Ctrl+H the puzzle overview)
- A voice narrator with three verbosity levels and an output mode (voice, screen reader, or both) so blind players get the same flow through their own screen reader
- Colorblind palettes and reduced-motion cell animations
- Sound effects and a tor-friendly mode that keeps the page light for slow connections

---

## 🕸️ The Fediverse

Cruciverb speaks ActivityPub, the protocol behind Mastodon, Pixelfed, and the rest of the federated social web.

The heart of it: states federate with visitors and their own community, daily puzzles are announced to followers as forum posts, and everyone's solves stay as their own Outbox history. Nobody's data lives on a server they don't run. There's no "main" instance, no center - just neighbors.

Following another instance isn't just a greeting, it's trade. Once you federate:

- **Common puzzle pages** - remote puzzles appear on your boards as first-class citizens, with reputation context
- **Clue exchange** - community clues flow both ways, each carrying blind reputation scores from the delivering domain
- **Federated leaderboards** - see who's fast on this puzzle across every connected instance
- **Portable receipts** - solves are cryptographically signed, so claiming your work on a new instance verifies rather than trusts
- **Discovery without stalking** - fresh instances announce themselves, you decide who gets to federate
- **Your own moderation** - freeze federation, block domains, or approve followers manually. Nobody gets to decide your community's norms but your community

---

## 🛡️ Security and privacy

**For solvers**
- No email, no passwords, no signup wall: you're an anonymous session the moment you arrive
- Optional WebAuthn passkeys for a durable identity - and only then, if you want it, cookie binding to your browser fingerprint
- Everything you submit (clues, puzzles, votes) is tied to your community's reputation, and you can export your entire history and take it with you
- Tor-friendly mode, cookie lifetimes you control, and no analytics or trackers anywhere on the page

**For admins and moderators**
- An admin panel with real teeth: user bans, word blocklists with import and export, clue queue moderation, audit logging, and report handling
- Fine-grained federation controls: allowlist mode, follower approval, new-peer review gates, and an outbox rate limit
- Federation freeze switch, per-domain blocks with reasons, and automatic key-change detection with alerting
- Signed receipts that let any instance verify a claimed solve from any other instance

**Under the hood**
- Encrypted at rest: AES-256-GCM over display names and personal fields, key held in the environment, never in the database
- Rate limits applied per session and per IP address, at the socket, not from spoofable headers: the server reads the real connection address behind the proxy
- In-memory anti-replay and challenge-consumption handling, with the public API v1 endpoints behind keyed limits

---

## 🗂️ Project Structure

```
cruciverb/
├── cruciverb-core/           Generator engine
│   └── src/
│       ├── grid/             Grid construction, slots, symmetry
│       ├── dict/             Word lists and indices
│       ├── solver/           Fill CSP with AC-3 propagation
│       ├── import/           IPUZ and PUZ readers
│       ├── export/           IPUZ, PUZ, PDF writers
│       └── clue/             Clue seeds and blocklists
├── cruciverb-server/         Axum API
│   └── src/
│       ├── activitypub/      Actor, inbox, outbox, signatures
│       ├── routes.rs         Main route handlers
│       └── db.rs             SQLite storage layer
├── cruciverb-cli/            Command line generation
├── frontend/                 Vite + React client
│   └── src/
│       ├── components/       Grid, panels, overlays
│       ├── recognition/      Handwriting engines
│       └── hooks/            Input, focus, narrator hooks
├── scripts/                  Template and preview tools
└── Dockerfile                Multi-stage server image
```

---

## 🏃 Running it

The built image is published on the git server, so there's nothing to compile:

```bash
docker login git.lashman.live
cp docker-compose.example.yml docker-compose.yml
cp .env.example .env
docker compose up -d
```

That pulls the image, mounts a ./data directory, and serves the puzzle on port 8484. Before first start, set your real domain and federation names in .env, and add a DATA_ENC_KEY if you want storage encryption.

Prefer to run from source?

```bash
cargo run -p cruciverb-server
cd frontend && npm install && npm run dev
```

The server needs a data directory and, ideally, a domain: `INSTANCE_URL` and `AP_DOMAIN` tell the federation layer who you are, `DATA_ENC_KEY` locks the private columns, and an optional `TRUST_PROXY` tells it to take the reverse proxy's word for connection addresses. Full config stays on the host, where it belongs.

---

## 🤝 Contributing

Cruciverb is public domain software. No contributor license agreements, no assignment forms, no gatekeepers. If you want to help, help. Fork it, bend it, run your own instance with your own house rules. The code belongs to everyone equally.

Some ways to contribute: report bugs, build puzzles in the editor, submit clues through the trainer (that counts), improve recognition accuracy, add translations, or run an instance and federate with a neighbor.

Good ideas get merged. Decisions are made by the people doing the work.

---

## 📜 License

<p align="center">
  <a href="https://creativecommons.org/publicdomain/zero/1.0/">
    <img src="https://img.shields.io/badge/License-CC0_1.0_Universal-brightgreen?style=for-the-badge" alt="CC0">
  </a>
</p>

Dedicated to the **public domain** under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/). No copyright, no restrictions, no permission needed. Take it, use it, change it, share it.

Crosswords have belonged to the commons for over a century. No reason to stop now.

---

<p align="center">
  <sub>
    Built for people who like their puzzles hard and their software honest.<br>
    No ads. No tracking. No engagement dark patterns.<br>
    No profit motive. Just a grid, a pen, and a deadline.
  </sub>
</p>