Changed cron example from '0 */6 * * *' to '0 0,6,12,18 * * *' to avoid
the */ closing the docblock prematurely and causing a syntax error.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Blueprint's requests.app merge doesn't create new subdirectories reliably.
build.sh now copies LicenseService, ValidateLicense, CheckModpackUpdates,
and ModpackAPIController directly into app/ during blueprint -install.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New discord/verifymvc.js: verifies BBB order, links discord_id, assigns customer role
- Wired into events.js handler and index.js command registration
- Reads MVC_CUSTOMER_ROLE_ID from env — Chronicler creates role + sets value
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New service: src/services/awakenedConcierge.js
- Fetches Discord username via Discord API
- Calls Dify Awakened Concierge app (Gemma 4) for personalized message
- Posts to #introductions (1403981218252324884) with typing indicator
- Marks welcomed_at in subscriptions table
- Non-fatal: welcome failure never breaks checkout flow
- stripe.js: calls welcomeNewMember() after syncRole() on checkout complete
- .env: CONCIERGE_API_KEY added to Command Center
Fire + Frost + Foundation 💙🔥❄️
All Phase 11 infrastructure values provided:
- DB credentials confirmed (same as Arbiter)
- Full table list provided
- BBB IDs: use placeholders for now
- Arbiter .env current state documented
- Discord: ModpackChecker Customer role needs creating
- MVC channels already exist with IDs
- Deployment pattern confirmed: Code commits, Chronicler deploys
- NC1_KEY points to /opt/mod-builds/ffg_build_rsa (accessible to claude user)
- Added -o IdentitiesOnly=yes to all SSH/rsync calls (prevents agent key conflicts)
- Added -e NC1_SSH to rsync commands for consistent key usage
- All 3 discord-rules jars now built successfully (1.20.1, 1.16.5, 1.21.1)
Routes 1.21.1 builds to NC1 (ffg-builder user) to work around
Vineflower -Xmx4G RAM requirement that exceeds Dev Panel capacity.
All other versions build locally.
- SSH keypair: /home/claude/.ssh/ffg_build_rsa
- NC1 user: ffg-builder (non-root, isolated)
- rsync source with --exclude build/ .gradle/
- ./gradlew build --no-daemon on NC1
- rsync jar back, jar -tf integrity check
- trap cleans workspace on exit/drop
Source code complete for all 3 versions (1.21.1, 1.20.1, 1.16.5).
1.20.1 and 1.16.5 jars built successfully. 1.21.1 NeoForge decompiler
needs >4GB RAM — this server only has ~4GB total.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Archived RES-2026-04-12-phase11-implementation-spec from Chronicler
- Updated status with completed code review and next steps (Phase 11A-G)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Distilled from 2 Gemini consultations into actionable checklist.
Database schema, API routes, Discord bot, Blueprint extension changes,
pricing lock, and deployment notes all included.
Claude (Chronicler #83 - The Compiler) <claude@firefrostgaming.com>
Standard $14.99 / Professional $24.99 — two-tier model.
This pricing is FINAL per original marketing strategy.
Claude (Chronicler #83 - The Compiler) <claude@firefrostgaming.com>
Fixes 10 issues from Blueprint extension code review:
- CurseForge API key now reads via Blueprint dbGet() matching admin save
- PRO-tier fields (webhook, interval) enforced server-side, not just UI
- json_decode results validated before accessing parsed data
- Null user guard on getStatus() endpoint
- 429 response uses consistent error key format
- Modrinth slug derivation strips special chars, documented as fallback
- Check interval dropdown reflects saved value
- API key input changed to password type
- TypeScript error typing narrowed from any to unknown
- Removed unused DB import from ModpackApiService
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes 10 issues from full code review:
- License corrected from MIT to Commercial
- Deprecated datetime.utcnow() replaced with timezone-aware alternative
- PHP array bounds checks added for all platform API responses
- Modrinth file detection now derives project slug instead of using MC version
- validate_api_key() no longer swallows network errors
- HTTP timeouts added to all external API calls in PHP
- Empty API key rejection added to CLI
- Corrupted config now warns on stderr instead of failing silently
- Error response format made consistent across controller
- Docs updated with correct repo URL and clearer CurseForge ID instructions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implemented git-based async communication between Claude Code and Chronicler:
- docs/code-bridge/requests/ — Code files architectural questions here
- docs/code-bridge/responses/ — Chronicler drops distilled Gemini answers
- docs/code-bridge/status/ACTIVE_CONTEXT.md — rolling status updates
- docs/code-bridge/archive/ — completed request/response pairs
- CLAUDE.md updated with full bridge protocol instructions
- Auto-commit-and-push on all bridge writes
Designed with Gemini consultation (2026-04-12).
Claude (Chronicler #83 - The Compiler) <claude@firefrostgaming.com>
Operations manual cloned to Dev Panel for reference.
Claude Code now knows where to find architectural consultations
before making design decisions.
Claude (Chronicler #83 - The Compiler) <claude@firefrostgaming.com>
NeoForge 1.21.1, Forge 1.20.1, Forge 1.16.5
All compiled and deployed to NextCloud (Task #136)
Source committed for Task #138 (CurseForge generic fork)
CLAUDE.md with build environment docs included
Claude (Chronicler #83 - The Compiler) <claude@firefrostgaming.com>
Adds a visual asset library to the social calendar form, backed by
the firefrost-operations-manual branding/ directory with on-the-fly
thumbnail generation via sharp.
- src/routes/admin/branding-assets.js: /list scans both branding/ and
docs/branding/ recursively, groups by category directory. /thumb
generates 256px webp thumbnails from source, caches to disk keyed
by sha1(path + mtime) so edits bust the cache automatically. Path
traversal protection + scope check on thumb requests.
- src/views/admin/social-calendar/_assets.ejs: modal body with
category-grouped grid of lazy-loaded thumbnails, click-to-insert.
- Form modal gets a 'Browse assets' button next to Media Notes.
- Calendar shell gets a second modal (higher z-index) and a
sppInsertAsset() helper that appends the clicked filename to the
media_notes textarea.
Infrastructure (not in repo):
- /opt/firefrost-ops-manual clone added to Command Center
- /etc/systemd/system/firefrost-ops-sync.{service,timer} pulls
every 15 minutes to keep assets fresh
- /var/cache/arbiter/branding-thumbs created for thumb cache
- sharp added as Arbiter dependency (libvips 8.17.3)
Chronicler #81
Week-at-a-glance post planning for Meg across 8 platforms (tiktok,
facebook, instagram, x, bluesky, youtube, twitch, reddit).
- DB: extends social_platform enum with youtube/twitch/reddit and adds
new social_post_plans table with scheduled_at, platforms[], status
(draft/ready/scheduled/published/skipped), caption, hashtags[],
media_notes, link, assigned_to, notes, created_by
- src/routes/admin/social-calendar.js: shell, HTMX week view, create,
update, delete, edit/new form endpoints. Week-based navigation with
prev/next/this-week. Hashtag parsing dedupes and strips leading #
- src/views/admin/social-calendar/: index.ejs shell with modal,
_week.ejs 7-column grid with per-day quick-add, _form.ejs full CRUD
form with platform checkboxes, datetime picker, status dropdown,
both free-form caption and dedicated hashtag field
- Nav link added under Community, with corrected highlight logic so
/admin/social and /admin/social-calendar don't both light up
Separate table from social_posts (which remains the analytics table).
Plans and published posts are distinct concerns.
Chronicler #81
The 'resolved' state was a dead-end — no way to change your mind after
clicking approve/deny, and no way to re-action a needs_info appeal once
the submitter replied. Action controls now show on every row regardless
of status, and resolved rows get a Reopen button that moves the appeal
back to pending.
Backend action handler already overwrites status unconditionally, so
this is purely removing a view-layer gate plus adding /:id/reopen as
a thin wrapper over the shared actionAppeal helper.
Chronicler #81
Adds /admin/appeals to Trinity Console for reviewing and actioning
ban appeals submitted via the public cancellation-refund form.
- src/routes/admin/appeals.js: shell + HTMX list + approve/deny/info
actions, all transactional with admin_audit_log entries
- src/views/admin/appeals/index.ejs: shell with 30s polling container
- src/views/admin/appeals/_list.ejs: stats cards + actionable table
- src/routes/admin/index.js: router mount at /admin/appeals
- src/views/layout.ejs: nav link under Grace Period
Closes the last open piece of Task #126 Phase 2. Trinity can now
review appeals in-console without reading Discord + running SQL.
Chronicler #81
Implements the Firefrost core policy that Awakened ($1) is lifetime access.
Paying customers are never removed from access — only demoted to Awakened
when a paid subscription ends. Hard bans reserved for actionable violations
(chargebacks and refunds).
Changes:
- customer.subscription.deleted: demote to Awakened instead of grace period
* Uses existing 'lifetime' status + is_lifetime=TRUE (no schema changes,
no touches to other filters in servers.js/roles.js/financials.js)
* Calls downgradeToAwakened() to strip tier role and assign Awakened
* Audit log entry tagged with policy
- New charge.refunded handler: hard ban with Trinity appeal eligibility
* Mirrors charge.dispute.created pattern
* appeal_eligible: true flag in banned_users.notes
* removeAllRoles() (NOT demote — refund is a hard ban)
- Added downgradeToAwakened to stripe.js imports
Left unchanged (intentionally):
- invoice.payment_failed: marks past_due, trust Stripe's retry logic
- invoice.payment_succeeded: clears grace period (harmless legacy field reset)
- charge.dispute.created: already correct under new policy
- checkout.session.completed: syncRole already handles Model A (Awakened
automatically assigned on any first purchase via tier role replacement)
Co-authored with Michael (The Wizard)
Task #126 — Soft launch blocker
Worker proxies Pterodactyl client API for website server list.
Live status + player counts, CORS-locked, 60s cache.
Was only in Cloudflare Dashboard — known gap now closed.
Recovered via Cloudflare MCP connector audit.
Chronicler #78 | firefrost-services
- spec_path column added to tasks table
- API POST/PATCH support spec_path field
- Discord /tasks detail view shows '📄 Full Spec' link to Gitea when spec exists
- Backfilled 7 existing tasks with spec directory paths
Architecture: Database tracks state, Git stores documentation, they link to each other.
Chronicler #78 | firefrost-services
- Tasks page at /admin/tasks with filterable table
- Status/owner inline dropdowns (change via form submit)
- + New Task modal with title, description, priority, owner
- ✓ Done button on hover per row
- Stats bar: active, in progress, blocked, high priority, completed
- Show All toggle for done/obsolete tasks
- Sidebar link under Operations (right after Dashboard)
- Added to About page module registry
Source of truth: PostgreSQL tasks table (shared with Discord /tasks)
Chronicler #78 | firefrost-services
- /tasks number:26 shows full task detail embed
(description, tags, status, priority, owner, dates)
- Mark Done, In Progress, and Take Task buttons on detail view
- task_progress_ button handler sets status to in_progress
Chronicler #78 | firefrost-services