Compare commits

..
34 changed files with 41 additions and 1580 deletions
@@ -1,31 +0,0 @@
name: Workflows queue consistency
# Fails the build if the analysis producer, the workflows engine and the stage
# workers would render onto different WORKFLOWS_QUEUE names — the silent
# producer/consumer queue-name drift that leaves runs piling up with no
# consumer. Pure `helm template` render check, no cluster required.
on:
workflow_dispatch:
pull_request:
paths:
- 'charts/hub/**'
- 'scripts/check-workflows-queue-consistency.sh'
- '.github/workflows/workflows-queue-consistency.yaml'
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-24.04
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: v3.16.2
- name: Check WORKFLOWS_QUEUE consistency
run: ./scripts/check-workflows-queue-consistency.sh charts/hub
+1 -1
View File
@@ -16,7 +16,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.117.0
version: 0.113.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
+3 -3
View File
@@ -1,6 +1,6 @@
# Kerberos Hub
Kerberos Hub is the single pane of glass for your Kerberos agents. It comes with a best of breed open source technology stack, modular and scale first mindset, and allows you to build and maintain an everless growing video surveillance and video analytics landscape.
Kerberos Hub is the single pane of glass for your Agents. It comes with a best of breed open source technology stack, modular and scale first mindset, and allows you to build and maintain an everless growing video surveillance and video analytics landscape.
![Kerberos Hub Interface](assets/images/hub-dashboard.png)
@@ -273,7 +273,7 @@ Below all configuration options and parameters are listed.
| `kerberoshub.cleanup.defaultTaskRetentionDays` | Default retention (in days) applied to tasks without an explicit `retention_days`. Tasks older than this (anchored on `creation_date`) are deleted with their `case_media` rows. Set to `"0"` or a negative value to keep tasks forever. Must match `kerberoshub.api.defaultTaskRetentionDays`. | `"0"` |
| `kerberoshub.cleanup.resources.requests.memory` | Memory request for `kerberoshub.cleanup`. | `"10Mi"` |
| `kerberoshub.cleanup.resources.requests.cpu` | CPU request for `kerberoshub.cleanup`. | `"10m"` |
| `kerberoshub.monitordevice.repository` | The monitoring microservice, following up the status of your cameras and Kerberos Agents. | `"ghcr.io/uug-ai/hub-monitor-device"` |
| `kerberoshub.monitordevice.repository` | The monitoring microservice, following up the status of your cameras and Agents. | `"ghcr.io/uug-ai/hub-monitor-device"` |
| `kerberoshub.monitordevice.pullPolicy` | The Docker registry pull policy. | `"IfNotPresent"` |
| `kerberoshub.monitordevice.tag` | The Docker image tag/version. | `"v1.4.0"` |
| `kerberoshub.monitordevice.replicas` | Number of replicas for `kerberoshub.monitordevice`. | `1` |
@@ -423,7 +423,7 @@ Below all configuration options and parameters are listed.
| `email.templates.highupload` | The template which is send when an account is reaching a specific upload threshold, by default `'threshold'`. | `"highupload"` |
| `email.templates.device` | The template which is send when a camera goes online or offline, by default `'device'`. | `"device"` |
| `email.templates.alertTitle` | Email subject title for the `alert` template. | `"[Alert] Kerberos Hub detected something an event"` |
| `email.templates.deviceTitle` | Email subject title for the `device` template. | `"[Device] A Kerberos Agent's status has been changed"` |
| `email.templates.deviceTitle` | Email subject title for the `device` template. | `"[Device] An Agent's status has been changed"` |
| `email.mailgun.apiKey` | The Mailgun API key linked to your Mailgun domain. | `""` |
| `imagePullSecrets.name` | Docker registry secret name, which is also granted with the license. This allows you to download the Docker images. | `""` |
| `kerberoshub.forwarder.pullPolicy` | The Docker registry pull policy. | `""` |
@@ -1,11 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: custom-i18n-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: azurefile-premium
resources:
requests:
storage: 25Mi
@@ -1,9 +0,0 @@
{
"nav": {
"cases": "Investigations",
"dashboard": "Home"
},
"login": {
"signInTo": "Sign in to {{domain}} \u2014 Acme Security"
}
}
+12 -26
View File
@@ -351,17 +351,6 @@
"generateExport": "Generate export",
"regenerateExport": "Generate export",
"exportInProgress": "Generating export…",
"exportStuck": "Export stuck — Retry",
"exportStuckTitle": "Export hasn't reported progress for over 2 minutes. Click to force a retry.",
"exportRetryConfirmTitle": "Force retry export?",
"exportRetryConfirmText": "This export hasn't reported progress for more than two minutes. Forcing a retry will discard the current run and start a new one.",
"exportRetryConfirmButton": "Force retry",
"exportRetryCancelButton": "Cancel",
"exportStatusGenerating": "Generating export…",
"exportStatusUnresponsive": "Export not responding",
"exportStatusFailed": "Export failed",
"exportStatusStale": "Export out of date",
"exportStatusReady": "Export ready",
"shareCase": "Share case",
"deleteCase": "Delete case",
"download": "Download",
@@ -1115,7 +1104,7 @@
"k1": "Add a Dataset",
"k2": "Are you sure you want to remove device",
"k3": "Search dataset..",
"k4": "Set up your first Kerberos Agent",
"k4": "Set up your first Agent",
"k5": "No devices are available",
"k6": "Subscribe to a plan",
"k7": "Dataset",
@@ -1163,12 +1152,12 @@
},
"new": {
"new": {
"k1": "Before you will see your recordings shown up on Kerberos Hub, you first need to install and configure a Kerberos Agent. A Kerberos Agent is a piece of software that processes a device, and will make recordings when a portion of the video stream has changed. These recordings will then be send to, and processed by the Kerberos Hub application.",
"k2": "After you've properly installed one or more Kerberos Agents, you can link it to the Kerberos Hub application using your",
"k1": "Before you will see your recordings shown up on Kerberos Hub, you first need to install and configure an Agent. An Agent is a piece of software that processes a device, and will make recordings when a portion of the video stream has changed. These recordings will then be send to, and processed by the Kerberos Hub application.",
"k2": "After you've properly installed one or more Agents, you can link it to the Kerberos Hub application using your",
"k3": "cloud credentials",
"k4": ". When linked successfully, your device will show up on the devices page.",
"k5": "Get started",
"k6": "We have different ways to get your Kerberos Agent up and running. You can install the Kerberos Agent using a prebuilt Docker container, inside your Kubernetes Cluster, on your Raspberry Pi (or any other compatible board) or on a simple VM. Please select the most appropriate solution to get started. Visit",
"k6": "We have different ways to get your Agent up and running. You can install the Agent using a prebuilt Docker container, inside your Kubernetes Cluster, on your Raspberry Pi (or any other compatible board) or on a simple VM. Please select the most appropriate solution to get started. Visit",
"k7": "our documentation website to learn more",
"k8": "A Docker image (amd64, armv7, arm64) is",
"k9": "available on the Docker Hub",
@@ -1178,8 +1167,8 @@
"k13": "Go to the plan page",
"k14": "on your cloud account and copy your cloud credentials.",
"k15": "When created successfully, you can navigate to the Kerberos Factory interface, and enter your cloud credentials in your deployment of global configuration.",
"k16": "Your new Kerberos Agent will show up on the devices page.",
"k17": "If you want to manage Kerberos Agents at scale with Kubernetes, then",
"k16": "Your new Agent will show up on the devices page.",
"k17": "If you want to manage Agents at scale with Kubernetes, then",
"k18": "Kerberos Factory is what you are looking for",
"k19": "Setup a Kubernetes cluster or use your existing Kubernetes cluster.",
"k20": "Install the different components of",
@@ -1188,7 +1177,7 @@
"k23": "Go to the plans & integrations page",
"k24": "on your Kerberos Hub account and copy your credentials.",
"k25": "When configured Kerberos Factory successfully, you can modify the global settings, and enter your credentials.",
"k26": "All Kerberos Agent you will now add, will show up on your devices page.",
"k26": "All Agent you will now add, will show up on your devices page.",
"k27": "KiOS is a custom linux OS, created for the Raspberry Pi, which runs Kerberos.io out-of-the-box. This is the most simple and basic installation procedure.",
"k28": "Download",
"k29": "the KiOS installer",
@@ -1687,10 +1676,7 @@
"k11": "Forward 1 second",
"k12": "Frames per second for redaction",
"k13": "Frames per second",
"k14": "fps",
"k15": "Detection run",
"k16": "Select a detection run",
"k17": "No detections"
"k14": "fps"
}
},
"motionmap": {
@@ -2131,9 +2117,9 @@
"k20": "Kerberos Hub credentials",
"k21": "Cloud credentials",
"k22": "Media storage region",
"k23": "Your media can be stored in one of the following regions. Different Kerberos agents can store in different regions.",
"k23": "Your media can be stored in one of the following regions. Different Agents can store in different regions.",
"k24": "Copy",
"k25": "Use the below credentials to connect your Kerberos Agents to your Kerberos Hub.",
"k25": "Use the below credentials to connect your Agents to your Kerberos Hub.",
"k26": "Use the below credentials to connect your devices to our cloud.",
"k27": "How to connect Agents to Kerberos Hub",
"k28": "Public key",
@@ -2572,7 +2558,7 @@
"k2": "draft",
"k3": "Device detail",
"k4": "Kerberos Factory",
"k5": "Kerberos Agent",
"k5": "Agent",
"k6": "n/a",
"k7": "Remove device"
}
@@ -2808,7 +2794,7 @@
"k2": "PREVIEW",
"k3": "Switch to live [Shift + L]",
"k4": "LIVE",
"k5": "Make sure you are using the latest Kerberos agent and a Gold subscription",
"k5": "Make sure you are using the latest Agent and a Gold subscription",
"k6": "You are not allowed to watch a live stream.",
"k7": "Turn sound off",
"k8": "Turn sound on",
@@ -225,7 +225,7 @@
margin-bottom: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;">One of your Kerberos Agents changed</h2>
padding-right: 0;">One of your Agents changed</h2>
<h2 style=" font-family: Inter;
font-size: 20px;
font-style: normal;
@@ -265,7 +265,7 @@
letter-spacing: 0em;
text-align: left;
color: #262424;
width: 480px">The status your Kerberos Agent changed</h3>
width: 480px">The status your Agent changed</h3>
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
@@ -275,8 +275,8 @@
letter-spacing: 0em;
text-align: left;
color: #6D6666;
margin-top: 12px;">Kerberos Agents go offline due to a variety of reasons. The machine, node, micro controller on which your Kerberos Agent runs, gets corrupted or disconnected from the internet.
The camera itself is broken, damaged or in the worst case tampered. Have a look into your Kerberos Hub account for the latest recordings and/or verify the connection and status of your Kerberos Agent.</p>
margin-top: 12px;">Agents go offline due to a variety of reasons. The machine, node, micro controller on which your Agent runs, gets corrupted or disconnected from the internet.
The camera itself is broken, damaged or in the worst case tampered. Have a look into your Kerberos Hub account for the latest recordings and/or verify the connection and status of your Agent.</p>
<a style="text-decoration: none;color: none;" href="{{link}}">
<p style="font-family: Inter;
@@ -286,7 +286,7 @@ Tomorrow your account will be reset, and recordings will be uploaded again to yo
letter-spacing: 0em;
text-align: left;
color: #6D6666;
margin-top: 12px;">If you are hitting your daily limits a lot, you might consider upgrading your Kerberos Hub subscription, or fine-tune your Kerberos Agents so they record less recordings.</p>
margin-top: 12px;">If you are hitting your daily limits a lot, you might consider upgrading your Kerberos Hub subscription, or fine-tune your Agents so they record less recordings.</p>
<a style="text-decoration: none;color: none;" href="{{link}}">
<p style="font-family: Inter;
@@ -225,7 +225,7 @@
margin-bottom: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;">High upload detected by one or more Kerberos Agents</h2>
padding-right: 0;">High upload detected by one or more Agents</h2>
</td>
<td class="corner-td" align="right"></td>
</tr>
@@ -1,418 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"
/>
<meta name="description" content="Kerberos.io Mailing">
<style type="text/css">
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff2"),
url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("https://kerberos.io/dist/fonts/Inter-Medium.woff2?v=3.18") format("woff2"),
url("https://kerberos.io/dist/fonts/Inter-Medium.woff?v=3.18") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff2?v=3.18") format("woff2"),
url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff?v=3.18") format("woff");
}
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url("https://kerberos.io/dist/fonts/Inter-roman.var.woff2?v=3.18") format("woff2");
}
body{
background: #E5E5E5;
margin-top:0;
margin-bottom: 0;
margin-right: 0;
margin-left: 0;
padding-top: 0;
padding-left: 0;
padding-right: 0;
padding-bottom: 0;
font-family: 'Inter';
}
a, a:hover, a:active {
color: #262424;
text-decoration: none;
}
.corner-td{
width: 60px;
}
table {border-collapse:separate;max-width: 850px; margin: 0 auto; width: 100%;}
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td {line-height: 100%;}
.ExternalClass {width: 100%;}
@media screen and (max-width:500px){
.tab-td{
padding-left: 10px!important;
}
.tab-td a h4{
font-size: 14px!important;
}
.corner-td{
width: 20px!important;
}
.company-name-td h3{
font-size: 16px!important;
}
table.header-table{
padding-top: 8px!important;
padding-right: 0px!important;
padding-bottom: 24px!important;
padding-left: 0px!important;
}
.colored-card-td h4{
font-size: 14px!important;
}
.colored-card-td h2{
font-size: 20px!important;
}
.colored-card-td a p{
font-size: 12px!important;
width: 143px!important;
}
.colored-card-td{
padding-top: 24px!important;
padding-right: 24px!important;
padding-bottom: 24px!important;
padding-left: 24px!important;
}
.colorless-card-td{
padding-top: 24px!important;
padding-right: 24px!important;
padding-bottom: 24px!important;
padding-left: 24px!important;
}
.colorless-card-td h3{
font-size: 18px!important;
}
.colorless-card-td p{
font-size: 14px!important;
}
.colorless-card-table{
margin-left: 0px!important;
margin-right: 0px!important;
margin-top: 24px!important;
margin-bottom: 24px!important;
}
.footer-td{
display: table-row!important;
}
}
@media screen and (max-width:600px) {
.footer-td{
display: table-row!important;
}
}
@media screen and (max-width:650px) {
.footer-table{
margin-left: 0px!important;
margin-right: 0px!important;
margin-top: 0px!important;
margin-bottom: 36px!important;
}
}
</style>
</head>
<body height="100%" width="100%">
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tr>
<td bgcolor="E5E5E5">
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="36" class="header-table" style="padding-top: 36px ;padding-right: 0;padding-bottom: 36px;padding-left: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tbody>
<tr>
<td class="corner-td" align="left"></td>
<td width="48" height="36" align="left"><img alt="Kerberos.io" width="36" height="36" src="https://kerberos.io/images/email/kerberos.png"/></td>
<td height="36" align="left" class="company-name-td">
<h3 width="36" height="36" style=" font-family: Inter;
font-size: 20px;
font-style: normal;
font-weight: 600;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #262424;">Kerberos.io</h3>
</td>
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right">
<a style="text-decoration: none;color: none;" href={{tab1_href}}>
<h4 style="font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: right;
color: #6D6666;">{{tab1_title}}</h4>
</a>
</td>
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right" >
<a style="text-decoration: none;color: none;" href={{tab2_href}}>
<h4 style="font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: right;
color: #6D6666;">{{tab2_title}}</h4>
</a>
</td>
<td class="corner-td" align="right"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tr>
<td bgcolor="E5E5E5">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
<tbody>
<tr>
<td class="corner-td" align="left"></td>
<td class="colored-card-td" bgcolor="#57356B" style="padding-left: 48px;padding-right: 48px;padding-top: 48px;padding-bottom: 48px;border-radius: 4px;background-color:#57356B;">
<h2 style=" font-family: Inter;
font-size: 24px;
font-style: normal;
font-weight: 600;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color:#FFFFFF;
padding-top: 12px;
margin-bottom: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;">A case has been shared with you</h2>
<h4 style="font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color:#b09fb9;">{{user}} shared a case with you</h4>
</td>
<td class="corner-td" align="right"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tr>
<td bgcolor="E5E5E5">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="colorless-card-table" style="margin-top: 36px;margin-bottom: 36px;margin-left: 0;margin-right: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
<tbody>
<tr>
<td class="corner-td" align="left"></td>
<td class="colorless-card-td" bgcolor="#FFFFFF" style="background-color:#FFFFFF;padding-top: 36px;padding-right: 43px;padding-bottom: 25px;padding-left: 43px;border-radius: 4px;">
<h3 style=" font-family: Inter;
font-size: 20px;
font-style: normal;
font-weight: 600;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #262424;
width: 280px">Open the shared case</h3>
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #6D6666;
margin-top: 12px;">{{user}} has shared a case with you. Click the button below to open it. You'll be asked to request a one-time verification code from the share page itself.<br/><br/>This link will expire in {{expiry}}.</p>
<a style="text-decoration: none;color: none;" href="{{url}}">
<p style="font-family: Inter;
font-size: 14px;
font-style: normal;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color:#FFFFFF;
background-color: #84559F;
padding-top: 6px;
padding-bottom: 6px;
padding-right: 16px;
padding-left: 16px;
width: 130px;
border-radius: 4px;
text-align: center;
cursor: pointer;">Open case -></p>
</a>
</td>
<td class="corner-td" align="right"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tr>
<td bgcolor="E5E5E5">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="footer-table" style="margin-top: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
<tbody>
<tr>
<td class="corner-td" align="left"></td>
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
<![endif]-->
<td height="146" width="190" class="footer-td" style="margin-bottom: 12px;" valign="top" align="left">
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tbody>
<tr>
<td>
<h4 style=" font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color:#6D6666;">Get in touch</h4>
<a style="text-decoration: none;color: none;" href="mailto:support@kerberos.io">
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 16px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #A69D9D;">support@kerberos.io</p>
</a>
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 16px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #A69D9D;">9000 Ghent, BE</p>
<a style="text-decoration: none;color: none;" href="https://kerberos.io/">
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #A69D9D;">https://kerberos.io</p>
</a>
</td>
</tr>
</tbody>
</table>
</td>
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
<![endif]-->
<td class="footer-td" style="border-radius: 4px;padding-left: 0;padding-right: 0;padding-top: 0;padding-bottom: 0; margin-bottom: 12px;" valign="top" align="left">
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tbody>
<tr>
<td>
<h4 style=" font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color:#6D6666;">About Kerberos</h4>
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #A69D9D;">Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.</p>
<p style="margin-top: 12px;">
<a href="https://twitter.com/kerberosio" style="text-decoration: none;color: none;">
<img width="24" height="24" alt="Twitter" src="https://kerberos.io/images/email/twitter.png"/>
</a>
<a href="https://reddit.com/r/kerberos_io" style="text-decoration: none;color: none;">
<img g width="24" height="24" alt="Reddit" src="https://kerberos.io/images/email/reddit.png"/>
</a>
<a href="https://www.youtube.com/channel/UCnd9q7iRNNw4W95eQwQuECA" style="text-decoration: none;color: none;">
<img g width="24" height="24" alt="Youtube" src="https://kerberos.io/images/email/youtube.png"/>
</a>
<a href="https://github.com/kerberos-io" style="text-decoration: none;color: none;">
<img g width="24" height="24" alt="Github" src="https://kerberos.io/images/email/github.png"/>
</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
<td class="corner-td" align="right"></td>
</tr>
</tbody>
</table>
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tbody>
<tr style="height: 50px">
<td></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</body>
</html>
@@ -1,21 +0,0 @@
Kerberos.io
------------
A case has been shared with you
{{user}} shared a case with you
Open the shared case
{{user}} has shared a case with you. Open the link below to access it — you'll be asked to request a one-time verification code from the share page.
{{url}}
This link will expire in {{expiry}}.
Get in touch
------------
support@kerberos.io
9000 Ghent, BE
https://kerberos.io
About Kerberos
------------
Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.
@@ -1,425 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"
/>
<meta name="description" content="Kerberos.io Mailing">
<style type="text/css">
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff2"),
url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("https://kerberos.io/dist/fonts/Inter-Medium.woff2?v=3.18") format("woff2"),
url("https://kerberos.io/dist/fonts/Inter-Medium.woff?v=3.18") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff2?v=3.18") format("woff2"),
url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff?v=3.18") format("woff");
}
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url("https://kerberos.io/dist/fonts/Inter-roman.var.woff2?v=3.18") format("woff2");
}
body{
background: #E5E5E5;
margin-top:0;
margin-bottom: 0;
margin-right: 0;
margin-left: 0;
padding-top: 0;
padding-left: 0;
padding-right: 0;
padding-bottom: 0;
font-family: 'Inter';
}
a, a:hover, a:active {
color: #262424;
text-decoration: none;
}
.corner-td{
width: 60px;
}
table {border-collapse:separate;max-width: 850px; margin: 0 auto; width: 100%;}
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td {line-height: 100%;}
.ExternalClass {width: 100%;}
@media screen and (max-width:500px){
.tab-td{
padding-left: 10px!important;
}
.tab-td a h4{
font-size: 14px!important;
}
.corner-td{
width: 20px!important;
}
.company-name-td h3{
font-size: 16px!important;
}
table.header-table{
padding-top: 8px!important;
padding-right: 0px!important;
padding-bottom: 24px!important;
padding-left: 0px!important;
}
.colored-card-td h4{
font-size: 14px!important;
}
.colored-card-td h2{
font-size: 20px!important;
}
.colored-card-td a p{
font-size: 12px!important;
width: 143px!important;
}
.colored-card-td{
padding-top: 24px!important;
padding-right: 24px!important;
padding-bottom: 24px!important;
padding-left: 24px!important;
}
.colorless-card-td{
padding-top: 24px!important;
padding-right: 24px!important;
padding-bottom: 24px!important;
padding-left: 24px!important;
}
.colorless-card-td h3{
font-size: 18px!important;
}
.colorless-card-td p{
font-size: 14px!important;
}
.colorless-card-table{
margin-left: 0px!important;
margin-right: 0px!important;
margin-top: 24px!important;
margin-bottom: 24px!important;
}
.footer-td{
display: table-row!important;
}
}
@media screen and (max-width:600px) {
.footer-td{
display: table-row!important;
}
}
@media screen and (max-width:650px) {
.footer-table{
margin-left: 0px!important;
margin-right: 0px!important;
margin-top: 0px!important;
margin-bottom: 36px!important;
}
}
</style>
</head>
<body height="100%" width="100%">
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tr>
<td bgcolor="E5E5E5">
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="36" class="header-table" style="padding-top: 36px ;padding-right: 0;padding-bottom: 36px;padding-left: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tbody>
<tr>
<td class="corner-td" align="left"></td>
<td width="48" height="36" align="left"><img alt="Kerberos.io" width="36" height="36" src="https://kerberos.io/images/email/kerberos.png"/></td>
<td height="36" align="left" class="company-name-td">
<h3 width="36" height="36" style=" font-family: Inter;
font-size: 20px;
font-style: normal;
font-weight: 600;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #262424;">Kerberos.io</h3>
</td>
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right">
<a style="text-decoration: none;color: none;" href={{tab1_href}}>
<h4 style="font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: right;
color: #6D6666;">{{tab1_title}}</h4>
</a>
</td>
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right" >
<a style="text-decoration: none;color: none;" href={{tab2_href}}>
<h4 style="font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: right;
color: #6D6666;">{{tab2_title}}</h4>
</a>
</td>
<td class="corner-td" align="right"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tr>
<td bgcolor="E5E5E5">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
<tbody>
<tr>
<td class="corner-td" align="left"></td>
<td class="colored-card-td" bgcolor="#57356B" style="padding-left: 48px;padding-right: 48px;padding-top: 48px;padding-bottom: 48px;border-radius: 4px;background-color:#57356B;">
<h2 style=" font-family: Inter;
font-size: 24px;
font-style: normal;
font-weight: 600;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color:#FFFFFF;
padding-top: 12px;
margin-bottom: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;">Verify your access</h2>
<h4 style="font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color:#b09fb9;">Use the code below to open the shared case</h4>
</td>
<td class="corner-td" align="right"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tr>
<td bgcolor="E5E5E5">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="colorless-card-table" style="margin-top: 36px;margin-bottom: 36px;margin-left: 0;margin-right: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
<tbody>
<tr>
<td class="corner-td" align="left"></td>
<td class="colorless-card-td" bgcolor="#FFFFFF" style="background-color:#FFFFFF;padding-top: 36px;padding-right: 43px;padding-bottom: 25px;padding-left: 43px;border-radius: 4px;">
<h3 style=" font-family: Inter;
font-size: 20px;
font-style: normal;
font-weight: 600;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #262424;
width: 280px">Your verification code</h3>
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #6D6666;
margin-top: 12px;">Enter the code below on the share page to access the case.</p>
<p style="font-family: 'Courier New', Courier, monospace;
font-size: 32px;
font-style: normal;
font-weight: 600;
line-height: 40px;
mso-line-height-rule:exactly;
letter-spacing: 8px;
text-align: center;
color:#262424;
background-color: #F2F0F4;
padding-top: 16px;
padding-bottom: 16px;
padding-right: 16px;
padding-left: 16px;
margin-top: 16px;
margin-bottom: 16px;
border-radius: 4px;">{{code}}</p>
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #6D6666;">This code expires in {{expiry}}. If you didn't request this, you can safely ignore this email.</p>
</td>
<td class="corner-td" align="right"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tr>
<td bgcolor="E5E5E5">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="footer-table" style="margin-top: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
<tbody>
<tr>
<td class="corner-td" align="left"></td>
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
<![endif]-->
<td height="146" width="190" class="footer-td" style="margin-bottom: 12px;" valign="top" align="left">
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tbody>
<tr>
<td>
<h4 style=" font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color:#6D6666;">Get in touch</h4>
<a style="text-decoration: none;color: none;" href="mailto:support@kerberos.io">
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 16px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #A69D9D;">support@kerberos.io</p>
</a>
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 16px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #A69D9D;">9000 Ghent, BE</p>
<a style="text-decoration: none;color: none;" href="https://kerberos.io/">
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #A69D9D;">https://kerberos.io</p>
</a>
</td>
</tr>
</tbody>
</table>
</td>
<!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
<![endif]-->
<td class="footer-td" style="border-radius: 4px;padding-left: 0;padding-right: 0;padding-top: 0;padding-bottom: 0; margin-bottom: 12px;" valign="top" align="left">
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tbody>
<tr>
<td>
<h4 style=" font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 36px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color:#6D6666;">About Kerberos</h4>
<p style=" font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 24px;
mso-line-height-rule:exactly;
letter-spacing: 0em;
text-align: left;
color: #A69D9D;">Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.</p>
<p style="margin-top: 12px;">
<a href="https://twitter.com/kerberosio" style="text-decoration: none;color: none;">
<img width="24" height="24" alt="Twitter" src="https://kerberos.io/images/email/twitter.png"/>
</a>
<a href="https://reddit.com/r/kerberos_io" style="text-decoration: none;color: none;">
<img g width="24" height="24" alt="Reddit" src="https://kerberos.io/images/email/reddit.png"/>
</a>
<a href="https://www.youtube.com/channel/UCnd9q7iRNNw4W95eQwQuECA" style="text-decoration: none;color: none;">
<img g width="24" height="24" alt="Youtube" src="https://kerberos.io/images/email/youtube.png"/>
</a>
<a href="https://github.com/kerberos-io" style="text-decoration: none;color: none;">
<img g width="24" height="24" alt="Github" src="https://kerberos.io/images/email/github.png"/>
</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
<td class="corner-td" align="right"></td>
</tr>
</tbody>
</table>
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
<tbody>
<tr style="height: 50px">
<td></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</body>
</html>
@@ -1,21 +0,0 @@
Kerberos.io
------------
Verify your access
Use the code below to open the shared case
Your verification code
{{code}}
Enter this code on the share page to access the case. This code expires in {{expiry}}.
If you didn't request this, you can safely ignore this email.
Get in touch
------------
support@kerberos.io
9000 Ghent, BE
https://kerberos.io
About Kerberos
------------
Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.
@@ -353,14 +353,6 @@ spec:
value: "{{ .Values.email.templates.share }}"
- name: SHARE_TITLE
value: "{{ .Values.email.templates.shareTitle }}"
- name: CASE_SHARE_TEMPLATE
value: "{{ .Values.email.templates.caseShare }}"
- name: CASE_SHARE_TITLE
value: "{{ .Values.email.templates.caseShareTitle }}"
- name: CASE_SHARE_OTP_TEMPLATE
value: "{{ .Values.email.templates.caseShareOtp }}"
- name: CASE_SHARE_OTP_TITLE
value: "{{ .Values.email.templates.caseShareOtpTitle }}"
- name: ASSIGN_TASK_TEMPLATE
value: "{{ .Values.email.templates.assignTask }}"
- name: ASSIGN_TASK_TITLE
@@ -416,7 +408,6 @@ spec:
{{- toYaml .Values.kerberoshub.extraEnv | nindent 12 }}
{{- end }}
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -424,5 +415,4 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
{{- end }}
@@ -241,8 +241,6 @@ spec:
value: "{{ .Values.kerberoshub.frontend.features.i18n.defaultLanguage }}"
# features > liveview
- name: FEATURE_LIVE_STREAM_MODE
value: "{{ .Values.kerberoshub.frontend.features.liveview.liveStreamMode }}"
- name: FEATURE_LIVEVIEW_PAGINATION_MODE
value: "{{ .Values.kerberoshub.frontend.features.liveview.paginationMode }}"
- name: FEATURE_LIVEVIEW_PAGE_SIZE
@@ -326,8 +326,6 @@ spec:
# features > liveview
- name: FEATURE_DEFAULT_STREAM_MODE
value: "{{ .Values.kerberoshub.frontend.features.liveview.defaultStreamMode }}"
- name: FEATURE_LIVE_STREAM_MODE
value: "{{ .Values.kerberoshub.frontend.features.liveview.liveStreamMode }}"
- name: FEATURE_LIVEVIEW_PAGINATION_MODE
value: "{{ .Values.kerberoshub.frontend.features.liveview.paginationMode }}"
- name: FEATURE_LIVEVIEW_PAGE_SIZE
@@ -1,37 +0,0 @@
{{/*
Assemble the workflows engine stage registry (PIPELINE_STAGE_REGISTRY) as a
JSON array from every *enabled* stage under kerberoshub.workflows.stages, so the
engine's routing stays in lockstep with the deployed stage workers and the
per-stage queue cannot drift. The `stages:` block is the routing source of truth;
each stage's deployment (and its queue) lives under the matching
kerberoshub.services.<name> entry.
Each enabled stage contributes one descriptor:
operation defaults to the stage's map key.
dispatch defaults to "always".
queue taken from the matching services.<key>.queue (authoritative; the
same value the worker consumes via <NAME>_QUEUE). Omitted when the
service or its queue is unset, so the engine derives
"kcloud-<operation>-queue.fifo".
needs conditional stages only: the upstream dependencies (each
{operation, condition?}) carried through verbatim.
needsMode conditional stages with more than one need: how they combine —
"any" (default; fire on the first matching upstream) or "all"
(a join; fire only once every need has resolved and matched).
Carried through verbatim; omitted when unset (engine defaults any).
*/}}
{{- define "kerberoshub.workflows.stageRegistry" -}}
{{- $entries := list -}}
{{- $services := .Values.kerberoshub.services | default dict -}}
{{- range $name, $stage := .Values.kerberoshub.workflows.stages -}}
{{- if $stage.enabled -}}
{{- $entry := dict "operation" (default $name $stage.operation) "dispatch" (default "always" $stage.dispatch) -}}
{{- $service := index $services $name -}}
{{- if $service }}{{- with $service.queue }}{{- $_ := set $entry "queue" . -}}{{- end }}{{- end }}
{{- with $stage.needs }}{{- $_ := set $entry "needs" . -}}{{- end }}
{{- with $stage.needsMode }}{{- $_ := set $entry "needsMode" . -}}{{- end }}
{{- $entries = append $entries $entry -}}
{{- end -}}
{{- end -}}
{{- $entries | toJson -}}
{{- end -}}
@@ -1,124 +0,0 @@
{{- /*
Generic workflow-stage worker.
Renders a Deployment + Service for every stage under
kerberoshub.workflows.stages that has a matching, enabled worker under
kerberoshub.services.<name>. A custom stage joins the pipeline by values
alone — no per-stage template needed.
Every stage worker receives the same connection contract; the only value that
varies by stage is the consume-queue variable name, <NAME>_QUEUE (a stage
keyed "anpr" gets ANPR_QUEUE, "my-stage" gets MY_STAGE_QUEUE). To run a worker
outside the chart instead, leave services.<name>.enabled unset (or false)
while keeping the stage under workflows.stages so the engine still routes to
the queue you publish.
*/ -}}
{{- if and (or (eq .Values.mode "all") (eq .Values.mode "pipeline")) .Values.kerberoshub.workflows.enabled -}}
{{- $root := . -}}
{{- $services := .Values.kerberoshub.services | default dict -}}
{{- range $name, $stage := .Values.kerberoshub.workflows.stages -}}
{{- $svc := index $services $name -}}
{{- if and $svc $svc.enabled -}}
{{- $queueEnv := printf "%s_QUEUE" ($name | upper | replace "-" "_") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hub-{{ $name }}
namespace: {{ $root.Release.Namespace }}
spec:
replicas: {{ $svc.replicas | default 1 }}
selector:
matchLabels:
app: hub-{{ $name }}
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
app: hub-{{ $name }}
spec:
{{- if $root.Values.kerberoshub.serviceAccount.create }}
serviceAccountName: {{ default (printf "%s-%s-sa" $root.Release.Name $root.Chart.Name | trunc 63 | trimSuffix "-") $root.Values.kerberoshub.serviceAccount.name }}
{{- end }}
{{- with $root.Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $svc.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $svc.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: hub-{{ $name }}
image: "{{ $root.Values.global.imageRegistry }}{{ $svc.repository }}:{{ $svc.tag }}"
imagePullPolicy: {{ $svc.pullPolicy | default "IfNotPresent" }}
{{- with $svc.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $svc.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
env:
- name: LOG_LEVEL
value: "{{ $svc.logLevel | default "info" }}"
- name: QUEUE_SYSTEM
value: "{{ $root.Values.queueProvider }}"
# The queue this stage worker consumes dispatched "{{ $name }}" stages
# from ({{ $queueEnv }}) and the workflows engine queue it routes its
# result back to (WORKFLOWS_QUEUE, so the run records the resolution and
# any stage that needs "{{ $name }}" can fire).
- name: {{ $queueEnv }}
value: "{{ $svc.queue }}"
- name: WORKFLOWS_QUEUE
value: "{{ $root.Values.kerberoshub.services.workflows.queue }}"
# RabbitMQ settings
- name: RABBITMQ_HOST
value: "{{ $root.Values.rabbitmq.host }}"
- name: RABBITMQ_EXCHANGE
value: "{{ $root.Values.rabbitmq.exchange }}"
- name: RABBITMQ_USERNAME
value: "{{ $root.Values.rabbitmq.username }}"
- name: RABBITMQ_PASSWORD
value: "{{ $root.Values.rabbitmq.password }}"
# Kerberos Vault — global storage credentials this stage uses to fetch
# the media it operates on.
- name: KERBEROS_STORAGE_URI
value: "{{ $root.Values.kerberosvault.uri }}"
- name: KERBEROS_STORAGE_ACCESS_KEY
value: "{{ $root.Values.kerberosvault.accesskey }}"
- name: KERBEROS_STORAGE_SECRET
value: "{{ $root.Values.kerberosvault.secretkey }}"
---
apiVersion: v1
kind: Service
metadata:
name: hub-{{ $name }}
namespace: {{ $root.Release.Namespace }}
labels:
app: hub-{{ $name }}
service: pipe
spec:
ports:
- name: hub-metrics
port: 8080
targetPort: 8080
protocol: TCP
selector:
app: hub-{{ $name }}
{{- end }}
{{- end }}
{{- end }}
@@ -1,116 +0,0 @@
{{- if and (or (eq .Values.mode "all") (eq .Values.mode "pipeline")) .Values.kerberoshub.workflows.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: hub-workflows
namespace: {{ .Release.Namespace }}
spec:
replicas: {{ .Values.kerberoshub.services.workflows.replicas }}
selector:
matchLabels:
app: hub-workflows
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
labels:
app: hub-workflows
spec:
{{- if .Values.kerberoshub.serviceAccount.create }}
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
{{- end }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberoshub.services.workflows.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberoshub.services.workflows.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: hub-workflows
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.services.workflows.repository }}:{{ .Values.kerberoshub.services.workflows.tag }}"
imagePullPolicy: {{ .Values.kerberoshub.services.workflows.pullPolicy }}
{{- with .Values.kerberoshub.services.workflows.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberoshub.services.workflows.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
env:
- name: LOG_LEVEL
value: "{{ .Values.kerberoshub.services.workflows.logLevel }}"
- name: QUEUE_SYSTEM
value: "{{ .Values.queueProvider }}"
# Queue this service consumes from (WORKFLOWS_QUEUE) and the custom
# pipeline stage registry it may dispatch (PIPELINE_STAGE_REGISTRY).
# The registry is assembled from the enabled stages under
# kerberoshub.workflows.stages (see _workflows-helpers.tpl).
- name: WORKFLOWS_QUEUE
value: "{{ .Values.kerberoshub.services.workflows.queue }}"
- name: PIPELINE_STAGE_REGISTRY
value: {{ include "kerberoshub.workflows.stageRegistry" . | quote }}
# RabbitMQ settings
- name: RABBITMQ_HOST
value: "{{ .Values.rabbitmq.host }}"
- name: RABBITMQ_EXCHANGE
value: "{{ .Values.rabbitmq.exchange }}"
- name: RABBITMQ_USERNAME
value: "{{ .Values.rabbitmq.username }}"
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
# Kerberos Vault — global storage credentials a dispatched stage worker
# uses to fetch the media. Per-recording vault overrides (site/account)
# are resolved at dispatch time from the database.
- name: KERBEROS_STORAGE_URI
value: "{{ .Values.kerberosvault.uri }}"
- name: KERBEROS_STORAGE_ACCESS_KEY
value: "{{ .Values.kerberosvault.accesskey }}"
- name: KERBEROS_STORAGE_SECRET
value: "{{ .Values.kerberosvault.secretkey }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
- name: OTEL_EXPORTED_OTLP_ROUTING_ENABLED
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: hub-workflows
namespace: {{ .Release.Namespace }}
labels:
app: hub-workflows
service: pipe
spec:
ports:
- name: hub-metrics
port: 8080
targetPort: 8080
protocol: TCP
selector:
app: hub-workflows
{{- end }}
@@ -81,18 +81,6 @@ spec:
value: "{{ .Values.rabbitmq.username }}"
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
# When true, analysis tees the classify result to the hub-workflows
# service in parallel with the throttler/notification tail (which still
# runs unchanged). Kept in sync with whether the workflows service runs.
- name: WORKFLOWS_ENABLED
value: "{{ .Values.kerberoshub.workflows.enabled }}"
# Queue analysis publishes opened workflow runs to (WORKFLOWS_QUEUE),
# taken from the workflows service's queue so analysis and the engine
# always agree on the queue name (no drift).
- name: WORKFLOWS_QUEUE
value: "{{ .Values.kerberoshub.services.workflows.queue }}"
# Kerberos Vault
- name: KERBEROS_STORAGE_URI
@@ -106,7 +94,6 @@ spec:
- name: SPRITE_ENABLED
value: "{{ .Values.kerberospipeline.sprite.enabled }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -114,7 +101,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -77,7 +77,6 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -85,7 +84,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -77,7 +77,6 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -85,7 +84,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -86,7 +86,6 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -94,7 +93,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -122,7 +122,6 @@ spec:
- name: VAULT_THUMBNAIL_SECRET_KEY
value: "{{ .Values.kerberosvault.thumbnail.secretKey }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -130,7 +129,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -103,7 +103,6 @@ spec:
- name: SMTP_PASSWORD
value: "{{ .Values.email.smtp.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -111,7 +110,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -110,7 +110,6 @@ spec:
- name: SMTP_PASSWORD
value: "{{ .Values.email.smtp.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -118,7 +117,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -142,7 +142,6 @@ spec:
- name: VAULT_SPRITE_SECRET_KEY
value: "{{ .Values.kerberosvault.sprite.secretKey }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -150,7 +149,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -87,7 +87,6 @@ spec:
- name: KERBEROS_STORAGE_SECRET
value: "{{ .Values.kerberosvault.secretkey }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -95,7 +94,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -80,7 +80,6 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -88,7 +87,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -100,7 +100,6 @@ spec:
- name: VAULT_SPRITE_HEIGHT
value: "{{ .Values.kerberospipeline.sprite.height }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -108,7 +107,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -82,7 +82,6 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -90,7 +89,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -103,7 +103,6 @@ spec:
- name: VAULT_THUMBNAIL_SECRET_KEY
value: "{{ .Values.kerberosvault.thumbnail.secretKey }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -111,7 +110,6 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
+19 -190
View File
@@ -66,7 +66,7 @@ mongodb:
# A MQTT broker (vernemq or other like mosquitto) is used to have a bi-directional communication between Agents and Hub.
# We recommend to use VernemQ (as part of this installation), but a stand-alone mosquitto broker is also possible.
mqtt:
host: "mqtt.yourdomain.com" # this needs to be a public accessible DNS name (it's used to communicate between Kerberos Agents and Kerberos Hub)
host: "mqtt.yourdomain.com" # this needs to be a public accessible DNS name (it's used to communicate between Agents and Kerberos Hub)
port: "8443"
protocol: "wss"
username: "yourusername"
@@ -121,12 +121,7 @@ opentelemetry:
enabled: false
routingEnabled: false
collector:
# NOTE: the services use the OTLP *HTTP* exporter, so this must be the
# collector's HTTP port (4318) and must include the scheme. Use http://
# for a plaintext in-cluster collector (e.g. Jaeger) and https:// only if
# the collector terminates TLS. A scheme-less value defaults to TLS and
# fails against a plaintext collector ("server gave HTTP response to HTTPS client").
endpoint: "http://otel-collector:4318"
endpoint: "http://otel-collector:4317"
############################################
# OpenAI configuration (semantic search)
#
@@ -231,7 +226,7 @@ kerberoshub:
api:
repository: ghcr.io/uug-ai/hub-api
pullPolicy: IfNotPresent
tag: "v1.9.29"
tag: "v1.9.8"
replicas: 2
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Set to false to skip rendering the hub-api Service (e.g. when an
@@ -344,7 +339,7 @@ kerberoshub:
frontend:
repository: ghcr.io/uug-ai/hub-frontend
pullPolicy: IfNotPresent
tag: "v1.9.32"
tag: "v1.9.11"
replicas: 2
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Set to false to skip rendering the hub-frontend Service (e.g. when an
@@ -377,8 +372,8 @@ kerberoshub:
# - hosts:
# - "demo.yourdomain.com"
# secretName: kerberoshub-demo
# You can disable the Kerberos agent buttons, this make sense
# in a white-label setup, or where you are managing the Kerberos Agents for your customers.
# You can disable the Agent buttons, this make sense
# in a white-label setup, or where you are managing the Agents for your customers.
hideAddAgent: "false"
# Multi tenancy (domains)
# By default the Kerberos Hub allows multi-tenancy through the concept
@@ -396,32 +391,17 @@ kerberoshub:
logo: "custom"
# Custom layout: override css, favicons and translations (i18n)
# By providing a style.css file in the custom folder this file will override
# any css styling. Favicons are mounted into the favicon folder.
#
# Translations (i18n): for each language the served strings are
# deepMerge(assets/i18n/<lang>.json, assets/i18n-custom/<lang>.json)
# — your optional overrides layered on top of the shipped base file. To
# customise, mount a volume at assets/i18n-custom holding <lang>.json files
# with the keys you want to change. Any key you omit falls back to the shipped
# value, so keys added in future releases always render a real string (never a
# raw key) and you only maintain your diffs. Put as little or as much here as
# you like — even a complete file — but mounting at this overlay layer is
# always the safe choice, because missing keys can never leak into the UI.
# You only need files for the languages you actually customise. A partial
# example lives in custom-layout/i18n-custom/en.json; the matching complete
# files (handy to copy keys from) are published as hub-frontend release assets
# (en.json / i18n-<tag>.zip) and ship in the image at assets/i18n.
#
# (Backward compatibility: deployments that instead mount complete files over
# assets/i18n keep working, but that layer has no fallback — any key you do
# not supply shows as a raw key — so prefer assets/i18n-custom for new setups.)
# any css styling. Favicons are mounted into the favicon folder. Custom
# translation files (e.g. en.json, nl.json, ...) can be mounted into the
# i18n folder to override the built-in translations shipped in the image.
# An example translation file can be found in custom-layout/i18n/en.json.
#volumeMounts:
# - name: custom-layout
# mountPath: /usr/share/nginx/html/assets/custom
# - name: custom-favicon
# mountPath: /usr/share/nginx/html/assets/favicon
# - name: custom-i18n
# mountPath: /usr/share/nginx/html/assets/i18n-custom
# mountPath: /usr/share/nginx/html/assets/i18n
#volumes:
# - name: custom-layout
# persistentVolumeClaim:
@@ -474,7 +454,6 @@ kerberoshub:
# Live view page
liveview:
defaultStreamMode: "SD" # Default stream mode 'SD' or 'HD' (will be migrated to 'preview' or 'live')
liveStreamMode: "webrtc" # Transport backing the LIVE (HD) mode 'webrtc' (default) or 'hls' (firewall-friendly, no TURN required)
paginationMode: "scroll" # Pagination mode in live view 'scroll', 'numbered' or 'maxStreams'
pageSize: "6" # Max streams shown per page when paginationMode is 'numbered' (4, 8, 12, 16 or 25)
maxStreams: "-1" # Maximum number of live streams to show in live view, -1 for unlimited
@@ -573,7 +552,7 @@ kerberoshub:
cleanup:
repository: ghcr.io/uug-ai/hub-cleanup
pullPolicy: IfNotPresent
tag: "v1.4.16"
tag: "v1.4.13"
replicas: 1 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
@@ -613,152 +592,6 @@ kerberoshub:
requests:
memory: 10Mi
cpu: 10m
# hub-workflows is the standalone, queue-driven workflow engine. It consumes
# pipeline events and dispatches custom stages declared in the stage registry,
# tracking each run in its own `workflow_runs` collection. It shares events
# (not a document) with the analysis pipeline and is meant to grow into the
# primary orchestrator. See https://github.com/uug-ai/hub-workflows.
workflows:
# Disabled by default. When enabled, the analysis service tees each
# classify result to this service (via WORKFLOWS_ENABLED on pipe-analysis)
# in parallel with the normal throttler/notification tail, which still runs
# unchanged. Flip to true to run the workflows engine.
enabled: false
# This block is purely behaviour: the master switch above plus the stage
# routing below. The engine's own deployment (image/tag/replicas/queue/
# resources) lives under kerberoshub.services.workflows, in the same uniform
# shape as the stage workers it dispatches to.
#
# Stage routing — how each custom stage fits into the workflow. The engine's
# PIPELINE_STAGE_REGISTRY is assembled automatically from the *enabled*
# stages below: each contributes its operation and dispatch/needs routing,
# plus the queue from its matching `services.<name>` entry — so the engine's
# routing stays in lockstep with the deployed worker and the queue cannot
# drift. There is nothing to set for the registry directly.
#
# Each stage is keyed to a worker of the same name under kerberoshub.services.
# Routing (here) and deployment (services) are toggled independently:
# workflows.stages.<name>.enabled -> include the stage in the registry (route to it)
# services.<name>.enabled -> deploy the worker (pipe-<name>.yaml)
stages:
# hub-anpr — automatic number-plate recognition stage. Routing only; its
# worker deployment lives under kerberoshub.services.anpr.
anpr:
# Include this stage in the engine's registry (route to it).
enabled: false
# operation unique stage id (defaults to the key "anpr" if omitted);
# binds the queue (taken from services.anpr.queue) and how
# the result is recorded.
# dispatch "always" (run on every workflow) | "conditional".
# needs conditional stages only: upstream dependencies, each
# {operation?, condition}. operation is the readiness GATE —
# the upstream op whose data must be present before the
# condition is read; leave it empty for a check on the run
# root itself (device/user/identity), read as soon as the run
# opens. condition shape:
# {path: <abs-path>, op: eq|ne|contains|in|exists|gt|gte|lt|lte, value: <operand>}.
# path is ABSOLUTE from the run root and resolves through
# string-keyed maps only (it CANNOT index into arrays):
# inputs.<op>.<field> (a trigger result, e.g. classify),
# results.<op>.<field> (a finished stage), device.<field>,
# user.<field>, or a top-level scalar (operation/runId/key).
# classify is the only operation teed to workflows, so it is
# the reliable upstream: match inputs.classify.properties (the
# detected class strings) with `contains` — there is no
# top-level `label`, and inputs.classify.details is an array a
# path cannot index into — or gate on inputs.classify.objectCount
# (top-level int) numerically. The engine rejects an unknown
# path at boot.
# needsMode conditional stages with more than one need: how they
# combine. "any" (default) fires on the first matching
# need; "all" is a join — the stage fires only once every
# need has resolved and each condition matches, and only once.
operation: anpr
dispatch: conditional
# The demo worker hands back a detection block (the car detection it
# compiles from the classify boxes, in the run Payload) that the engine's
# shared ingest core persists into the detections collection, so the
# recording's edit-media modal shows the car boxes. The engine routes
# ingest by each block's type, so no per-stage wiring is needed here.
# Default routing — run anpr whenever classify reports a car (any camera).
needsMode: any
needs:
- operation: classify
condition: {path: "inputs.classify.properties", op: contains, value: car}
# Restrict plate recognition to a SINGLE camera: add an ungated need on
# the recording's device (empty operation = checked as soon as the run
# opens) and switch needsMode to "all", so both must hold — "that camera
# AND a car was detected". For several cameras use op: in with a list.
#needsMode: all
#needs:
# - operation: classify
# condition: {path: "inputs.classify.properties", op: contains, value: car}
# - operation:
# condition: {path: "device.deviceKey", op: eq, value: device02}
# Workflow deployments. Every workflows-subsystem Deployment's image/tag/
# replicas/resources/queue lives here in a single, uniform shape:
# - `workflows` is the engine itself (the orchestrator). It is deployed
# whenever kerberoshub.workflows.enabled is true and has no `enabled` of
# its own — the master switch already gates the whole subsystem.
# - every other entry is a stage worker the engine dispatches to, keyed to
# match a kerberoshub.workflows.stages entry of the same name. A worker
# consumes its own queue and routes its result back to the engine queue.
# Deploying a worker (services.<name>.enabled) is independent from routing
# to it (workflows.stages.<name>.enabled) — it runs only when its own
# `enabled` is true AND the workflows engine is enabled.
services:
# hub-workflows — the workflows engine (orchestrator). Consumes the engine
# queue, evaluates the stage registry (assembled from workflows.stages) and
# dispatches to the stage workers below. Deployed when workflows.enabled is
# true; it has no separate `enabled` here.
workflows:
repository: ghcr.io/uug-ai/hub-workflows
pullPolicy: IfNotPresent
tag: "v1.0.0"
replicas: 1 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
#volumes:
# - name: extra
# emptyDir: {}
#volumeMounts:
# - name: extra
# mountPath: /data
logLevel: "info" # possible values: trace, debug, info, warn, error
# Queue this service consumes ingest events and upstream results from
# (WORKFLOWS_QUEUE). Must be fed the same messages the analysis service sees.
queue: "hub-workflows-queue"
resources:
requests:
memory: 10Mi
cpu: 10m
# hub-anpr — automatic number-plate recognition worker. Lives in the
# hub-workflows repository as its own module. See
# https://github.com/uug-ai/hub-workflows/tree/main/hub-anpr.
anpr:
# Deploy the hub-anpr worker.
enabled: false
repository: ghcr.io/uug-ai/hub-anpr
pullPolicy: IfNotPresent
tag: "v1.0.0"
replicas: 1 # Number of pods for the worker.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
#volumes:
# - name: extra
# emptyDir: {}
#volumeMounts:
# - name: extra
# mountPath: /data
logLevel: "info" # possible values: trace, debug, info, warn, error
# Queue this worker consumes dispatched messages from (ANPR_QUEUE). This
# same value is read into the engine's generated registry entry for the
# matching stage, so the engine dispatches and the worker consumes the same
# queue with no drift. Convention: "kcloud-<operation>-queue.fifo".
queue: "kcloud-anpr-queue.fifo"
resources:
requests:
memory: 10Mi
cpu: 10m
monitordevice:
repository: ghcr.io/uug-ai/hub-monitor-device
pullPolicy: IfNotPresent
@@ -858,7 +691,7 @@ kerberospipeline:
monitor:
repository: ghcr.io/uug-ai/hub-pipeline-monitor
pullPolicy: IfNotPresent
tag: "v1.3.10"
tag: "v1.3.9"
replicas: 1 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
@@ -947,7 +780,7 @@ kerberospipeline:
analysis:
repository: ghcr.io/uug-ai/hub-pipeline-analysis
pullPolicy: IfNotPresent
tag: "v1.7.17"
tag: "v1.7.8"
replicas: 1 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
@@ -986,7 +819,7 @@ kerberospipeline:
thumbnail:
repository: ghcr.io/uug-ai/hub-pipeline-thumbnail
pullPolicy: IfNotPresent
tag: "v1.3.6"
tag: "v1.3.4"
replicas: 2 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
@@ -1031,7 +864,7 @@ kerberospipeline:
enabled: false # Enable or disable the sprite generation 'true' or 'false
repository: ghcr.io/uug-ai/hub-pipeline-sprite
pullPolicy: IfNotPresent
tag: "v1.1.14"
tag: "v1.1.12"
replicas: 5 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
@@ -1055,7 +888,7 @@ kerberospipeline:
export:
repository: ghcr.io/uug-ai/hub-pipeline-export
pullPolicy: IfNotPresent
tag: "v1.2.8"
tag: "v1.2.4"
replicas: 2 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
logLevel: "info" # possible values: trace, debug, info, warn, error
@@ -1119,15 +952,11 @@ email:
forgotTitle: "Password reset Kerberos Hub. You forgot your password"
share: "share"
shareTitle: "[Action] You received a recording from Kerberos Hub"
caseShare: "share_case"
caseShareTitle: "[Action] A case has been shared with you on Kerberos Hub"
caseShareOtp: "share_case_otp"
caseShareOtpTitle: "Your Kerberos Hub verification code"
assignTask: "assign_task"
assignTaskTitle: "[Action] You've been assigned to a task"
detection: "detection"
disabled: "disable"
disabled: "disabled"
highupload: "highupload"
device: "device"
alertTitle: "[Alert] Kerberos Hub detected something an event"
deviceTitle: "[Device] A Kerberos Agent's status has been changed"
deviceTitle: "[Device] An Agent's status has been changed"
@@ -1,89 +0,0 @@
#!/usr/bin/env bash
#
# Render the hub chart and assert that every deployment which carries the
# workflows hand-off queue (the WORKFLOWS_QUEUE env var) resolves to the SAME,
# non-empty value.
#
# Why: the analysis pipeline (pipe-analysis) publishes opened workflow runs to
# WORKFLOWS_QUEUE, the workflows engine (hub-workflows) consumes it, and every
# stage worker (hub-stage) routes its result back to it. All three templates
# read the single key `kerberoshub.services.workflows.queue`. If a future edit
# hardcodes a value, reads the wrong key, or drops the env on one of them, the
# producer and consumer silently drift onto different queue names and messages
# pile up with no consumer. This check fails the build before that can ship.
#
# Usage: scripts/check-workflows-queue-consistency.sh [chart-dir]
# (chart-dir defaults to charts/hub, relative to the repo root)
set -euo pipefail
CHART_DIR="${1:-charts/hub}"
PROBE="drift-probe-queue-name"
# Flags that force all three deployment kinds (analysis, engine and one stage
# worker) to render, so the check actually has something to compare. anpr is a
# stage/worker shipped in the chart's default values.
RENDER_FLAGS=(
--set mode=all
--set kerberoshub.workflows.enabled=true
--set kerberoshub.workflows.stages.anpr.enabled=true
--set kerberoshub.services.anpr.enabled=true
)
# Read `helm template` output on stdin and print one WORKFLOWS_QUEUE value per
# line. Matches the `- name: WORKFLOWS_QUEUE` env entry and captures the value
# from the following `value:` line, skipping blank/comment lines in between.
extract_workflows_queue() {
awk '
/^[[:space:]]*-[[:space:]]*name:[[:space:]]*WORKFLOWS_QUEUE[[:space:]]*$/ { want=1; next }
want==1 {
if ($0 ~ /^[[:space:]]*#/ || $0 ~ /^[[:space:]]*$/) next
v=$0
sub(/^[[:space:]]*value:[[:space:]]*/, "", v)
sub(/^"/, "", v); sub(/"[[:space:]]*$/, "", v)
sub(/[[:space:]]+$/, "", v)
print v
want=0
}
'
}
assert_all_equal() {
local expected="$1"; shift
local label="$1"; shift
local -a vals=("$@")
if [ "${#vals[@]}" -lt 2 ]; then
echo "FAIL (${label}): expected at least 2 WORKFLOWS_QUEUE values (analysis + engine), found ${#vals[@]}" >&2
return 1
fi
local v
for v in "${vals[@]}"; do
if [ -z "${v}" ]; then
echo "FAIL (${label}): a deployment rendered an empty WORKFLOWS_QUEUE value" >&2
return 1
fi
if [ "${v}" != "${expected}" ]; then
echo "FAIL (${label}): WORKFLOWS_QUEUE drift detected — expected '${expected}' but a deployment rendered '${v}'" >&2
printf ' rendered values: %s\n' "${vals[*]}" >&2
return 1
fi
done
echo "OK (${label}): ${#vals[@]} deployments all use WORKFLOWS_QUEUE='${expected}'"
}
echo "== Rendering ${CHART_DIR} with the chart's default workflows queue =="
default_out="$(helm template hub "${CHART_DIR}" "${RENDER_FLAGS[@]}")"
mapfile -t default_vals < <(printf '%s\n' "${default_out}" | extract_workflows_queue)
default_queue="${default_vals[0]:-}"
assert_all_equal "${default_queue}" "default values" "${default_vals[@]}" || exit 1
echo "== Rendering ${CHART_DIR} with an overridden workflows queue (-> ${PROBE}) =="
probe_out="$(helm template hub "${CHART_DIR}" "${RENDER_FLAGS[@]}" \
--set kerberoshub.services.workflows.queue="${PROBE}")"
mapfile -t probe_vals < <(printf '%s\n' "${probe_out}" | extract_workflows_queue)
assert_all_equal "${PROBE}" "override probe" "${probe_vals[@]}" || exit 1
echo "All WORKFLOWS_QUEUE consistency checks passed."