Skip to content

Releases: Kitsune-Den/KitsuneCommand

v2.8.2 — KitsuneCommand

04 Jun 06:16
b219698

Choose a tag to compare

Full notes
· Patch release — three stability fixes from a live Windows prod box,
plus the timezone field on the Server Restart panel grows up into a
host-resolvable dropdown. No new features, no schema changes.

Fixed

  • No more stack overflow from LogCallbackEvent re-entrancy during
    shutdown.
    EventBroadcaster subscribed to LogCallbackEvent and
    broadcast each fired log message over the WebSocket. When KC was
    mid-shutdown and the WebSocket manager had already stopped, the
    inner Broadcast threw ("The current state of the manager is not
    Start.") and the catch block called Log.Warning(...) — which fired
    another LogCallbackEvent, re-entered the same subscriber, threw
    again, logged again, and recursed until the stack overflowed and KC
    crashed. Observed live on a Windows prod box: a botched
    GracefulRestart left the WS manager stopped while the event bus
    was still alive, and KC logged "Error in event handler for
    LogCallbackEvent: The requested operation caused a stack overflow."
    dozens of times before the service died. Fix: a [ThreadStatic]
    re-entrancy guard scoped to the LogCallbackEvent path only (other
    events don't loop back into the logger), and the failure path on
    that path writes to Console.Error instead of Log.Warning so the
    recursion can't restart.
  • Restart Server no longer probes systemctl on Windows. The
    POST /api/server/restart endpoint always tried
    sudo -n systemctl restart 7daystodie.service first and waited up to
    5 seconds before falling back to in-game shutdown. On Windows there
    is no systemctl, so every restart click wasted ~5s and logged a
    misleading systemctl restart failed or not available... warning.
    On a live Windows prod box this code path was the trigger for a
    cascading crash — the fallback in-game shutdown ran concurrent with
    a stuck main-thread operation and snowballed into a stack overflow.
    Fix: new Core/OsRestartStrategy.cs picks the path per OS. Windows
    goes straight to in-game shutdown and relies on the service
    supervisor (NSSM AppExit Restart, which is its default) to bounce
    7DTD on game exit; an INFO-level log line announces the chosen path
    instead of the warning. Linux behavior is unchanged — systemctl
    first, fall back to in-game shutdown for Restart=always. Console
    command krestart already uses the OS-agnostic
    GracefulRestartFeature.TriggerNow (no shell-out) so it didn't need
    to change.
  • Graceful restart timezone is now host-resolvable. The
    GracefulRestart feature called
    TimeZoneInfo.FindSystemTimeZoneById with whatever string was in
    the persisted config. On .NET Framework 4.8 / Windows, IANA IDs
    like America/Los_Angeles aren't recognized, so the tick threw,
    the catch block logged "bad schedule config" every 30 seconds, and
    the daily restart never fired. Observed in the wild with a value
    of "UTC-5" (not even an IANA ID) where nobody realized the
    feature was a no-op. Two-part fix: (1) LoadPersistedSettings
    now probes the configured timezone, and if it doesn't resolve,
    falls back to TimeZoneInfo.Local (or Utc if Local is unset),
    logs one INFO line, and persists the corrected value back to the
    DB so the next boot is clean. (2) New GET /api/server/timezones
    endpoint returns the runtime-resolvable timezone list sorted by
    UTC offset; the Settings → Server Restart panel now renders a
    PrimeVue Select populated from that endpoint instead of a
    free-text input that admins would fill with strings the host
    couldn't parse. Saves id (whatever the runtime accepts:
    Windows registry IDs on .NET Framework, IANA on .NET Core),
    shows displayName to the user.

v2.8.1 — KitsuneCommand

29 May 19:27
85e812b

Choose a tag to compare

Full notes
· VIP tiers, join diagnostics, and the PackRelay Launcher land here.
Supersedes v2.8.0, which shipped without the Windows SkiaSharp native
(broken map rendering) and self-reported the wrong version.

Added

  • VIP tiers. Per-player tiers with a first-login welcome pack and
    recurring tier gifts. New 012_vip_tiers migration; set a player's
    tier straight from the Players panel.
  • Join diagnostics. A companion KitsuneJoinDiag mod feeds a new
    "Join Attempts" panel - see who tried to connect, when, and why a
    join failed.
  • PackRelay Launcher v0.1. Scaffold that hooks the main menu
    (XUiC_MainMenu.OnOpen) - groundwork for launching straight into a
    PackRelay-published modpack.

Fixed

  • Windows map rendering no longer breaks on a clean install. The
    release build sourced libSkiaSharp.dll only from version-pinned
    NuGet-cache paths and skipped it with a warning when they weren't
    there, so the v2.8.0 zip shipped without the Windows SkiaSharp native
    and MapTileRenderer threw "Unable to load library 'libSkiaSharp'".
    Both Skia natives now come from the committed src/.../x64/ copies
    (same as sqlite3.dll), and a missing native is a hard build error
    instead of a silent skip.
  • Mod version matches the release again. ModInfo.xml was stuck at
    2.7.4, so the in-game mod list disagreed with the release tag. Bumped
    to track the release.

v2.7.3 — KitsuneCommand

19 May 11:31
b0592b0

Choose a tag to compare

Full notes
· Patch release — mods page gets a "Check for Updates" button that
cross-references installed mods against Nexus, and the mod uploader
now streams large files to disk instead of buffering in RAM (no more
OOM on big modpacks).

Added

  • Mods → Check for Updates — new button on the Mods page that
    cross-references every installed mod against Nexus by exact-name
    match and surfaces a per-row badge (update available / version differs) linking to the matching Nexus page. Stateless, on-demand —
    no mod_origins table or persisted match cache. Skips
    IsProtected (KitsuneCommand itself). New
    Services/ModUpdateService.cs + POST /api/mods/check-updates +
    i18n keys across all eight locales (en/de/fr/es translated;
    ja/ko/zh-CN/zh-TW carry English placeholders pending translation).
    (PR #86)

Fixed

  • Mod uploader — large modpacks no longer OOM the mod process.
    ModsController.UploadMod was using MultipartMemoryStreamProvider,
    which buffers the entire multipart body in RAM — a 500MB modpack
    allocated 500MB+ inside the mod process, enough to OOM-kill prod on
    an 8GB box with the game server already resident. Swapped in
    MultipartFileStreamProvider so the upload streams to a temp file
    in constant memory. Temp dir cleaned up in a finally block.
    Frontend FileUpload cap bumped 200MB → 1GB to match. (PR #87)

v2.7.2 — KitsuneCommand

18 May 17:43
5cd492b

Choose a tag to compare

Full notes
· Patch release — deploy-script reliability + the first signed
release. From here on every release zip ships with a SHA-256 sum
and a minisign signature attached so downloaders can verify
provenance before extracting.

Added

  • Signed releases — every release zip now ships with two
    sidecar files: <zip>.sha256 (BSD-style sum, sha256sum -c-verifiable
    from any platform) and <zip>.minisig (minisign Ed25519 signature).
    Verify via sha256sum -c + minisign -Vm <zip> -P <public-key>.
    Public key + verify instructions live in docs/RELEASES.md.
    Rationale, key-rotation, and the one-time setup walkthrough in
    docs/SIGNING.md. Minisign chosen for parity
    with the PackRelay launcher's updater (also minisign-based) — one
    signing primitive across the product family. (PR #78, kanban #138)
  • Tag-push release workflow.github/workflows/release.yml
    turns git tag -a vX.Y.Z && git push --tags into a draft GitHub
    Release with all three signed assets attached and the body
    auto-pulled from the matching ## [X.Y.Z] CHANGELOG section.
    Maintainer reviews the draft + clicks Publish. Pre-release tags
    (-rc.N, -beta.N) get marked prerelease automatically. The
    workflow gracefully degrades when minisign secrets aren't set —
    still produces the zip + .sha256, skips the .minisig with a
    warning. (PR #78, kanban #136)
  • README "signed releases" pill in the existing pill row, linking
    to the verify section.

Fixed

  • tools/deploy.ps1 heredoc through ssh — Windows OpenSSH
    joins argv with spaces, not newlines, so multi-line scripts
    passed as ssh $remote $script collapsed to one line on the
    remote and bash choked (bash: line 1: set: -: invalid option).
    Fix: pipe the heredoc to ssh ... bash -s via stdin. Newlines
    survive verbatim. Two call sites changed (snapshot + restore
    phases). Worked around by hand during the v2.7.0 + v2.7.1
    deploys; future v2.7.x deploys can use tools\deploy.ps1
    directly from Windows. (PR #77, kanban #145)
  • tools/deploy.sh rsync graceful degradation — native
    Windows Git Bash doesn't ship rsync; previously the script
    died at the sync step. Now detects rsync via command -v. If
    present, fast path. If absent, falls back to scp + snapshot
    pattern (parity with deploy.ps1). Refactored sync block into
    sync_via_rsync + sync_via_scp_snapshot helpers. Behavior on
    Linux/macOS unchanged. Real-world tested against prod with rsync
    masked — works end-to-end. (PR #77, kanban #172)

v2.7.1 — PackRelay panel readability patch

17 May 22:47
a81ebd1

Choose a tag to compare

What's new since v2.7.0

Patch release — PackRelay panel readability fixes that landed right after the v2.7.0 cut. Pure CSS, no backend changes, no migration. Cosmetic but worth shipping standalone instead of letting the rough edges sit on main until the next feature release.

🎨 PackRelay panel: readable on the dark theme

Two contrast bugs that snuck through the v2.7.0 cut, both in frontend/src/views/PackRelayView.vue:

  • Card titles + form labels were washing out at ~3:1 contrast on the dark theme. Bumped .page-title, .p-card-title, .form-label, and .meta-label to --kc-text-primary (#e8eaed) so they read at WCAG-AA contrast against the dark card surface. (PR #74)
  • Cards rendered on a pale default surface because KC's global.css only set --p-card-color (text) and never --p-card-background — so PrimeVue's stock pale card bled through and made the just-brightened labels invisible. Scoped :deep(.p-card) override in the view forces the card background to --kc-bg-card with a --kc-border outline. Same pattern other KC views (e.g. SettingsView) already use locally. (PR #75)

Net result: the PackRelay tab now matches the rest of the panel's dark-card chrome.

Installation

Server (KitsuneCommand)

Download KitsuneCommand-v2.7.1.zip, extract, drop KitsuneCommand/ into your server's Mods/ directory. Same zip works on Windows and Linux.

If you're already on v2.7.0, this is the first patch release where tools/deploy.{sh,ps1} is the recommended upgrade path — no migration this time, just a drop-in replacement:

tools/deploy.sh --build      # Linux / WSL / Git Bash
tools\deploy.ps1 -Build      # Windows native

Client (optional — KitsuneTraderUnlock)

Unchanged from v2.6.4.

Operators upgrading from v2.7.0

No migration. No config changes. No new env vars. Drop the new zip in place, restart, done.

Coming next

  • PackRelay curator epic (kanban #146–#154) — locking pack creation to a curated source whitelist starting with Nexus Mods, hybrid manifest+cache architecture, per-publisher Premium API key model. First feature work for packrelay.cloud v0.50 series.
  • In-panel update (kanban #139–#144) — still the goal that retires manual deploys entirely.

🤖 Release notes generated with Claude Code

v2.7.0 — Publish to PackRelay.cloud from the panel

17 May 21:06
1168b49

Choose a tag to compare

What's new since v2.6.4

Marquee feature: Publish to PackRelay.cloud from the panel, in one click. Plus a sweeping test-suite green-up (32 fails -> 0), a license switch to PolyForm Noncommercial 1.0.0, and the first version of a proper deploy script so you stop accidentally clobbering your appsettings.json.

🎯 Publish to PackRelay

New PackRelay entry in the sidebar (between Mods and Backups). Paste your packrelay.cloud API token + Ed25519 signing key once (encrypted at rest with AES-256-CBC + HMAC-SHA256), curate a modpack via the existing Mods → Modpack flow, then one click publishes a signed, content-addressed pack to packrelay.cloud:

  • Walks bundled mod folders, SHA-256s every file streaming
  • Idempotent file uploads — re-publishing the same content short-circuits at /files/exists
  • Single-shot ≤4 MB / multipart ≥4 MB (Vercel platform limit)
  • Canonical-JSON manifest signed locally with Ed25519, verified server-side
  • Live progress UI (Walking → Hashing → Uploading → Signing → Posting → Done)
  • 409 duplicate_version surfaces as "already published" (soft success for retries)

Five backend stages went in: C# crypto primitives (canonical-JSON + Ed25519 + SHA-256), HTTP client, publish orchestrator with parallel uploads, encrypted settings storage, controller + Vue tab. Backed by 75 new NUnit cases against RFC 8032 + reference vectors + cloud-shape parity.

(PR #66, kanban #129–#133)

📝 License: MIT → PolyForm Noncommercial 1.0.0

KC is now licensed under PolyForm Noncommercial 1.0.0. Reads like MIT (read, fork, modify, redistribute, build on it) with one carve-out: no commercial use. Personal use, research, hobby projects, nonprofits, education, and government use all explicitly permitted. Commercial license available on request.

Not OSI-approved by design. Same change landed in parallel on the companion repos (PackRelay Cloud, Kitsunebi Cloud). (PR #71)

🛠 Better deploy: tools/deploy.{sh,ps1}

For the manual-deploy-until-in-panel-update-lands era, two new scripts wrap the dance:

  • Snapshot Config/appsettings.json + Plugins/* on the remote
  • Wipe the mod folder, scp/rsync the fresh dist (with --delete semantics)
  • Restore the snapshot, chown -R ada:ada, systemctl restart
  • Health check via systemctl is-active (failed → dump last 20 journalctl lines + exit non-zero)

Bash flavor uses rsync --exclude in one pass. PowerShell flavor uses ssh+scp with snapshot pattern (no rsync needed on Windows native). Config via .deploy.env (gitignored). Documented in docs/DEPLOYING.md. (PR #72)

🧪 Test suite green-up (32 fails → 0 passes)

The test suite had been 32 failed / 30 passed / 3 skipped on main. Two unrelated root causes:

  1. 31 DB-backed integration tests failing OneTimeSetUp with DllNotFoundException: 'sqlite3'. The native sqlite3.dll wasn't copied to bin output. Fixed: <None CopyToOutputDirectory> rule in KitsuneCommand.csproj + a new TestAssemblySetUp.cs that preloads x64/sqlite3.dll via LoadLibrary (same shape ModEntry.cs uses at production runtime).
  2. 1 stale AuthService test asserting against the buggy Update(account) shape that the production fix had already replaced with UpdatePassword(id, hash).

After: 177 passed / 0 failed / 3 skipped. The 3 skips are intentional [Ignore] cases for paths that need a live 7DTD process to exercise. (PR #66)

📚 CHANGELOG.md + docs/RELEASES.md

Backfilled keep-a-changelog 1.1.0 entries for every release from v2.0.0 to v2.7.0. Documents the MAJOR.MINOR.PATCH convention (semver-ish with KC-shaped edges) and today's manual release process. (PR #66, kanban #137)

✨ UX + chrome polish

  • PackRelay panel styling — switched from stock Tailwind utility classes to KC's --kc-text-secondary / --kc-bg-card / --kc-border CSS variables. Labels and hints had been washing out at ~3:1 contrast on the dark theme; now ~5.4:1 (WCAG AA). Cards get visible 1.5rem gaps + internal dividers. (PR #68)
  • PackRelay sidebar entry between Mods and Backups (was missing from #66 — route + view shipped but navItems array wiring slipped). (PR #67)
  • README pill row — added a Codecov pill alongside CI / release / license / PRs; canonicalized all badge URLs to Kitsune-Den/KitsuneCommand. (PRs #70, #71)

🔧 CI hygiene

  • Coverage workflow refresh: concurrency cancel-in-progress (~50% CI minute savings on rapid pushes), npm cache, codecov-action v4 → v5, fail_ci_if_error: true, flags: frontend (for the day backend coverage lands). (PR #69)

Installation

Server (KitsuneCommand)

Download KitsuneCommand-v2.7.0.zip, extract, drop KitsuneCommand/ into your server's Mods/ directory. The same zip works on both Windows and Linux — the mod auto-detects OS and loads the correct native libraries.

Client (optional — KitsuneTraderUnlock)

Unchanged from v2.6.4. Only needed for V2's locked-down client if you want to interact with trader-area blocks when an admin opens them up via ktrader off.

Operators upgrading from v2.6.4

First time using the new deploy script

If you're upgrading from a v2.6.x panel, this is a good moment to start using tools/deploy.{sh,ps1} so future updates don't accidentally clobber your Config/appsettings.json. Copy .deploy.env.example to .deploy.env, fill in your KC_DEPLOY_HOST, then:

tools/deploy.sh --build      # Linux / WSL / Git Bash
tools\deploy.ps1 -Build      # Windows native (uses ssh + scp)

See docs/DEPLOYING.md for the full walkthrough.

Manual upgrade (legacy)

Drop the new zip in place and restart. Migration 011_packrelay.sql applies on first boot (creates the encrypted-settings table for the publish flow). No other migration steps needed.

If you customized Config/appsettings.json (changed WebUrl, port numbers, CORS), back it up first — manual scp without the new deploy script will overwrite it.

Coming next

  • In-panel update — kanban #139–#144 tracks an SSH-less version where the admin clicks Update in the panel and KC swaps itself in via the systemd pre-start hook. Will replace the manual deploy entirely.
  • Automated release workflow — kanban #136 — tag-push → GHA → draft GH release with built zip. This is the last fully-manual release before that lands.

🤖 Release notes generated with Claude Code

v2.6.4 — Modpack feature

13 May 00:33
40d2277

Choose a tag to compare

What's new since v2.6.2

A marquee feature + a slate of UX, ops, and i18n improvements. v2.6.3 was bumped on main but never tagged — this release folds it into v2.6.4 so the public history stays clean.

🎯 Modpack — Bundle installed mods for player download

Like Minecraft modpacks. Admin picks installed mods, names + versions a "pack," builds a zip, publishes. Once published, a top-right download CTA appears on the login page so players can grab matching client-side mods without needing a panel account.

  • Mods → new Modpack tab with mod picker, name + version + description
  • Three-state workflow: draft → published → archived
  • Anonymous metadata + download endpoints (no auth required)
  • Atomic temp-file zip build so public downloaders can't grab a half-written archive mid-rebuild
  • Bundle target is <game-root>/KitsuneModpacks/ (parallel to KitsuneBackups)

(PR #64)

🛠 Graceful Restart — Daily restarts with player warnings

Schedule daily restarts with a player-friendly in-game countdown.

  • Configurable warning ladder (default: 10 min / 5 min / 1 min / 0 min, escalating colors)
  • IANA-timezone schedule (DST-aware) — e.g. 04:00 America/Los_Angeles follows the actual wall clock through PDT/PST transitions
  • krestart [minutes] console command for on-demand restarts
  • Manual "Restart Now" button in the Settings → Server Restart tab
  • Re-entrancy guard prevents overlapping countdowns

Replaces the systemd-timer-plus-shell-script stopgap from v2.6.1.

(PRs #57, #58)

🐛 Backups silent write loss

BackupService had a redundant conn.Open() call on every method — six call sites total. The custom System.Data.SQLite build that ships with KC silently dropped INSERT/UPDATE statements after a double-Open (no exception, just no row written). Reads worked, so the bug went unnoticed until the first hands-on test of the backup feature, where ZIPs appeared on disk but the audit table stayed empty.

(PR #56)

🌍 German, French, Spanish locales

Polite-form translations across ~250 keys / 30 namespaces in each. Browser auto-detect now picks the right one for de-* / fr-* / es-* visitors. Tooltip on the language switcher notes that in-game broadcast messages don't auto-translate.

8 supported locales total: English, German, French, Spanish, Chinese Simplified, Chinese Traditional, Japanese, Korean.

(PRs #59, #61, #62)

✨ Favicon refresh

Regenerated all four favicon variants (favicon.svg, favicon-16x16.png, favicon-32x32.png, favicon.ico) from kitsune-command-logo-transparent.png so the browser tab matches the panel's sidebar logo. Tooling: tools/regen-favicons.py (Pillow only, single source-of-truth).

(PR #60)

📝 README updates

Features list now reflects Vote Rewards (was missed in v2.6.2's README pass), Graceful Restart, Trader Zone Toggle, and Modpack.

(PRs #63, #65)

Installation

Server (KitsuneCommand)

Download KitsuneCommand-v2.6.4.zip, extract, drop KitsuneCommand/ into your server's Mods/ directory.

Client (optional — KitsuneTraderUnlock)

Unchanged from v2.6.1. Only needed for V2's locked-down client if you want to interact with trader-area blocks when an admin opens them up via ktrader off.

Operators upgrading from v2.6.2

Drop the new ZIP in place and restart. No migration steps needed beyond letting the new 010_modpack.sql migration apply on first boot. If you were using the systemd timer + shell script stopgap for graceful restarts (set up earlier this cycle), disable it before deploying — KC's GracefulRestart feature now handles that natively and the two would fire at the same time otherwise.

🤖 Release notes generated with Claude Code

v2.6.2 — Vote-for-Rewards integration

30 Apr 17:15
ac74483

Choose a tag to compare

What's new since v2.6.1

A meaty release. The marquee feature is Vote-for-Rewards, plus four supporting changes from the same cycle: an in-game /help discoverability command, a panel-screenshot tour in the README, a troubleshooting entry covering the V 2.x EOS migration's downstream effects, and a bundle of UI polish from real-world testing.

🎯 Vote-for-Rewards integration

Self-hosted 7D2D admins routinely list their server on sites like 7daystodie-servers.com. Players who vote bump the listing's ranking, and in exchange the server hands them an in-game reward. KC already had every piece of the reward delivery (Points, VIP Gifts, audit log) — this release is the glue that pulls votes in and dispatches them.

v1 ships with one provider — 7daystodie-servers.com — but the abstraction is built pluggable from day one. New listing sites slot in as a single IVoteSiteProvider class. The shape costs nothing now and saves a real refactor when admins start asking for gtop100 and similar.

Two trigger paths share the same idempotent grant primitive:

  • Background sweep — a 60-second timer ticks; per-provider PollIntervalMinutes (default 5) gates the actual API calls. Fire-and-forget on a Task.Run so the timer thread never blocks on network I/O. Re-entrancy guarded via Interlocked.CompareExchange so a stalled API call past 60s doesn't pile up overlapping sweeps.
  • /vote chat command — for the "I voted from outside the game" case. Off the game thread, replies via pm once the round-trip completes. Cooldown gated.

Idempotency is insert-first. A vote_grants table (new in this release) is unique on (provider, steam_id, vote_date). Both the sweep and /vote insert the audit row before granting the reward; if the unique constraint fires (because another sweep beat us to it, or /vote raced the sweep), we skip without granting. Two sweeps running concurrently can't double-grant. A sweep racing a /vote can't double-grant. The listing site's "mark claimed" call is the third step, never the first.

Three reward types are wired:

  • Points — credits the player's existing wallet via IPointsRepository.AdjustPoints. Works.
  • VIP Gift — clones a pre-built template (admin creates a vip_gift row with player_id _template_ via the regular VIP Gifts admin tab; the clone happens at grant time, mirroring items + commands junctions). Voter claims with /vip on next login. Works.
  • CD Key — reserved with a clear NotImplementedException. The path is in place; the template wiring is the next iteration.

Failure mode handled: if dispatch throws (e.g. an admin typo'd the VIP-gift template name), the audit row is rolled back via IVoteGrantRepository.DeleteByKey so the next sweep can retry once the misconfiguration is fixed. Without this, a typo would silently lock out every voter on that provider — the HasGrantForDate short-circuit would skip every future attempt.

Player ID resolution went through a learning curve. 7D2D V 2.x identifies players via EOS even on Steam, so a player's CrossplatformId is EOS_<32hex> and that's what KC's tables key on. A naive Steam_<76digit> produces orphan rows that don't merge with the player's real account. The dispatch now resolves the actual player_id via LivePlayerManager.GetAllOnline() matched on PlatformId — common case (vote arrives while the player is online or recently was) lands on the correct row. Offline-at-grant-time fall-through is logged with a warning; tightening that path to skip-and-retry-when-online is queued.

Settings UI (admin-only Vote Rewards tab):

  • Master enable toggle + per-provider config (API key as password input, server ID, poll interval, reward type, broadcast template with {player} and {reward} tokens)
  • Recent grants audit table beside the config in a 2-col layout
  • Reward type Select switches the value field below it (points amount / VIP-gift template name / CD-key template id)
  • Self-heals from any duplicate-provider state poisoning (a Newtonsoft-append quirk that was caught and fixed mid-cycle)

(PRs #46, #47, #48, #49, #50, #51, #53, #54)

/help chat command

Players can now type /help, /commands, or /? in chat to see every enabled command on the server, grouped by feature (Home, Teleport, Points, Store, VIP, Tickets, Blood Moon, Vote Rewards). Only enabled groups appear — running /help on a server with the store turned off won't tell players to try /buy. The command is gated by the same prefix as the rest (default /, configurable). (PR #44)

Panel screenshots in the README

Eight click-to-zoom panels covering Server Control, Server Update, Config Editor, Mods Manager, Backups, Item Database, Discord Settings, Account Settings — so prospective admins can see what the panel looks like before pulling the mod. Click opens full-size in a new tab. (PRs #42, #43)

Server-browser N/A troubleshooting note

V 2.x moved server-browser discovery from Steam Query to EOS, and a subset of servers' EOS listings render as "N/A" ping with a blank description in the in-game browser even when the server is healthy. Direct-connect still works; it's a presentation-layer issue downstream of EOS, not a server-side bug. New docs/troubleshooting.md entry walks through the symptom-cause-fix and a verification grep on boot logs. (PR #45)

Installation

Server (KitsuneCommand)

Download KitsuneCommand-v2.6.2.zip, extract, drop KitsuneCommand/ into your server's Mods/ directory. Restart the dedicated server.

Vote Rewards setup (if you want to use the marquee feature)

  1. Get an API key from your server's management page on https://7daystodie-servers.com
  2. Open the panel → Settings → Vote Rewards
  3. Master toggle ON, enable the 7daystodie-servers.com provider, paste API key + your numeric server ID
  4. Pick a reward type (Points or VIP Gift) and configure
  5. Save. Vote at the listing site to verify — sweep grants within ~5 min, or /vote in-game claims instantly

Operators upgrading from v2.6.1

Drop-in replacement. Migration 009_vote_grants.sql runs on first boot to create the audit table — no-op idempotent if you've already booted a v2.6.2 build.

If you'd been testing pre-release builds (#46 → ~#49) and have polluted VoteRewards settings state with duplicate provider entries, you'll see a one-time line in the boot log: VoteRewards: deduped 1 duplicate provider entry/entries on load. That's normal; the dedup logic self-heals on first boot.

🤖 Release notes generated with Claude Code

v2.6.1 — Discord race fix, WebSocket host-header, SkiaSharp Linux, kcresetpw

26 Apr 14:53
6599483

Choose a tag to compare

What's new since v2.6.0

A patch-release rollup of seven prod-quality fixes and one new admin recovery command. If you were running v2.6.0 behind a Cloudflare Tunnel, on a Linux server, or with the Discord bot enabled — at least one of these is for you.

Discord "Server Online" notification now reliably fires

The notification was racing the bot's own gateway connection on every boot — GameStartDoneEvent fired ~1.3 seconds before the websocket actually finished its handshake, so SendEmbedAsync silently no-op'd. Two-flag tracking (_gameStarted + _botReady) emits exactly when both are true, idempotently across reconnects. (PR #40)

Live WebSocket Live badge actually flips green on prod

WebSocketSharp's server does strict Host-header validation: it rejects any request whose Host doesn't match the authority it was bound to. Cloudflared was forwarding the public hostname (panel.example.com) → 400 from origin → "Finished 0 kB" in the browser. Most of the surface area is documentation: docs/cloudflared-tunnel.example.yml ships a proven config (incl. the gotcha that httpHostHeader must be host:port form, not bare host), and docs/troubleshooting.md indexes this and six other prod-only failure modes in symptom → cause → fix shape. The WebSocket endpoint also moved from /ws to /kctunnel along the way; harmless but visible if you're poking at headers. (PRs #38, #39)

Map view renders on Linux

SkiaSharp's LibraryLoader does [DllImport("dl")], and 7D2D's bundled Mono config has no dllmap for the short dl name. KC now ships SkiaSharp.dll.config next to the DLL with <dllmap dll="dl" target="libdl.so.2" os="!windows" />, mirroring the existing System.Data.SQLite.dll.config. Bonus: build scripts now copy libSkiaSharp.so to x64/ (where SkiaSharp's GetLibraryPath actually looks) instead of linux-x64/. (PR #34)

kcresetpw console command — admin password recovery

New ConsoleCmdAbstract for the "I'm locked out of the panel" case. Run kcresetpw <username> <newpassword> from the 7D2D game console, telnet (nc localhost 8081), or KC's own Console view. Min 8 chars, admin-only (DefaultPermissionLevel = 0), hashes via the existing PasswordHasher and writes via IUserAccountRepository.UpdatePassword. The plaintext is not logged. (PR #37)

Settings → Account "Change Password" actually saves

Was calling a generic Update that didn't persist password_hash, so the new password appeared to save but never wrote. Fixed by calling IUserAccountRepository.UpdatePassword directly. (PR #36)

Login + sidebar polish

  • Logo above the KitsuneCommand brand mark in the sidebar (centered, 72×72)
  • Logo on the login page (110×110, above the title)
  • Password input now stretches the full form width
  • Language selector moved from absolute-top-right into the login footer (next to "MANAGEMENT")
  • Fox-head favicon (orange brand silhouette, SVG-first with PNG/ICO/apple-touch-icon fallbacks generated from the same source)

(PRs #33, #35)

Installation

Server (KitsuneCommand)

Download KitsuneCommand-v2.6.1.zip, extract, drop KitsuneCommand/ into your server's Mods/ directory.

Client (optional — KitsuneTraderUnlock)

Unchanged from v2.6.0 (same v1.0.0, bit-identical asset). Only needed for V2's locked-down client if you want to interact with trader-area blocks when an admin opens them up via ktrader off. No-ops on dedicated servers.

Operators upgrading from v2.6.0

Most of you can drop the new ZIP in place and restart. Two flags to check if you're behind a Cloudflare Tunnel:

  • Your tunnel ingress for the WS endpoint should target localhost:8002 (or whatever your WebSocketPort resolves to) with originRequest.httpHostHeader: "localhost:<port>". Bare localhost won't work — must be host:port form.
  • The /ws path is now /kctunnel. Update your tunnel rule (or the dashboard public hostname) to match. The frontend bundles the new path automatically.

Both gotchas are documented in docs/troubleshooting.md and the example tunnel config at docs/cloudflared-tunnel.example.yml.

🤖 Release notes generated with Claude Code

v2.6.0 — Smart Mod Upload + Config Editor Polish

23 Apr 14:45
028e0b5

Choose a tag to compare

What's new since v2.5.0

Smart mod upload

The Mods Manager now handles real-world zip shapes that previously broke metadata detection:

  • Walks the extracted tree recursively (case-insensitive) for ModInfo.xml — nested structures, wrapper folders, mod packs all detected correctly
  • Fixes Windows-zipped backslash paths — some Nexus packagers write ModName\ModInfo.xml instead of ModName/ModInfo.xml; we now normalize both separators so Linux extraction actually unfolds into a tree
  • Supports mod packs (multiple ModInfo.xml in one zip → multiple installs)
  • Strips Nexus-style -<modId>-<version>-<timestamp> suffixes from folder names
  • Auto-generates a minimal ModInfo.xml for zips that ship without one so 7D2D actually loads the mod
  • Success + warning toasts on upload surface exactly what happened

Config Editor polish

  • Rewritten help text for every field — warm, specific, vanilla-reference numbers ("100% is vanilla", "125% is a gentle boost")
  • Day/Night Cycle visual widget with slider + warm/cool split bar + compact day/night minute spinners
  • Group reshape: World / Player / Gameplay / Block Damage — Gameplay and Player split apart, Block Damage demoted to 4th
  • Humanized labels via formatFieldLabel fallback (camelCase → human)

Discord restart-required banner

First-time Discord bot setup silently needed a server restart to take effect. Now a yellow banner appears after save with an inline "Restart now" button.

Reverse-proxy URL compat

KC now works when served through HTTPS proxies (Cloudflare Tunnel, Caddy, nginx). Login fetch uses a relative path; WebSocket connects on same origin unless the page is at :8890 directly.

Dynamic version display

ModInfo.xml is now the single source of truth for the mod version. Read once at startup into ModEntry.ModVersion, exposed via /api/server/info, consumed by the sidebar badge and the Dashboard info row. Bumping the version = editing one line.

Dashboard layout fix

The KitsuneCommand row is back on the Dashboard info card (middle slot, not last), so Local IP and Public IP align on the final row again.

Installation

Server (KitsuneCommand)

Download KitsuneCommand-v2.6.0.zip, extract, drop KitsuneCommand/ into your server's Mods/ directory.

Client (optional — KitsuneTraderUnlock)

Download KitsuneTraderUnlock-v1.0.0.zip, extract, drop into your client's Mods/ directory. Only needed for V2's locked-down client if you want to interact with trader-area blocks when an admin opens them up. No-ops on dedicated servers.

🤖 Release notes generated with Claude Code