npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@onnie81/murdoku-spor

v1.26.0

Published

Kludoku — online murder-mystery logic puzzle (sudoku-style placement deduction). Deploy artifact for games.onniehex.dev.

Readme

Kludoku

A complete online murder-mystery placement-deduction game — the pencil-and-paper placement-deduction genre (Cluedo × Sudoku), fully procedural and multiplayer-social.

A victim and a cast of suspects must be placed on an illustrated crime scene: each row and each column holds exactly one person, furniture blocks squares, rooms and testimonies constrain everyone — and whoever ends up alone with the victim is the killer.

Features

  • Procedural cases — every board is generated: room layout, furniture, ground truth, and a minimal clue set proven to have a unique solution that is solvable without blind guessing (propagation + single-assumption logic).
  • 5 difficulties — Beginner 5×5 → Expert 9×9, graded by the number of assumption rounds a logical solver needs, not just clue count.
  • Daily challenges — same five seeded cases for the whole world (UTC), with global and friends leaderboards, streaks, and a case archive.
  • Full play kit — tap or drag placement, legal-move highlighting with automatic graying of non-allowed squares, X marks, pencil marks (small numbers or mini faces), clue spotlighting + strike-through, undo/redo, hints, pause, mistake tracking, three assist levels.
  • Accounts, sessions, friends — instant guest play (per-device session), optional username+password accounts that adopt the guest history, friend requests, and daily leaderboards. SQLite storage, one file.
  • History tracker — per-user record: solved/played, streaks, best and average times per difficulty, full case log; autosave + resume on any device.
  • Responsive + touch — phone, tablet, desktop; pointer-based drag & drop, long-press to mark X, keyboard shortcuts (1-9, N, X, U, R, E).
  • Multilingual — English and Spanish shipped; clues are structured data rendered through per-language templates, so adding a language is one file.
  • Furniture that matters — blockers, seats you can be seated on (armchairs, two-square sofas), beds you can be lying in, and wall-mounted windows/paintings/mirrors that live on the walls themselves (windows count from both sides). Clues use all of it: "was seated on the sofa", "was not lying in any bed", "was beside a window".
  • Original art & sound — 12 hand-drawn SVG suspect portraits, colorful furniture sprites, 8 scene themes; synthesized noir soundtrack and sound effects (WebAudio, zero assets, all original).
  • Phone-first play screen — fullscreen mode, an always-visible lateral suspect overlay that turns translucent while you place, floating controls, and a clue bottom-sheet.

Quick start (Docker)

docker compose up -d --build
# open http://localhost:8080

Data (users, games, puzzles) lives in the murdoku-data volume as a single SQLite file. Back it up with:

docker run --rm -v murdoku-data:/data -v "$PWD:/out" debian \
  cp /data/murdoku.db /out/murdoku-backup.db

HTTPS / domain

The stack ships a Caddy service that terminates TLS and reverse-proxies the app. On docker compose up -d it automatically obtains and renews a Let's Encrypt certificate for the domain — no manual certs. Requirements:

  • ports 80 and 443 open to the internet (Caddy needs 80/443 for the ACME challenge),
  • DNS A records, all DNS-only (not proxied, or the challenge can't reach Caddy): kludoku.com and www.kludoku.com → the server's public IP — the canonical home. Legacy games.onniehex.dev (and optionally onniehex.dev) point at the same IP and 301-redirect to kludoku.com.

MURDOKU_DOMAIN still names the legacy redirect host. Port 5000 stays mapped straight to the app for direct testing / health checks. The app sets trust proxy, so Secure cookies and client IPs work correctly behind Caddy.

Cloud delivery (npm)

Published as @onnie81/murdoku-spor. First-time server setup:

curl -fsSL https://registry.npmjs.org/@onnie81/murdoku-spor/-/murdoku-spor-<version>.tgz \
  | tar xz && sudo bash package/scripts/npm-server-bootstrap.sh

This installs Docker/git if missing, unpacks a real git repo (from the bundled history) into /opt/murdoku, builds, and installs a systemd timer that polls npm every 60s and rebuilds on each new published version.

Configuration

Operator settings live in /opt/murdoku/.env on the server (created by hand, preserved across deploys; docker compose reads it automatically). Every feature below is off until its variables are set — the app runs fine without any.

| Env | Default | Meaning | |---|---|---| | PORT | 8080 | app HTTP port (inside the container) | | DATA_DIR | /data (container) / ./data (bare) | SQLite location | | MURDOKU_DOMAIN | games.onniehex.dev | domain Caddy requests a cert for | | PUBLIC_URL | https://kludoku.com | canonical origin (OAuth redirects, reset links) | | ADMIN_TOKEN | (unset = admin off) | bearer token for /admin + /api/admin/* | | SMTP_URL | (unset = mail off) | e.g. smtps://user:[email protected]:465 — enables password-reset mail | | MAIL_FROM | Kludoku <no-reply@…> | From header for outgoing mail | | ADSENSE_CLIENT | (unset = ads off) | AdSense publisher id ca-pub-… (see MONETIZATION.md) | | AUTH_GOOGLE_CLIENT_ID / AUTH_GOOGLE_CLIENT_SECRET | (unset) | "Sign in with Google" | | AUTH_FACEBOOK_CLIENT_ID / AUTH_FACEBOOK_CLIENT_SECRET | (unset) | "Sign in with Facebook" | | AUTH_APPLE_CLIENT_ID / AUTH_APPLE_TEAM_ID / AUTH_APPLE_KEY_ID / AUTH_APPLE_PRIVATE_KEY | (unset) | "Sign in with Apple" (private key PEM, \n-escaped) |

OAuth redirect URIs to register with each provider: https://kludoku.com/api/auth/oauth/google/callback (Google), …/facebook/callback (Facebook), …/apple/callback (Apple — also requires the domain to be verified in the Apple developer console).

Monitoring

With ADMIN_TOKEN set, GET /admin serves a small authenticated dashboard (users, DAU/WAU, wins by difficulty, telemetry top events, stuck rate, DB sizes) and GET /api/admin/metrics returns the same as JSON:

curl -H "Authorization: Bearer $ADMIN_TOKEN" https://kludoku.com/api/admin/metrics

POST /api/admin/premium {username, premium} toggles an account's ad-free flag. /admin is noindex and disallowed in robots.txt.

Development

npm install && npm --prefix client install
npm run build        # vite build -> server/public
npm run dev          # serve on :8080
npm test             # engine + API integration tests
node tests/e2e.mjs   # Playwright visual/interaction smoke (needs chromium)

Dev loop with hot reload: npm run dev in one terminal, npm --prefix client run dev in another (Vite on :5173 proxies /api).

Architecture

shared/    puzzle engine (isomorphic, seeded & deterministic)
  rng.js         xmur3 + mulberry32 seeded RNG
  model.js       cast, themes, difficulty bands, clue-type sets
  clues.js       scene context + bitmask tables, predicates, clue-pool gen
  bits.js        96-bit bitset primitives
  solver.js      CSP: propagation (singles, arc revision, hidden singles,
                 room counts), uniqueness counting, "what-if" logical solver
  generator.js   rooms → furniture → ground truth (victim alone with exactly
                 one person) → clue selection/minimization → difficulty grading
server/    Express + better-sqlite3
  db.js          schema + queries (users, sessions, puzzles, games, friendships)
  auth.js        cookie sessions, bcrypt accounts, guest→account merge
  puzzles.js     daily seeding (UTC), free-play pregeneration pool
  api.js         REST: auth, settings, daily, games, history, stats,
                 friends, leaderboards
client/    React + Vite single-page app
  game.js        per-game store: placement, legality, notes, X, undo/redo,
                 autosave, accusation flow
  components/    Board (SVG scene), Tray, CluePanel, Play/Home/Daily/
                 History/Friends/Settings views
  i18n/          en/es dictionaries + structured clue renderer
  art/           original SVG portraits, furniture, logo

The daily seed is daily:<UTC date>; generation is fully deterministic, and generated dailies are persisted, so every player gets the identical case.

Honesty model: the solution ships to the client to power instant assists (wrong-placement flags, accusation feedback). Finishing is validated server-side against the stored solution, but a determined user could read the solution from devtools — fine for a friendly game, worth knowing for prizes.

Adding a language

Copy client/src/i18n/en.js to e.g. fr.js, translate the values (UI strings, roles, rooms, objects, scenes, clue templates), and register it in client/src/i18n/index.js (DICTS, LANGS). Rooms/objects carry n (label) and p (in-sentence with article) forms so grammar stays natural.

Originality

Game mechanics follow the published "Murdoku" puzzle genre by Manuel Garand (one person per row/column, map evidence, the murderer alone with the victim). All code, characters, scenes, artwork, and text in this project are original. The game ships under the original name "Kludoku"; "Murdoku" as a name/brand belongs to its owners and is referenced only to credit the genre. For a public commercial deployment, consider your own title.