Address review feedback on #744:
- clamp DATABASE_POOL_MAX to >= 1 and DATABASE_POOL_MIN to <= max so bad
env values can not produce a pool that fails every acquire
- healthcheck redis probe checks client status before PING so commands are
not queued indefinitely while redis is down (maxRetriesPerRequest is null)
- probe() clears its timeout timer once the check settles
- error.html shows only the status code, not the error message
- docs: correct SQLite default to kener.sqlite.db to match knexfile
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address review feedback on #742:
- GetSiteURL now only returns absolute http(s) origins, falling back to the
ORIGIN env var when the stored siteURL is unset or scheme-less, so the v4
url fields honor the OpenAPI format: uri contract
- event status casts now use the response interface types
(MaintenanceEventResponse["status"], MaintenanceEventDetailResponse["event_status"])
so READY is included and the casts can not drift from the API contract
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The public /maintenances/<id> route is keyed by maintenance EVENT id by
default, while /api/v4/maintenances returns maintenance ids. Consumers
that concatenated API ids onto the public path landed on the wrong page
(#723) — an apparent off-by-one title mismatch with no actual data
corruption.
Instead of flipping the route default (which would break every internal
link, subscriber email, and bookmarked URL), v4 API responses now carry
an absolute `url` field built from the configured Site URL:
- Maintenance responses link via /maintenances/<id>?type=maintenance
- Maintenance event responses link via /maintenances/<event_id>
- Incident responses link via /incidents/<id> (parity)
Also updates the OpenAPI spec, records the decision in
docs/adr/0002, and pins Maintenance vs Maintenance Event in CONTEXT.md.
Fixes#723
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sticky theme-plus bar has no background, and the gap between it and the
fixed nav stays transparent, so page content shows through both sections as
it scrolls. Paint one blurred layer behind both (above content, below nav
z-10 and bar z-20).
- Modify alertToVariables function to accept an optional monitorTag parameter for better flexibility in alert naming.
- Update sendAlertNotifications function to pass monitorTag to alertToVariables.
- Enhance addWorker function to handle multiple monitors by querying and creating alerts with the appropriate monitorTag.
- Introduce a new junction table for many-to-many relationships between monitor alerts and monitors.
- Update database migration to accommodate the new schema and backfill existing data.
- Revise frontend components to support multiple monitor selections in alert configurations.
- Enhance documentation to include analytics provider setup instructions.
implements #675
feat(dashboard): Refactor notification payload to include event start and end times
refactor(controllers): Modify group call scoring model to use normalized scores for status
docs(changelog): Document changes in group monitor scoring model in v4.0.14 release notes
feat(badges): Implement locale selection for status badges in management interface
refactor(i18n): Create server-side i18n helper for translating strings outside of Svelte components
- Revise site configuration documentation to clarify page visibility behavior.
- Introduce global page visibility settings with detailed descriptions and functionality.
- Modify API server to dynamically select the correct specification path based on environment.
- Streamline event fetching logic in event pages to improve performance and maintainability.
- Remove unused vault secret management code from the manage API.
- Enhance customizations page to support global page visibility settings.
- Create new guides for adding custom fonts and custom JavaScript/CSS.
- Implement server-side logic for handling events by month with improved date validation.
feat(links): Correct URL formatting in invitation and verification email links
refactor(notification): Simplify notification utility imports
chore(docs): Update Discord link and API reference URLs in documentation
style(buttons): Change button variant for better UI consistency
chore(scripts): Implement script to sort translation keys in locale files
- Implement multi-stage Dockerfile for building and running the Kener application with support for Alpine and Debian variants.
- Establish development and production Docker Compose files for local testing and deployment.
- Configure Redis service for caching and job scheduling.
- Set up environment variables for application configuration, including secret keys and database connections.
- Define health checks for Redis service to ensure reliability.
Add comprehensive skill file for creating and editing high-quality Kener documentation with guidelines for:
- Documentation structure and organization
- Custom heading anchors for deep linking
- Markdown features and formatting
- Quality guidelines and best practices
- Avoiding content duplication with references
- Complete workflow and checklist
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: rajnandan1 <16224367+rajnandan1@users.noreply.github.com>
refactor: Modify alertToVariables function to streamline alert data handling
refactor: Change alert_id and alert_incident_id types in types.ts for consistency
refactor: Simplify alert notification templates to include incident URL and update variable types
docs: Update alerting templates documentation to reflect new variables and structure
refactor: Enhance webhook alert template to accommodate new incident URL handling
refactor: Adjust server API to utilize the latest monitor tag for testing alerts
- Create UserRecordDashboard interface to include password status.
- Update password reset API to validate password strength and set user as verified.
- Refactor invitation handling in the manage route to streamline user management.
- Introduce invitation acceptance flow with password creation and validation.
- Create email template for user invitations.
- Implement invitation verification logic to ensure token validity and user existence.
- Enhance user management UI to support invitation resending and account activation.
The API reference page was blank because spec.content expected a parsed
JSON object but received a JSON string. Added JSON.parse() to properly
parse the OpenAPI spec before passing it to @scalar/express-api-reference.
Fixes#532
- Set Russian as default locale in seedSiteData and i18n server configuration
- Replace MMMM with LLLL format for proper Russian month declension (nominative case)
- Update all date format strings across incident pages and controllers
- Add date range validation (2023 to current+12 months) with 404 error handling
- Implement conditional navigation buttons that hide at date boundaries
- Fix Russian month names display (октябрь instead of октября in titles)
- Add MIN_YEAR constant to prevent navigation before system launch
- Improve TypeScript compatibility and fix linter errors
This resolves Russian grammatical issues and prevents access to invalid date ranges
while maintaining proper localization and user experience.
Refactors the subscription status toggle to use a dedicated handler, ensuring correct behavior when toggling status for both new and existing triggers. Enhances user experience by automatically saving and reloading data when needed.
Adds support for toggling the subscription trigger's active/inactive status from the UI and updates backend/controller logic to allow dynamic status changes. Restricts editing of subscription event configuration and user subscriptions to when the trigger is active, improving admin control and preventing unintended changes when subscriptions are inactive.
Returns structured error messages from notification providers, updates API logic to surface errors to users, and enhances UI to display error or success states during trigger tests. Also adjusts monitor API request payload and corrects data range calculation.
Relates to improved reliability and user experience.
Introduces per-device configuration for maximum data range and selectable days on the homepage, allowing separate settings for desktop and mobile. Updates UI, server logic, and data model to support these options, and uses user agent detection to apply the correct configuration. Improves flexibility for data display across devices.
Relates to #105
Introduces a 'tz' parameter for specifying timezone in embed scripts and iframe URLs, improving localization for embedded monitors.
Updates documentation and refactors parameter handling to prioritize explicit timezone over browser-detected values.
Relates to improved internationalization and user experience.
Enhances subscription menu by showing a default icon when no image is available.
Replaces hardcoded verification code in email template with a variable.
Prevents background scrolling when subscription menu is open.
Removes redundant new subscriber response in API.
Removes unused documentation button from subscriptions page.
Adjusts checkbox styling for better UI consistency.
Introduces a unified function for monitoring data insertion,
adds support for group monitor status updates, and ensures
consistency by routing all monitoring data writes through the
new logic. Removes duplicated validation and streamlines
group monitor handling for improved reliability.
Relates to #123
Introduces an option to enable a site status banner summarizing the operational state of monitored systems. Calculates and displays aggregated status with a progress bar. Localizes banner messages in all supported languages.
Helps users quickly assess overall system health from the homepage.
- Updated monitorSheet.svelte to use raw JSON data for AllGamesList, replacing the previous import method.
- Refactored game retrieval logic in monitorSheet.svelte to utilize GetGameFromId function with the new AllGamesList structure.
- Modified monitorsAdd.svelte to parse AllGamesList from raw JSON, ensuring consistent data usage across components.
- Enhanced event pages (+page.svelte) with appropriate meta tags for better SEO, including titles and descriptions.
- Added a new all-games-list.json file containing comprehensive game data for improved functionality and maintainability.
- Updated subscriptions page to clarify SMTP setup instructions for users.
Integrates translation function into subscription and maintenance components,
localizing all user-facing strings. Expands translation resources for multiple languages
to cover newly localized phrases, improving accessibility and user experience for non-English users.
Unifies URLs for incident events by switching from singular to plural path segments and removes obsolete routes to streamline navigation. Introduces paginated incident fetching with filtering and sorting to improve performance and scalability. Updates UI components to reflect new paths and behaviors.
Relates to improved incident management and navigation.
Introduces a copy-to-clipboard button for incident links with animated feedback. Adds ability to hide accordion chevrons and refactors chevron and copy button styles. Updates incident URL generation and allows querying monitors by tag.
Relates to improved UX for incident management.
Introduces a queue-based system to send email notifications for incident events, including creation, updates, monitor changes, and comments. Fetches eligible subscribers dynamically and uses configurable templates for email delivery. Also updates the UI to improve the Preview button styling for better user experience.
- Implemented GenerateTokenWithExpiry function for JWT token generation with a specified expiry time.
- Added GetSubscriberByID function to retrieve subscriber details by ID.
- Updated CreateNewSubscription to accept subscriber ID directly.
- Introduced GetSubscribersPaginated for paginated retrieval of subscribers.
- Enhanced subscription trigger management with CreateSubscriptionTrigger and GetSubscriptionTriggerByEmail functions.
- Updated email_code.html to change expiration notice from 1 day to 5 minutes.
- Modified layout.server.js to include canSendEmail check for conditional rendering.
- Updated subscription page to display subscribers with pagination and subscription status management.
- Refactored subscription-related API endpoints for improved clarity and functionality.
- Added server-side logic for managing subscription triggers and email settings.
- Added functionality to create, update, and delete subscribers and their subscriptions in the controller and database implementation.
- Introduced new API endpoints for subscribing and unsubscribing users, including email verification and management of subscription preferences.
- Created a new Svelte component for managing subscriptions, allowing users to subscribe to updates and manage their preferences.
- Added email template for sending verification codes to subscribers.
- Implemented utility functions for generating random numbers and validating email addresses.
Sometimes, it's useful to modify data in the monitoring_data table, for example, in case of a false positive.
I'm adding a new button in the monitor list that opens a popup
allowing users to change the status of monitor data for a given time
range.
feat: modify analytics event naming convention for consistency
fix: adjust delete monitor confirmation form layout for better UX
style: refine positioning of monitor action buttons for improved alignment
refactor: streamline password validation imports in setup and forgot password routes
fix: remove unnecessary line breaks in layout server file
Implements badges for monitors, including status, uptime, and liveness, along with a dedicated management page.
Adds embed options for various platforms with customizable styles.
Implements role-based access control with admin, editor, and member roles.
Introduces a user management system with profiles, activation/deactivation, and password reset.
Adds an email authentication system with verification and password reset via email.
Includes an invitation system with token-based invitations and admin controls.
Improves performance, security, UI, and developer experience.
Updates package version to 3.2.5 and adds vite-plugin-package-version.
Removes libcap related code from Dockerfile.
Improves the incident display by adding time status information
such as "Starts in", "Started", and "Will last for".
Also fixes database directory write permissions on startup.
Also fixes#337
- Makes the monitor tags wrap on smaller screens.
- Validates webhook body.
- Adds user agent to webhook.
- Fixes Discord logo URL construction.
Issue #336
Implements the SQL monitor feature, allowing users to monitor database connections and queries.
Adds UI elements for configuring SQL monitor parameters, including connection string, query, and timeout.
Validates user inputs for SQL monitor configuration.
Improves date formatting by adding timezone support using `date-fns-tz`.
Allows users to switch between different timezones via a new UI toggle in the settings.
Updates dependencies and integrates timezone functionality into date formatting functions.
Refactors the monitor component for better data display and user interaction, including improved uptime calculations and a dropdown for selecting time ranges.
Enhances incident creation and handling by adding incident sources and refining incident filtering.
Addresses UI responsiveness on smaller screens.
Improves incident management by filtering out existing auto incidents when creating manual incidents.
Enhances cron job scheduling by removing and adding jobs dynamically based on active monitors and prevents duplicated incidents.
Also, ensures jobs get triggered in the correct order.
Simplified Docker tagging - in turn fixes broken `alpine` tag to correctly point to latest stable Alpine release. Changes include:
- No more type=ref,event=branch – because this workflow is only for releases & manual triggers on `main` branch.
- Ensures `alpine` tag is always created for Alpine variant builds.
- Ensures `latest` tag is always created for Debian builds.
- Ensures all semver-based tags work correctly for both variants.
Improves incident display and management by introducing configurable incident group views and enhancing comment rendering to support HTML content.
Solves the bug raised in #295 where server crashes when an incident is created from an alert
Refines SMTP email settings by adding TLS configuration and allowing username/password to be optional. #300 and #298
Also, fixes a bug where only home page was being filtered. Now all pages are filtered. #297
Adds job to check if any Dependabot PRs are open and if so, fail the Docker build (since we need to ensure OS packages exist and are in their correct versions when using pinned versions for security purposes).
Noticed multiple individuals commenting about insecure/privacy-unfriendly Lato webfont library being served via Google Fonts. I had formerly suggested replacing this with BunnyFonts and was happy to see that added as a placeholder, however, I also understand someone’s comment about this being loaded from an external resource.
This brings that webfont local. Size of webfont files should minimally grow Docker image sizes and I think we should prioritize UI and privacy by including it locally. The font’s licensing is OFL, so we are allowed to package it for distribution with this project.
I’m including both the full font family (for archival purposes) and Latin subset of this font. The Latin variant is used in the Docker image build (since this will apply to the majority of users and keep the Docker image smaller). If users need to extend this with their own subsets, they can always load those as a custom font. :)
Adds a NO_DATA status to handle cases where monitor data is unavailable.
Refactors data interpolation and aggregation logic for better accuracy and clarity.
Updates documentation links.
fixes#288
Updates the Kener version from 3.1.2 to 3.1.3.
Refactors the group query to use `havingRaw` for better compatibility across different database systems.
Adds database information to the bug report template.
Updates documentation to reflect the new directory structure.
The documentation now correctly references images in the `/documentation` directory.
Removes the `src/static/documentation` directory in the Dockerfile.
Not sure if we are wanting Dependabot to track Node.js packages, so for the time being, commenting this block out, but leaving for now w/ “TODO” to come back to at a later point.
Changed from trying to use artifacts and the GHA workflow failing to now using a simple `BUILD_VERSION` repository variable and automatically updating that when the `build-and-push-to-registries` workflow succeeds.
Other changes include:
* Added `workflow_run` trigger to `generate-readme.yml` so when that workflow recognizes the “Publish Docker Image to Registries” workflow runs and succeeds, it will automatically run the `generate-readme.yml` workflow (since a new Docker release will require Docker image variants table in README.md to have versioning updated)
* Generate major and major-minor versions from the `BUILD_VERSION` repository variable (more efficient than storing three separate variables from the `build-and-push-to-registries` workflow job)
Changes include:
* Moving README generation to separate workflow (so that it can be trigger to run when any changes to `README.template.md` are pushed to `main` branch or a PR is opened with changes to template file
* GitHub Actions do not have privileges via `GITHUB_TOKEN` to commit to protected branches, thus, we need to take another approach and utilize a personal access token (which you’ll need to generate @rajnandan1) and add to the repository secrets (to avoid exposing that credential).
* Changes `publish-images` workflow to run now only when a new GitHub Release is created. (This will help prevent excessive workflow runs on merges into `main`)…in other words, @rajnandan1, you can merge freely into `main` now without excessive GitHub Actions usage.
The following changes have been made:
* Ensured `package-lock.json` is up-to-date with latest dependencies from `package.json` - moved check to new workflow job and set as dependency for ‘build-and-push-to-registries’ job
* Updated branch-tagging for non-main branches (used when building Docker images)
* Restored pinned OS package versions in Dockerfile (for best-security)
* Restored “TODO” comments to Dockerfile (for tracking purposes and because I will revisit those items later this week)
* Added `—no-fund` tag to suppress npm package funding messages (helpful for CI/CD)
* Changed from `wget` to `curl` to resolve Debian package versioning issue between differing architectures (was one of the reasons causing the build to fail)
* As a part of the last comment, needed to then conditionalize container healthcheck logic
* Checked in newest `package-lock.json` file
* Fixed broken Docker badges in `README.template.md`
🔄 Automate README Generation via Mustache Templating
- Use Mustache to dynamically generate `README.md` from `README.template.md`.
- Populate README with environment variables (e.g., `KENER_BUILD_FULL_VERSION`).
- Prevent direct edits to `README.md` by enforcing updates via the template.
- Enhance GitHub Actions workflow to auto-generate and commit the README.
- Add GitHub Action workflow (`protect-readme.yml`) to prevent others from direct updates to `README.md` via PR.
I caught an issue where the README will only auto-update listed Docker versions the first time. Commenting out for now (in case this PR gets merged before I have time to fix this). Will revisit this and fix this week.
Integrating Dependabot into the workflow ensures automatic dependency updates, improving security, reducing technical debt, and keeping packages up to date with minimal manual effort. This helps prevent vulnerabilities and maintain code stability over time.
Dependabot will automatically monitor the project’s dependencies and open pull requests (PRs) to update them when new versions are released. Here’s how it works:
1. Scans for Outdated Dependencies – It checks project dependency files (e.g., package.json, Dockerfile, .env.build, etc.) for outdated versions.
2. Fetches Latest Versions – When a newer version of a dependency is available, Dependabot retrieves it and updates the dependency files accordingly.
3. Opens a Pull Request – It then creates a PR with the updated dependency, detailing the changes and linking to release notes, changelogs, or security advisories.
4. Runs CI/CD Tests – If we end up setting up continuous integration (CI) tests, the PR will trigger the tests to check for breaking changes.
5. Security Updates – Dependabot also detects vulnerable dependencies and creates PRs to update them to a secure version.
6. Auto-Merging (Optional) – We might consider this at a later point, but if configured, it can automatically merge PRs when updates pass all tests and meet the requirements.
Noticed when doing some cleanup, that you had two awesome tags, but they both point to different URLs/repos. I added back in the one I had inadvertently removed.
* Expanded upon existing Docker README section.
* Created table which will contains version placeholder variables that will be replaced by new GitHub workflow job: “update_readme”. Job automatically runs after new images have been built & pushed to container registries.
Default documentation link in main nav won’t work because /docs are not included in built Docker images (to keep image smaller). Instead, changing seed data to point to the docs homepage. :)
Switching from Google Fonts to Bunny Fonts CDN. Bunny Fonts is an open-source, privacy-first web font platform. It is fully GDPR compliant (Google is not) and can act as a drop-in replacement for Google Fonts.
Streamlined the GitHub `publishImage.yml` workflow with the following functionality:
* Handle both Alpine and Debian variants through matrix strategy
* Push to both Docker Hub and GitHub Container Registry
* Add comprehensive tagging strategy, handling both branches (aka release version, e.g. 1.0.0), semantic versions (major.minor and major), and latest versions (`latest` and `alpine`)
* Add security aspects (cosign signing, proper permissions)
* Add better caching and multi-platform build settings
With this revised workflow, the following Docker image variants will be built for every successful release. As an example, if the release version is “3.0.9”, then the following Docker image variants will be built:
Debian variants (default):
- `kener:3.0.9` (Semver of current release)
- `kener:latest` (Latest Debian release, ’latest’ label points to 3.0.9)
- `kener:3.0` (major.minor version, major.minor ‘3.0’ label points to 3.0.9)
- `kener:3` (major version, major ‘3’ label points to 3.0.9)
Alpine variants (smallest filesize):
- `kener:3.0.9-alpine` (Semver of current release)
- `kener:alpine` (Latest Alpine release, ‘alpine’ label points to 3.0.9)
- `kener:3.0-alpine` (major.minor version, major.minor ‘3.0-alpine’ label points to 3.0.9)
- `kener:3-alpine` (major version, major ‘3-alpine’ label points to 3.0.9)
* Switch to multi-stage build pattern for smaller image size
* Add support for both Alpine and Debian variants via build args
* Change default image base to `node:23-slim` instead of using `node:23` (no need for full Debian base present in `node:23` since now prioritization is given to production-ready builds)
* Improve caching with --mount for npm dependencies
* Separate build and runtime dependencies
* Remove unnecessary Node.js packages in final stage
* Fix permissions on uploads/database directories
* Add proper scoping for build arguments
* Set NODE_ENV=production for better performance
This change reduces the final image size and improves build caching while adding flexibility to choose between Alpine and Debian base images.
Original: ~1.2GB
New Alpine: ~350MB
New Debian: ~450MB
When building for production, various warnings are output which slows down production build.
The following changes were made:
- Suppress unused export properties (unused-export-let).
- Suppress conflicting Svelte resolve warnings (conflicting-svelte-resolve).
- Suppress empty chunk warnings (empty-chunk).
- Suppress unused module imports (module-unused-import).
- Keep other important warnings visible, so we’re still aware of potential issues.
Now, production build should be cleaner and faster! 🚀
- Added Russian language support by creating ru.json with translations for various terms and phrases.
- Updated locales.json to include Russian in the list of available languages.
Use the right shadcn-svelte components when building UI in SvelteKit projects. This skill detects your project setup, shows what's available, and gives you access to full component documentation.
## Prerequisites
The project must be a SvelteKit app with shadcn-svelte initialized:
```bash
# Initialize shadcn-svelte in an existing SvelteKit project
npx shadcn-svelte@latest init
```
## How to use
### Step 1: Detect project setup
Run the detection script to verify this is a SvelteKit project with shadcn-svelte and see which components are already installed:
```bash
bash <skill-path>/scripts/detect.sh .
```
This will:
- Confirm it's a SvelteKit project (checks for `svelte.config.js/ts` and `@sveltejs/kit` in package.json)
- Confirm shadcn-svelte is installed (checks for `components.json`, `bits-ui`, or `shadcn-svelte` in package.json)
- List all currently installed components in the project's UI directory
- Provide the documentation URL
If the script exits with code 1, the project either isn't SvelteKit or doesn't have shadcn-svelte — do not proceed with shadcn-svelte components in that case.
### Step 2: Read the component documentation
The full component documentation for LLMs is available at:
```
https://www.shadcn-svelte.com/llms.txt
```
Fetch this URL to get a structured index of all available components organized by category, with links to individual component documentation pages (in `.md` format).
When you need to use a specific component, read its individual documentation page from the links provided in `llms.txt`. Each component doc includes:
- Import statements and usage examples
- Available props, events, and slots
- Variants and configuration options
- Accessibility information
### Step 3: Use the right component for the job
When building UI, follow this decision process:
1.**Run detection** to confirm shadcn-svelte is available and see installed components
2.**Fetch llms.txt** to see all available components
3.**Read the specific component docs** for the components you plan to use
4.**Check if the component is installed** — if not, add it:
```bash
npx shadcn-svelte@latest add <component-name>
```
5. **Import and use the component** following the documentation patterns
### Component categories
shadcn-svelte components are organized into these categories:
| **Form & Input** | Button, Calendar, Checkbox, Combobox, Date Picker, Input, Input OTP, Label, Radio Group, Range Calendar, Select, Slider, Switch, Textarea, Toggle, Toggle Group |
Components are typically imported from the project's `$lib/components/ui` directory:
```svelte
<script lang="ts">
import { Button } from "$lib/components/ui/button";
import * as Card from "$lib/components/ui/card";
import * as Dialog from "$lib/components/ui/dialog";
</script>
```
Some components use namespace imports (with `* as`) when they have multiple sub-components (Card, Dialog, Sheet, Table, etc.), while simpler components use named imports (Button, Input, Badge, etc.).
## Important guidelines
- **Always run detection first** before suggesting shadcn-svelte components
- **Always read component docs** before using a component — don't guess at props or patterns
- **Check installed components** and add missing ones before importing
- **Use the project's configured path** — the components directory may vary based on `components.json` configuration
if grep -q '"@sveltejs/kit"'"$PROJECT_DIR/package.json" 2>/dev/null;then
SVELTEKIT=true
fi
fi
if[["$SVELTEKIT" !="true"]];then
echo"NOT_SVELTEKIT"
echo"This is not a SvelteKit project. No svelte.config.js/ts found and @sveltejs/kit is not in package.json."
exit1
fi
# --- Step 2: Check for shadcn-svelte ---
SHADCN=false
# Check for components.json (shadcn-svelte config file)
if[[ -f "$PROJECT_DIR/components.json"]];then
# Verify it's actually a shadcn config (has $schema or style field)
if grep -qE '"(\$schema|style)"'"$PROJECT_DIR/components.json" 2>/dev/null;then
SHADCN=true
fi
fi
# Check for bits-ui in package.json (core dependency of shadcn-svelte)
if[[ -f "$PROJECT_DIR/package.json"]];then
if grep -q '"bits-ui"'"$PROJECT_DIR/package.json" 2>/dev/null;then
SHADCN=true
fi
fi
# Check for shadcn-svelte in package.json
if[[ -f "$PROJECT_DIR/package.json"]];then
if grep -q '"shadcn-svelte"'"$PROJECT_DIR/package.json" 2>/dev/null;then
SHADCN=true
fi
fi
if[["$SHADCN" !="true"]];then
echo"NO_SHADCN_SVELTE"
echo"SvelteKit project detected, but shadcn-svelte is not installed."
echo"Install it with: npx shadcn-svelte@latest init"
exit1
fi
# --- Step 3: Gather installed components ---
echo"DETECTED"
echo"SvelteKit project with shadcn-svelte detected."
echo""
# Check which components are already installed by scanning the components directory
COMPONENTS_DIR=""
# Try to read the components alias from components.json
if[[ -f "$PROJECT_DIR/components.json"]];then
# Extract the aliases.components path
ALIAS_PATH=$(grep -o '"components"[[:space:]]*:[[:space:]]*"[^"]*"'"$PROJECT_DIR/components.json"| head -1 | sed 's/.*"components"[[:space:]]*:[[:space:]]*"//'| sed 's/"//')
if[[ -n "$ALIAS_PATH"]];then
# Resolve $lib to src/lib
RESOLVED_PATH="${ALIAS_PATH//\$lib/src/lib}"
if[[ -d "$PROJECT_DIR/$RESOLVED_PATH/ui"]];then
COMPONENTS_DIR="$PROJECT_DIR/$RESOLVED_PATH/ui"
fi
fi
fi
# Fallback: check common locations
if[[ -z "$COMPONENTS_DIR"]];then
for dir in "src/lib/components/ui""src/lib/ui""src/components/ui";do
description: Specialized skill for creating and editing high-quality Kener documentation. MUST be used whenever creating or editing documentation files in the src/routes/(docs)/docs/content/ directory or updating docs.json navigation.
---
# Documentation Writer
Use this skill for all docs edits in `src/routes/(docs)/docs/content/` and when updating docs navigation in `src/routes/(docs)/docs.json`.
## Non-negotiable rules
1.**Be concise**: remove repetition and background that does not help the user complete a task.
2.**Be actionable**: prioritize “what to do” over theory.
3.**One source of truth**: if another page already has details, link to it instead of duplicating.
4.**Preserve structure**: keep valid frontmatter and heading anchor IDs.
5.**Keep examples copyable**: minimal, tested-looking, and directly relevant.
6.**Search before writing**: always check if the content already exists in some form before adding new sections or pages.
7.**Check Relevant Code**: Search the codebase inside `src/` for any relevant code, comments, or tests that can inform the documentation content and ensure accuracy.
## Docs config model (current)
`docs.json` is versioned. Sidebar lives inside tabs:
-`versions[].content.navigation.tabs[].sidebar`
- Sidebar groups contain `pages`
- Page paths use `content` (legacy `slug` may still appear in older content)
When adding a new doc page, add it to the appropriate tab sidebar path.
## Versioned link policy (mandatory)
- For v4 docs content, internal links MUST use explicit v4 paths: `/docs/v4/...`.
- Do not use unversioned shortcuts like `/docs/alerting/...` in v4 pages.
- Before finalizing, verify every internal link in edited files resolves to the intended version.
## Required page format
```markdown
---
title: Page Title
description: One-line summary of user outcome
---
```
- Use custom anchors for H2/H3 headings: `## Section {#section}`
- Use GitHub admonitions only when needed: `[!NOTE]`, `[!IMPORTANT]`, `[!WARNING]`, `[!CAUTION]`, `[!TIP]`
- Prefer short sections and short lists
## Preferred structure (default)
1. Short intro (1–2 sentences)
2. Quick setup / minimum config
3. Required variables/options table
4. Verification step
5. Top troubleshooting items
Only add extra sections if they materially improve task completion.
description: CLI tools for Svelte 5 documentation lookup and code analysis. MUST be used whenever creating or editing any Svelte component (.svelte) or Svelte module (.svelte.ts/.svelte.js). If possible, this skill should be executed within the svelte-file-editor agent for optimal results.
---
# Svelte 5 Code Writer
## CLI Tools
You have access to `@sveltejs/mcp` CLI for Svelte-specific assistance. Use these commands via `npx`:
### List Documentation Sections
```bash
npx @sveltejs/mcp list-sections
```
Lists all available Svelte 5 and SvelteKit documentation sections with titles and paths.
**Important:** When passing code with runes (`$state`, `$derived`, etc.) via the terminal, escape the `$` character as `\$` to prevent shell variable substitution.
## Workflow
1.**Uncertain about syntax?** Run `list-sections` then `get-documentation` for relevant topics
2.**Reviewing/debugging?** Run `svelte-autofixer` on the code to detect issues
3.**Always validate** - Run `svelte-autofixer` before finalizing any Svelte component
description: Tailwind CSS v4 utility-first styling patterns including responsive design, dark mode, and custom configuration. Use when styling with Tailwind, adding utility classes, configuring Tailwind, setting up dark mode, or customizing the theme.
user-invokable: false
metadata:
category: styling
---
# Tailwind CSS v4 Development Guidelines
Best practices for using Tailwind CSS v4 utility classes effectively.
**Note**: Tailwind CSS v4 (released January 2025) uses a CSS-first configuration approach. If you need v3 compatibility, tailwind.config.js is still supported.
## Core Principles
1.**Utility-First**: Use utility classes instead of custom CSS
2.**Mobile-First**: Design for mobile, then scale up with responsive modifiers
3.**Component Extraction**: Extract repeated patterns into components
4.**Consistent Spacing**: Use Tailwind's spacing scale
5.**Custom Configuration**: Extend the default theme for brand consistency
Thank you for considering contributing to our project! Here are some guidelines to help you get started.
---
## How to Contribute
1. Fork the repository and clone it locally.
2. Create a new branch for your feature or bug fix:
```bash
git checkout -b feature/your-feature-name
```
3. Make your changes and commit them:
```bash
git commit -m 'Describe your changes'
```
4. Push your changes to your fork:
```bash
git push origin feature/your-feature-name
```
5. Create a pull request to the `main` branch.
## Development
1. Install dependencies:
```bash
npm install
```
2. Create a `.env` file in the root of the project and add the following:
```bash
cp .env.example .env
```
2. Start the development server:
```bash
npm run dev
```
3. Open [http://localhost:3000](http://localhost:3000) in your browser.
## Documentation
The documentation is available in the `docs` folder. You can view it by going to [http://localhost:3000/docs/home](http://localhost:3000/docs/home) in your browser.
## Where to Start
1. Check out the [roadmap items](https://kener.ing/docs/roadmap/)
2. Add language support by following the [i18n guide](https://kener.ing/docs/i18n/)
Kener is an open-source status page application built with **SvelteKit 2.x** (**Svelte 5**) and **Node.js/Express**. It is a **TypeScript-first** codebase providing real-time monitoring, uptime tracking, incident management, and customizable dashboards.
## Architecture
### Dual Process Model
In development, `npm run dev` runs two parallel processes:
1.**SvelteKit dev server** (`vite dev`) - serves the frontend with HMR
In production, **`scripts/main.ts`** is the single entry point: Express server + SvelteKit handler + migrations + seeds + scheduler startup. Built output runs via `node build/main.js`.
- Route data loading: `+page.server.ts` / `+layout.server.ts`
- API endpoints: `+server.ts` files returning `json()`
## Types & Interfaces
Place types and interfaces in the appropriate folder based on where they are used:
- **`src/lib/types/`** - Shared types (safe to import from both server and client code). Use for domain models, DTOs, API response types, and anything needed on both sides.
- **`src/lib/server/types/`** - Server-only types (`db.ts`, `auth.ts`, `monitor.ts`, `api-server.ts`). Use for DB models, internal service types, auth/session types.
- **`src/lib/client/types/`** - Client-only types (`ui.ts`). Use for UI-specific types, component prop types.
Always use `import type { ... }` when importing types to avoid accidental runtime imports.
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
### 2. get-documentation
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.
## Database compatibility rule
All database operations — migrations, queries, and repository functions — **MUST** work across all three supported databases: **SQLite**, **PostgreSQL**, and **MySQL**. Use Knex.js schema builder and query builder abstractions; avoid raw SQL unless wrapped in dialect-safe helpers or guarded with `try/catch`. When writing migrations:
- Use `knex.schema.hasColumn` / `knex.schema.hasTable` guards for idempotency.
- Use Knex column types (`.string()`, `.integer()`, `.text()`, etc.) — never raw `ALTER TABLE` unless necessary.
- For data-seeding inside migrations, use standard Knex query builder (`.insert()`, `.update()`, `.orderBy()`, `.first()`).
- Test that `defaultTo()` values and `notNullable()` constraints work on all three engines.
## Documentation writing skill
When the user asks to write or edit documentation, follow the skill file:
-`.claude/skills/documentation-writer/SKILL.md`
This is mandatory for docs-related tasks. Prioritize short, clear, action-oriented docs and avoid bloat.
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What is Kener?
Kener is an open-source status page application built with **SvelteKit 2.x (Svelte 5)** and **Node.js/Express**. It is a **TypeScript-first** codebase providing real-time monitoring, uptime tracking, incident management, and customizable dashboards.
## Development Commands
```bash
npm run dev # Start dev server (SvelteKit + cron scheduler in parallel)
npm run build # Production build (SvelteKit then esbuild server bundle)
npm run start # Run production build (node build/main.js)
npm run check # Svelte + TypeScript type checking
npm run prettify # Format all files with Prettier
npm run migrate # Run database migrations via Knex
npm run seed # Run database seeds (migrations run automatically first)
```
## Architecture
### Dual Process Model
In development, `npm run dev` runs two parallel processes:
1.**SvelteKit dev server** (`vite dev`) - serves the frontend
An open-source status page application providing real-time monitoring, uptime tracking, incident management, and customizable dashboards.
## Language
### Monitoring
**Monitor**:
A single check against a service, with a unique tag, a type (API, Ping, TCP, DNS, SSL, SQL, Heartbeat, GameDig, Group, gRPC, None), and a status (ACTIVE or otherwise).
_Avoid_: Check, probe, service
**Inactive Monitor**:
A monitor that is not checked at all: the scheduler drops its job and no monitoring data is collected until it is made ACTIVE again. Independent of visibility (see Hidden Monitor).
_Avoid_: Disabled monitor, paused monitor
**Hidden Monitor**:
A monitor excluded from all status pages while remaining fully checked and alerted. Independent of ACTIVE/INACTIVE.
_Avoid_: Invisible monitor, private monitor
**Group Monitor**:
A monitor whose status is derived from other monitors via a weighted score (UP=1, DEGRADED=0.5, DOWN=0; maintenance counts as UP). A group cannot contain another group.
_Avoid_: Monitor group, composite monitor
**Member**:
A monitor belonging to a Group Monitor, carrying a weight and a position in the execution order. Membership is an explicit stored list, never a dynamic rule (e.g. tag wildcards).
_Avoid_: Child monitor, sub-monitor
**Weight**:
A member's share of the group score, between 0 and 1. Weights across a group's members must sum to 1. Any membership change (add or remove) redistributes all weights equally; manual tuning happens after membership is settled.
**Execution Order**:
The stored order in which a Group Monitor's members are checked before aggregation. Manually arranged, not derived.
**Eligible Monitor**:
A monitor that may become a Member: ACTIVE, not a Group Monitor, and not the group being edited itself.
**Stale Member**:
A Member whose monitor is no longer an Eligible Monitor (paused or deleted after being added). It remains a Member until explicitly removed, but is excluded from the group score.
### Maintenance
**Maintenance**:
A recurring maintenance definition: title, description, an RRULE schedule, a duration, and affected monitors. Identified by its own id.
_Avoid_: Maintenance window, maintenance event (that's an occurrence, see below)
**Maintenance Event**:
A single occurrence of a Maintenance, generated from its RRULE: a concrete start/end time with a lifecycle status (SCHEDULED → READY → ONGOING → COMPLETED). Has its own id, independent of the Maintenance id. The public maintenance page is keyed by Maintenance Event id.
Kener: Open-source Node.js status page tool, designed to make service monitoring and incident handling a breeze. It offers a sleek and user-friendly interface that simplifies tracking service outages and improves how we communicate during incidents. And the best part? Kener integrates seamlessly with GitHub, making incident management a team effort—making it easier for us to track and fix issues together in a collaborative and friendly environment.
# Kener - Stunning Status Pages
It uses files to store the data. Other adapters are coming soon
<p align="center">
<img src="https://kener.ing/og.jpg?v=1" width="100%" height="auto" class="rounded-lg shadow-lg" alt="kener example illustration">
</p>
Kener is still under development. There might be bugs. Please report them and I will fix them
[](https://railway.com/deploy/spSvic?referralCode=1Pn7vs&utm_medium=integration&utm_source=template&utm_campaign=generic)
[](https://zeabur.com/templates/1YRTMI?referralCode=rajnandan1)
[](https://render.com/deploy?repo=https%3A%2F%2Fgithub.com%2Frajnandan1%2Fkener)
</p>
## What is Kener?
**Kener** is a sleek and lightweight status page system built with **SvelteKit** and **NodeJS**. It’s not here to replace heavyweights like Datadog or Atlassian but rather to offer a simple, modern, and hassle-free way to set up a great-looking status page with minimal effort.
Designed with **ease of use** and **customization in mind**, Kener provides all the essential features you’d expect from a status page—without unnecessary complexity.
### Why Kener?
✅ Minimal overhead – Set up quickly with a clean, modern UI<br>
✅ Customizable – Easily tailor it to match your brand<br>
✅ Open-source & free – Because great tools should be accessible to everyone
### What's in a Name?
“Kener” is inspired by the Assamese word _“Kene”_, meaning _“how’s it going?”_. The _‘.ing’_ was added because, well… that domain was available. 😄
- Manage incidents with clear timelines, updates, and acknowledgements
- Schedule maintenance windows and keep users informed throughout
- Send notifications via **Email, Webhook, Slack, and Discord**
- Explore historical monitoring data and uptime trends
### 🎨 Status Page Experience and Branding
- Build branded, customizable status pages (logo, colors, CSS, themes)
- Support **light/dark mode**, localization, and timezone-aware display
- Embed status widgets and badges into external sites and portals
- Provide SEO-friendly public pages for global audiences
### 🛠️ Operations, Collaboration, and Automation
- Invite teams with role-based collaboration across workflows
- Manage multiple status pages from one Kener instance
- Use trigger-based workflows and template-driven messaging
- Manage API keys for secure integrations and automations
- Integrate analytics providers like GA, Plausible, Mixpanel, Umami, and Clarity
- Access the full REST API for incidents, monitors, and reporting
## Technologies Used
## Technologies used
- [SvelteKit](https://kit.svelte.dev/)
- [shadcn-svelte](https://www.shadcn-svelte.com/)
## Inspired from
- [Upptime](https://upptime.js.org/)
## Support Me
## Roadmap
- [ ] Add notification
- [ ] Add api to create incident
- [ ] Add Mysql adapter
If you’re enjoying Kener and want to support its development, consider sponsoring me on GitHub or treating me to a coffee. Your support helps keep the project growing! 🚀
- [Sponsor Me Using GitHub](https://github.com/sponsors/rajnandan1)
- [Buy Me a Coffee](https://www.buymeacoffee.com/rajnandan1)
## Contributing
If you want to contribute to Kener, please read the [Contribution Guide](https://github.com/rajnandan1/kener/blob/main/.github/CONTRIBUTING.md).
## Star History
[](https://star-history.com/#rajnandan1/kener&Date)
import{nasb,sasw,Vasm,Iasq,Tasx}from"./scheduler.ea4d12df.js";consta=[];functionz(s,u){return{subscribe:A(s,u).subscribe}}functionA(s,u=b){lett;constr=newSet;functiono(n){if(w(s,n)&&(s=n,t)){consti=!a.length;for(consteofr)e[1](),a.push(e,s);if(i){for(lete=0;e<a.length;e+=2)a[e][0](a[e+1]);a.length=0}}}functionf(n){o(n(s))}functionl(n,i=b){conste=[n,i];returnr.add(e),r.size===1&&(t=u(o,f)||b),n(s),()=>{r.delete(e),r.size===0&&t&&(t(),t=null)}}return{set:o,update:f,subscribe:l}}functionE(s,u,t){constr=!Array.isArray(s),o=r?[s]:s;if(!o.every(Boolean))thrownewError("derived() expects stores as input, got a falsy value");constf=u.length<2;returnz(t,(l,n)=>{leti=!1;conste=[];letd=0,p=b;consty=()=>{if(d)return;p();constc=u(r?e[0]:e,l,n);f?l(c):p=x(c)?c:b},h=o.map((c,g)=>m(c,_=>{e[g]=_,d&=~(1<<g),i&&y()},()=>{d|=1<<g}));returni=!0,y(),function(){q(h),p(),i=!1}})}functionI(s){return{subscribe:s.subscribe.bind(s)}}export{Iasa,Easd,zasr,Aasw};
import{wasu}from"./index.7ea6c3d8.js";varb;constE=((b=globalThis.__sveltekit_b9ssrf)==null?void0:b.base)??"";varh;(h=globalThis.__sveltekit_b9ssrf)==null||h.assets;constI="sveltekit:snapshot",x="sveltekit:scroll",O="sveltekit:index",c={tap:1,hover:2,viewport:3,eager:4,off:-1},v=location.origin;functionT(e){lett=e.baseURI;if(!t){consto=e.getElementsByTagName("base");t=o.length?o[0].href:e.URL}returnt}functionU(){return{x:pageXOffset,y:pageYOffset}}constd=newWeakSet,p={"preload-code":["","off","tap","hover","viewport","eager"],"preload-data":["","off","tap","hover"],keepfocus:["","true","off","false"],noscroll:["","true","off","false"],reload:["","true","off","false"],replacestate:["","true","off","false"]};functionf(e,t){consto=e.getAttribute(`data-sveltekit-${t}`);returnS(e,t,o),o}functionS(e,t,o){o!==null&&!d.has(e)&&!p[t].includes(o)&&(console.error(`Unexpected value for ${t} — should be one of ${p[t].map(n=>JSON.stringify(n)).join(", ")}`,e),d.add(e))}const_={...c,"":c.hover};functionk(e){lett=e.assignedSlot??e.parentNode;return(t==null?void0:t.nodeType)===11&&(t=t.host),t}functionN(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))returne;e=k(e)}}functionL(e,t){leto;try{o=newURL(einstanceofSVGAElement?e.href.baseVal:e.href,document.baseURI)}catch{}constn=einstanceofSVGAElement?e.target.baseVal:e.target,l=!o||!!n||R(o,t)||(e.getAttribute("rel")||"").split(/\s+/).includes("external"),r=(o==null?void0:o.origin)===v&&e.hasAttribute("download");return{url:o,external:l,target:n,download:r}}functionP(e){lett=null,o=null,n=null,l=null,r=null,a=null,s=e;for(;s&&s!==document.documentElement;)n===null&&(n=f(s,"preload-code")),l===null&&(l=f(s,"preload-data")),t===null&&(t=f(s,"keepfocus")),o===null&&(o=f(s,"noscroll")),r===null&&(r=f(s,"reload")),a===null&&(a=f(s,"replacestate")),s=k(s);functioni(w){switch(w){case"":case"true":return!0;case"off":case"false":return!1;default:returnnull}}return{preload_code:_[n??"off"],preload_data:_[l??"off"],keep_focus:i(t),noscroll:i(o),reload:i(r),replace_state:i(a)}}functiong(e){constt=u(e);leto=!0;functionn(){o=!0,t.update(a=>a)}functionl(a){o=!1,t.set(a)}functionr(a){lets;returnt.subscribe(i=>{(s===void0||o&&i!==s)&&a(s=i)})}return{notify:n,set:l,subscribe:r}}functionA(){const{set:e,subscribe:t}=u(!1);return{subscribe:t,check:async()=>!1}}functionR(e,t){returne.origin!==v||!e.pathname.startsWith(t)}functionV(e){e.client}constY={url:g({}),page:g({}),navigating:u(null),updated:A()};export{OasI,casP,xasS,Iasa,Lasb,Pasc,Yasd,Ease,Nasf,Tasg,Vash,Rasi,vaso,Uass};
import{sasO,zasR,Aasq,gasb,lasg,casz,hasE,ias$,masw,dasA,kasS,nask}from"../chunks/scheduler.ea4d12df.js";import{SasB,iasD,dasC,vasH,easd,aasm,oasj,sasx}from"../chunks/index.12c4b772.js";import{dasN}from"../chunks/singletons.1e27cce2.js";constP=()=>{constt=N;return{page:{subscribe:t.page.subscribe},navigating:{subscribe:t.navigating.subscribe},updated:t.updated}},_={subscribe(t){returnP().page.subscribe(t)}},y="node_modules/@sveltejs/kit/src/runtime/components/error.svelte";functionf(t){varh;lete,i=t[0].status+"",r,l,n,c=((h=t[0].error)==null?void0:h.message)+"",a;constv={c:function(){e=b("h1"),r=g(i),l=z(),n=b("p"),a=g(c),this.h()},l:function(s){e=E(s,"H1",{});varo=$(e);r=w(o,i),o.forEach(d),l=A(s),n=E(s,"P",{});varp=$(n);a=w(p,c),p.forEach(d),this.h()},h:function(){S(e,y,4,0,57),S(n,y,5,0,81)},m:function(s,o){m(s,e,o),j(e,r),m(s,l,o),m(s,n,o),j(n,a)},p:function(s,[o]){varp;o&1&&i!==(i=s[0].status+"")&&x(r,i),o&1&&c!==(c=((p=s[0].error)==null?void0:p.message)+"")&&x(a,c)},i:k,o:k,d:function(s){s&&(d(e),d(l),d(n))}};returnC("SvelteRegisterBlock",{block:v,id:f.name,type:"component",source:"",ctx:t}),v}functionF(t,e,i){letr;R(_,"page"),q(t,_,a=>i(0,r=a));let{$$slots:l={},$$scope:n}=e;H("Error",l,[]);constc=[];returnObject.keys(e).forEach(a=>{!~c.indexOf(a)&&a.slice(0,2)!=="$$"&&a!=="slot"&&console.warn(`<Error> was created with unknown prop '${a}'`)}),t.$capture_state=()=>({page:_,$page:r}),[r]}letK=classextendsB{constructor(e){super(e),D(this,e,F,f,O,{}),C("SvelteRegisterComponent",{component:this,tagName:"Error",options:e,id:f.name})}};export{Kascomponent};
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.