This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
BentoBox Downloads is a full-stack TypeScript application that generates customized ZIP files containing BentoBox (a Minecraft server plugin) and its addons. It mirrors releases from GitHub and dev builds from Jenkins CI, caching JARs in SQLite databases. Users select addons via a React frontend, and the Express backend generates a ZIP on-the-fly.
yarn # Install dependencies
yarn build # Production build (TypeScript compile + Webpack bundle)
yarn start # Run the built server (serves on port 8080, runs from dist/)
yarn dev # Full dev rebuild + start server
yarn site # Frontend-only dev rebuild + start serverNote: The clean script uses Windows rd command. On macOS/Linux, manually rm -rf dist if needed.
Setup: Requires npm i -g yarn, then yarn, then optionally yarn add sqlite3 if the native module fails to build.
Backend (src/index.ts, src/api/):
- Express server handles
/api/*routes viaApiManagerclass and serves the React SPA for all other routes ApiManager(src/api/api.ts) is the core class: manages GitHub/Jenkins polling, JAR caching, download counting, and ZIP generation- Scheduled cron job (every 6 minutes) updates addon releases from GitHub (via Octokit) and dev builds from Jenkins (ci.codemc.io)
- Three SQLite databases:
JarCache.sqlite(cached JARs),Downloads.sqlite(download counts), and old version cache — all managed via Sequelize ORM (src/api/models/database.ts)
Frontend (src/web/):
- React 17 SPA with React Router, styled with Tailwind CSS + twin.macro/styled-components
- Pages: Presets (
/), Custom builder (/custom), Third-party catalog (/thirdparty), Blueprints (/blueprints), Submit (/submit), Account (/account), Terms (/terms), Privacy (/privacy) - Components in
src/web/components/, API calls viasrc/web/ApiRequestManager.tsusing SWR
Configuration files (project root, read at runtime from ../ relative to dist/):
config.json— addon and preset definitions (the primary data source for all addons)thirdparty.json— third-party addon registryenv.json(optional, fromenv.example.json) — GitHub token, Discord webhook, port settings, optionalweblinkblock (url/path/branch) for the blueprint syncInstallation-Guide.txt— bundled in generated ZIP files
Blueprint catalog (src/api/weblink.ts, src/api/blueprintCatalog.ts):
WeblinkSyncshallow-clonesBentoBoxWorld/weblinkintodata/weblink/on startup andgit pull --ff-onlys it on the same 6-minute cron used for addon refresh. Default branch ismaster.buildCatalog()scansweblink/blueprints/<gameMode>/, parses.blueprintfiles and bundle.jsonfiles (per BentoBox'sblueprint.schema.json/blueprint-bundle.schema.json), and derives stats: dimensions, block-type counts, entity-type counts, air count, biome list, sinking flag.- The optional
blueprints/catalog.jsonoverlay in weblink supplies curation fields (displayName,description,author,authorLink,tags,license,image) plus tag colours and game-mode display names; overlay fields take precedence over what is in the.blueprintfile. - Images live at
weblink/blueprints/images/<gameMode>/<name>.png(recommended 800×450, with optional<name>.thumb.pngat 400×225). The catalog auto-detects them at sync time when the overlay does not specifyimage. The Express handler atsrc/index.tsserves them under/blueprints/images/...with a path-traversal guard and PNG-only whitelist. - Endpoints:
GET /api/blueprints(full catalog),GET /api/blueprints/download?id=<gameMode>/<name>&type=blueprint|bundle(single file),GET /api/blueprints/zip?ids=[...]|gameMode=<gm>(multi-select or whole-game-mode zip). IDs are validated against a strict regex and resolved paths are checked against the blueprints root before any read.
Blog (src/api/blog.ts, src/api/models/blog.ts, src/web/components/blog/, src/web/components/admin/BlogTab.tsx, src/web/components/admin/BlogEditor.tsx):
- Backed by
data/Blog.sqlitevia Sequelize. Models:posts(slug-keyed permalinks, draft/published, markdown source + sanitized HTML cache, view counter) andpost_revisions(one row per save, pruned daily after 90 days). - Markdown source is rendered server-side with
markdown-it+sanitize-htmlon every save; the HTML is what's served to readers, so the public path doesn't ship a markdown engine. The admin editor preview usesreact-markdown+remark-gfm+remark-breaksclient-side. - Permissions: an author is anyone with
users.isAdmin = trueORusers.canAuthorBlog = true. Pre-grant viaenv.blog_author_discord_ids(mirrorsadmin_discord_ids).requireBlogAuthormiddleware inauth.tsgates writes; the Admin page filters its tab list to only "Blog" for users who are blog authors but not full admins. - Endpoints:
GET /api/blog/posts?page&tag&q(paginated public list with optional tag filter and case-insensitive substring search across title/summary/body),GET /api/blog/posts/:slug(public, bumps view count),GET /api/blog/tags(aggregated tag counts across published posts),GET /api/blog/comments-config(public Giscus config or null),GET /api/blog/feed.xml(RSS 2.0),GET /blog/sitemap.xml(sitemap),GET /blog/highlight.css(highlight.js theme served from the bundled github-dark theme with our background override). Author endpoints under/api/blog/admin/posts(CRUD),/api/blog/admin/posts/:id/publish(with optional{ at: <epoch_ms> }body to schedule),/api/blog/admin/posts/:id/unpublish,/api/blog/admin/imagesfor multipart uploads (5 MB cap, PNG/JPG/WebP/GIF only). Admin-only:GET /api/admin/authorsandPOST /api/admin/authorsfor granting/revoking thecanAuthorBlogflag (admins are implicit authors and can't be revoked from there). All writes requireX-Csrf-TokenandrequireBlogAuthor(orrequireAdminfor the authors endpoints). - Permalinks at
/blog/p/:slugare server-rendered with OG/Twitter Card meta + JSON-LD Article schema + canonical URL byBlogManager.renderPostPage(). Express checks for the path insrc/index.tsbefore falling through to the SPA. Onblog.bentobox.world, the bare/p/:slugand/feed.xmlpaths also work via a hostname check (isBlogHost). - On
draft → publishedtransition the manager fires a Discord webhook configured byenv.discord_blog_webhook_url(full URL or/api/webhooks/...path). Failures are logged and ignored — they never block the publish. - Subdomain plumbing: in production set
env.cookie_domain = ".bentobox.world"so Discord sessions cover bothdownload.*andblog.*. Recommended nginx config: a singleserverblock forblog.bentobox.worldthat rewrites/(.*)to/blog/$1before proxying to the same upstream:8080— keeps the React Router and SSR paths uniform. - Uploaded images live under
data/blog/images/YYYY/MM/<sha256-prefix>.<ext>and are served by Express via the/blog/images/...static handler (path-traversal guarded, MIME whitelist). - Markdown extras: code fences are server-side syntax-highlighted via
highlight.js(auto-detect for unfenced langs); GitHub-style alert blockquotes (> [!NOTE],[!TIP],[!IMPORTANT],[!WARNING],[!CAUTION]) are post-processed into<div class="callout callout-X">boxes with coloured labels. - Scheduled posts have
status='scheduled'and a futurepublishedAt. A per-minute cron promotes them topublishedand fires the Discord webhook at promotion time, not at scheduling time. - Comments: optional Giscus integration. When
env.giscusis present (withrepo,repoId,category,categoryId), each post page embeds the widget below the body. The CSP allowlist forgiscus.app(script/frame/connect) is added unconditionally; nothing renders whenenv.giscusis missing because/api/blog/comments-configreturnsnull. - Phase scope: Phase 1 = list + permalink + RSS + OG + Discord webhook + image uploads + draft/publish, single author. Phase 2 added tags + tag archive, code highlighting, GFM callouts, scheduled publishing, sitemap. Phase 3 (current) adds the multi-author grant UI in admin (Authors tab), public substring search across title/summary/body, and opt-in Giscus comments. Phase 4 candidates: dev.to cross-post button, nightly git backup of post markdown, Plausible/Umami analytics, Mermaid diagrams, series support.
Auth + submissions (src/api/auth.ts, src/api/submissions.ts, src/api/models/auth.ts):
- Discord OAuth (server-side authorization-code flow,
identifyscope only). Sessions stored indata/Auth.sqlitevia Sequelize (User,Session,Submissiontables). Session cookie is HttpOnly,SameSite=Lax,Securein production, and carries an opaque server-generated session id. 30-day TTL, max 5 concurrent sessions per user, daily prune cron. - CSRF: per-session token returned by
GET /api/me, required asX-Csrf-Tokenon every POST. - Rate limits: 30 OAuth callbacks/min/IP; 3 submissions/hour, 10/day per Discord user.
POST /api/blueprints/submitaccepts a multipart upload (max 5 MB), JSON-parses, ajv-validates againstsrc/api/schemas/blueprint.schema.json(vendored frombentobox/), runsvalidateBlueprintSubmission(rejects command-block materials and opaqueinventorypayloads), checks slug collision against the local clone, then opens a PR onBentoBoxWorld/weblinkvia Octokit usingenv.weblink_github_token— branchsubmit/<discordId>/<slug>-<ts>with two file commits (the.blueprintand the patchedcatalog.json).- Helmet config in
src/index.tsis the canonical CSP — Discord OAuth navigation works without an explicit allowlist because helmet's CSP does not constrain top-level navigation. env.jsonkeys:discord_client_id,discord_client_secret,discord_redirect_uri,weblink_github_token(fine-grained PAT scoped toBentoBoxWorld/weblinkwithcontents: write+pull-requests: write), and the blog-related keysblog_author_discord_ids,discord_blog_webhook_url,blog_base_url,cookie_domain.
- All addon metadata is configuration-driven via
config.json; adding an addon means editing that file - TypeScript types for config are in
src/config.d.ts - Build outputs to
dist/; the server runs fromdist/and reads config files from../(project root) - Webpack bundles frontend to
dist/web/; TypeScript compiles backend todist/ - ESLint + Prettier enforced (config in
.eslintrc.jsand.prettierrc.js)
GitHub Actions (.github/workflows/build.yml) runs on push/PR to develop: tests Node.js 20.x/22.x with yarn && yarn add sqlite3 && yarn build.
When you need to inspect source code for a dependency (e.g., BentoBox, addons):
- Check local Maven repo first:
~/.m2/repository/— sources jars are named*-sources.jar - Check the workspace: Look for sibling directories or Git submodules that may contain the dependency as a local project (e.g.,
../bentoBox,../addon-*) - Check Maven local cache for already-extracted sources before downloading anything
- Only download a jar or fetch from the internet if the above steps yield nothing useful
Prefer reading .java source files directly from a local Git clone over decompiling or extracting a jar.
In general, the latest version of BentoBox should be targeted.
Related projects are checked out as siblings under ~/git/:
Core:
bentobox/— core BentoBox framework
Game modes:
addon-acidisland/— AcidIsland game modeaddon-bskyblock/— BSkyBlock game modeBoxed/— Boxed game mode (expandable box area)CaveBlock/— CaveBlock game modeOneBlock/— AOneBlock game modeSkyGrid/— SkyGrid game modeRaftMode/— Raft survival game modeStrangerRealms/— StrangerRealms game modeBrix/— plot game modeparkour/— Parkour game modeposeidon/— Poseidon game modegg/— gg game mode
Addons:
addon-level/— island level calculationaddon-challenges/— challenges systemaddon-welcomewarpsigns/— warp signsaddon-limits/— block/entity limitsaddon-invSwitcher//invSwitcher/— inventory switcheraddon-biomes//Biomes/— biomes managementBank/— island bankBorder/— world border for islandsChat/— island chatCheckMeOut/— island submission/votingControlPanel/— game mode control panelConverter/— ASkyBlock to BSkyBlock converterDimensionalTrees/— dimension-specific treesdiscordwebhook/— Discord integrationDownloads/— BentoBox downloads siteDragonFights/— per-island ender dragon fightsExtraMobs/— additional mob spawning rulesFarmersDance/— twerking crop growthGravityFlux/— gravity addonGreenhouses-addon/— greenhouse biomesIslandFly/— island flight permissionIslandRankup/— island rankup systemLikes/— island likes/dislikesLimits/— block/entity limitslost-sheep/— lost sheep adventureMagicCobblestoneGenerator/— custom cobblestone generatorPortalStart/— portal-based island startpp/— pp addonRegionerator/— region managementResidence/— residence addonTopBlock/— top ten for OneBlockTwerkingForTrees/— twerking tree growthUpgrades/— island upgrades (Vault)Visit/— island visitingweblink/— data repo (blueprints, challenges, MCG configs, addon downloads matrix); the Downloads server clones a copy of this for the Blueprints tabCrowdBound/— CrowdBound addon
Data packs:
BoxedDataPack/— advancement datapack for Boxed
Documentation & tools:
docs/— main documentation sitedocs-chinese/— Chinese documentationdocs-french/— French documentationBentoBoxWorld.github.io/— GitHub Pages sitewebsite/— websitetranslation-tool/— translation tool
Check these for source before any network fetch.
world.bentobox:bentobox→~/git/bentobox/src/