15 KiB
Quick Start
Kener has been tested from Node18.
Clone the repository
git clone https://github.com/rajnandan1/kener.git
cd kener
Install Dependencies
npm install
Configs
- Rename
config/site.example.yaml->config/site.yaml - Rename
config/monitors.example.yaml->config/monitors.yaml
mv config/site.example.yaml config/site.yaml
mv config/monitors.example.yaml config/monitors.yaml
Start Kener Dev
npm run kener:dev
Kener Development Server would be running at PORT 5173. Go to http://localhost:5173
Concepts
Kener has two parts. One is a svelte app which you can find in the src folder and there are code for monitors which you would find in scripts folder. If you want to update the frontend application then you should modify the src folder.
Folder structure
├── src (svelte frontend files)
├── static (things put here can be referenced directly example static/logo.png -> /logo.png)
├── scripts (nodejs server files)
├── prod.js(starts an express server, runs the scripts and serves the svelte site)
├── dev.js (starts the dev server)
Environment Vairable
PUBLIC_KENER_FOLDER (Required)
export PUBLIC_KENER_FOLDER=./build/client/kener
PORT
Defaults to 3000 if not specified
export PORT=4242
GH_TOKEN
A github token to read issues and create labels
export GH_TOKEN=your-github-token
API_TOKEN
To talk to kener apis you will need to set up a token. It uses Bearer Authorization
export API_TOKEN=sometoken
API_IP
export API_IP=127.0.0.1
MONITOR_YAML_PATH
export MONITOR_YAML_PATH=/your/path/monitors.yaml
SITE_YAML_PATH
export SITE_YAML_PATH=/your/path/site.yaml
Production Deployment
Once you have added the config/site.yaml or config/monitors.yaml or changed anything in src/
Kener should be run using prod.js script.
export NODE_ENV=production
npm i
npm run build
npm run serve
It also needs to yaml files to work
- site.yaml
- monitors.yaml
By default these are present in config/. However you can use different location either passing them as argument or having the path as enviorment variable
Add as Enviroment variables
export MONITOR_YAML_PATH=/your/path/monitors.yaml
export SITE_YAML_PATH=/your/path/site.yaml
Add as argument to prod.js
node prod.js --monitors /your/path/monitors.yaml --site /your/path/site.yaml
export PUBLIC_KENER_FOLDER=./build/client/kener
npm i
npm run kener:build
node prod.js
Github Setup
Kener uses github for incident management. Issues created in github using certain tags go to kener as incidents.
Step 1: Github Repositiory
Create a Github Repositiory. It can be either public or private
Step 2: Create Github Token
You can create either a classic token or personal access token
Creating Personal Access Token
- Go to Personal Access Token
- Token Name: kener
- Expiration: Use custom to select a calendar date
- Description: My Kener
- Repository access: Check Only Selected Repositories. Select your github repository
- Repository Permission: Select Issues Read Write
- Click on generate token
Creating Classic Token
- Go to Tokens
- Note: kener
- Expiration: No Expiration
- Scopes: write:packages
- Click on generate Token
Set the token as an environment variable
export GH_TOKEN=github_pat_11AD3ZA3Y0
Modify Site
There is a folder called /config. Inside which there is a site.yaml file. You can modify this file to have your own branding.
title: "Kener"
home: "/"
logo: "/logo.svg"
github:
owner: "rajnandan1"
repo: "kener"
incidentSince: 72
metaTags:
description: "Your description"
keywords: "keyword1, keyword2"
nav:
- name: "Documentation"
url: "/docs"
hero:
title: Kener is a Open-Source Status Page System
subtitle: Let your users know what's going on.
title
This translates to
<title>Your Title</title>
theme
It can be set by modifying the <html> class in src/app.html file
Dark Theme
<!DOCTYPE html>
<html lang="en" class="dark dark:bg-background">
Light theme
<!DOCTYPE html>
<html lang="en" >
Can be light or dark. Defaults to light
siteURL
Root URL where you are hosting kenner
...
siteURL: https://status.example.com
...
home
Location when someone clicks on the your brand in the top nav bar
...
home: "https://www.example.com
...
logo
URL of the logo that will be shown in the nav bar. You can also add your logo in the static folder
...
logo: "https://www.example.com/logo.png
...
favicon
It can be set by modifying the <head> tag in src/app.html file.
Example add a png called logo.png file in static/ and then
...
<link rel="icon" href="/logo.png" />
...
github
For incident kener uses github comments. Create an empty github repo and add them to site.yaml
github:
owner: "username"
repo: "respository"
incidentSince: 72
incidentSince is in hours. It means if an issue is created before 72 hours then kener would not honor it. Default is 24
metaTags
Meta tags are nothing but html <meta>. You can use them for SEO purposes
metaTags:
description: "Your description"
keywords: "keyword1, keyword2"
og:image: "https://example.com/og.png"
will become
<head>
<meta name="description" content="Your description">
<meta name="keywords" content="keyword1, keyword2">
<meta name="og:image" content="https://example.com/og.png">
</head>
hero
Use hero to add a banner to your kener page
hero:
title: Kener is a Open-Source Status Page System
subtitle: Let your users know what's going on.
nav
You can add more links to your navbar.
nav:
- name: "Home"
url: "/home"
Add Monitors
Inside config/ folder there is a file called monitors.yaml. We will be adding our monitors here. Please note that your yaml must be valid. It is an array.
Understanding monitors
Each monitor runs at 1 minute interval by default. Monitor runs in below priorty order.
- defaultStatus Data
- API call Data overrides above data(if specified)
- Push Status Data overrides API Data
- Manual Incident Data overrides Pushed data
Sample
- name: Google Search
description: Search the world's information, including webpages, images, videos and more.
tag: "google-search"
image: "/google.png"
cron: "* * * * *"
defaultStatus: "UP"
api:
timeout: 4000
method: POST
url: https://www.google.com/webhp
headers:
Content-Type: application/json
body: '{"order_amount":1,"order_currency":"INR"}'
eval: |
(function(statusCode, responseTime, responseDataBase64){
const resp = JSON.parse(atob(responseDataBase64));
return {
status: statusCode == 200 ? 'UP':'DOWN',
latency: responseTime,
}
})
| name | Required | This will be shown in the UI to your users. Keep it short and unique |
|---|---|---|
| name | Required + Unique | This will be shown in the UI to your users. Keep it short and unique |
| description | Optional | This will be show below your name |
| tag | Required + Unique | This is used to tag incidents created in Github using comments |
| image | Optional | To show a logo before the name |
| cron | Optinal | Use cron expression to specify the interval to run the monitors. Defaults to * * * * * i.e every minute |
| api.method | Optional | HTTP Method |
| api.url | Optional | HTTP URL |
| api.headers | Optional | HTTP headers |
| api.body | Optional | HTTP Body as string |
| api.eval | Optional | Evaluator written in JS, to parse HTTP response and calculate uptime and latency |
| defaultStatus | Optional | If no API is given this will be the default status. can be UP/DOWN/DEGRADED |
cron
Kener fills data every minute in UTC so if you give an expression that is not per minute, kener will backfill data using the latest status.
Example for cron: "*/15 * * * *"
- First run at "2023-12-02T18:00:00.000Z" - Status DOWN
- Second run at "2023-12-02T18:15:00.000Z" - Status UP
Kener will fill data from 18:01:00 to 18:14:00 as UP
eval
This is a anonymous JS function, by default it looks like this.
NOTE: The eval function should always return a json object. The json object can have only status(UP/DOWN/DEGRADED) and lantecy(number)
{status:"DEGRADED", latency: 200}.
(function (statusCode, responseTime, responseDataBase64) {
let statusCodeShort = Math.floor(statusCode/100);
let status = 'DOWN'
if(statusCodeShort >=2 && statusCodeShort <= 3) {
status = 'UP',
}
return {
status: 'DOWN',
latency: responseTime,
}
})
statusCodeREQUIRED is a number. It is the HTTP status coderesponseTimeREQUIREDis a number. It is the latency in millisecondsresponseDataBase64REQUIRED is a string. It is the base64 encoded response data. To use it you will have to decode it
let decodedResp = atob(responseDataBase64);
//let jsonResp = JSON.parse(decodedResp)
Monitor Examples
Here are some exhaustive examples for monitors
A Simple GET Monitor
- name: Google Search
tag: "google-search"
api:
method: GET
url: https://www.google.com/webhp
A GET Monitor with image
google.png is in the static folder
- name: Google Search
tag: "google-search"
image: "/google.png"
api:
method: GET
url: https://www.google.com/webhp
Get Monitor 15 Minute
- name: Google Search
description: Search the world's information, including webpages, images, videos and more.
tag: "google-search"
cron: "*/15 * * * *"
api:
method: GET
url: https://www.google.com/webhp
Post Monitor With Body
- name: Google Search
description: Google Search
tag: "google-search-post"
api:
method: POST
url: https://www.google.com/webhp
headers:
Content-Type: application/json
body: '{"order_amount":22222.1,"order_currency":"INR"}'
Secrets in Header
You can set ENV variables in your machine and use them in your monitors. Example below has GH_TOKEN as an environment variable. It uses process.env.GH_TOKEN.
export GH_TOKEN=some.token.for.github
NOTE: DO NOT forget the
$sign in your monitor
- name: Github Issues
description: Github Issues Fetch
tag: "gh-search-issue"
api:
method: GET
url: https://api.github.com/repos/rajnandan1/kener/issues
headers:
Authorization: Bearer $GH_TOKEN
Secrets in Body
Assuming ORDER_ID is present in env
- name: Github Issues
description: Github Issues Fetch
tag: "gh-search-issue"
api:
method: POST
url: https://api.github.com/repos/rajnandan1/kener/issues
headers:
Content-Type: application/json
body: '{"order_amount":22222.1,"order_currency":"INR", "order_id": "$ORDER_ID"}'
Eval Body
- name: Github Issues
description: Github Issues Fetch
tag: "gh-search-issue"
api:
method: GET
url: https://api.github.com/repos/rajnandan1/kener/issues
eval: |
(function(statusCode, responseTime, responseDataBase64){
const resp = JSON.parse(atob(responseDataBase64));
let status = 'DOWN'
if(statusCode == 200) status = 'UP';
if(resp.length == 0) status = 'DOWN';
if(statusCode == 200 && responseTime > 2000) status = 'DEGRADED';
return {
status: status,
latency: responseTime,
}
})
Incident Management
Kener uses Github to power incident management using labels
Labels
Kener auto creates labels for your monitors using the tag parameter
incident: If an issue is marked as incident it will show up in kener home pageincident-down: If an issue is marked as incident-down and incident kener would make that monitor downincident-degraded: If an issue is marked as incident-degraded and incident then kener would make the monitor degraded
Creating your first incident
- Go to your github repo of kener
- Go to issues
- Create an issue. Give it a title
- In the body add [start_datetime:1702651340] and [end_datetime:1702651140] and add some description. Time is UTC
- Add
incident,incident-downand the monitor tag. This will make the monitor down for 4 minutes
API
Kener also gives APIs to push data and create incident. Before you use kener apis you will have to set an authorization token called API_TOKEN. This also has to be set as an environment variable.
export API_TOKEN=some-token-set-by-you
Additonally you can set IP whitelisting by setting another environment token called API_IP
export API_IP=127.0.0.1
Update Status
curl --request POST \
--url http://your-kener.com/api/status \
--header 'Authorization: Bearer some-token-set-by-you' \
--header 'Content-Type: application/json' \
--data '{
"status": "UP",
"latency": 1213,
"timestampInSeconds": 1702405860,
"tag": "google-search"
}'
Badge
There are two types of badges
Status
Shows the last health check was UP/DOWN/DEGRADED
-> https://kener.ing/badge/earth/status

Uptime
Shows the 90 Day uptime
-> https://kener.ing/badge/earth/uptime



