Compare commits

..
Author SHA1 Message Date
Cédric Verstraeten 26957e239f Merge pull request #182 from kerberos-io/feature/add-disable-heartbeat-media-proxy
feature/add-disable-heartbeat-media-proxy
2026-10-05 17:16:59 +02:00
Cédric Verstraeten 03210c53c0 Add Hub media proxy and feature controls
Expose media proxy settings, a device polling disable switch, and an analysis filter toggle for both frontends. Document the options and bump the chart to 0.150.0.
2026-10-05 17:12:59 +02:00
KilianandCopilot 2111c0f9d6 Add workflow condition contracts and shared catalog packaging (#180)
* Add workflow condition contracts and shared catalog packaging

Define versioned contract and catalog schemas, Start and custom-stage examples, and opt-in bundled/external ConfigMap packaging. Extract classification defaults without changing the rendered default manifests or existing overrides.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Bump Hub chart to 0.149.0

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Omit optional classification catalog override by default

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 16:40:48 +02:00
KilianandCopilot eef273bf3c Support explicit IDs for deployment-defined workflows (#179)
Render and validate optional stable workflow IDs, retain legacy name-derived identities, pin documented examples, and exercise identity consistency across API and engine manifests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-29 16:15:02 +02:00
Cédric Verstraeten dc6c79df2e Merge pull request #178 from kerberos-io/feature/extend-workflow-designer
feature/extend-workflow-designer
2026-09-28 17:17:44 +02:00
cedricve a847bcd3d5 Bump chart version to 0.148.0 for release 2026-09-28 14:48:23 +00:00
cedricve a9f916fe89 Add WORKFLOW_EDITOR_STAGES to hub-api.yaml and values.yaml for workflow editor customization 2026-09-28 13:44:15 +00:00
cedricve 9900824b59 Update comments in hub-workflows.yaml to clarify engine behavior with empty config definitions 2026-09-28 12:17:27 +00:00
Cédric Verstraeten d2a550b526 Add WORKFLOW_STAGE_QUEUES to hub-workflows.yaml for DB workflow queue catalog 2026-09-28 13:53:45 +02:00
Cédric Verstraeten 277b4e64b6 Merge pull request #177 from kerberos-io/feature/hub-vlm-worker
feature/hub-vlm-worker
2026-09-25 16:26:13 +02:00
Cédric VerstraetenandCopilot 6f23fba755 Add hub-vlm stage worker example and secretEnv support
- hub-stage: render services.<name>.secretEnv (ENV: {name, key}) as
  secretKeyRef env so worker credentials stay out of values; nothing is
  rendered when unset, so existing workers are unchanged.
- values: commented hub-vlm (ghcr.io/uug-ai/hub-vlm:v1.0.0) worker and
  vlm-workflow definition examples.
- Bump chart to 0.147.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-25 14:18:56 +00:00
Cédric Verstraeten 1da29a87f4 Merge pull request #171 from kerberos-io/feature/dynamic-sprite-size
feature/dynamic-sprite-size
2026-09-21 21:40:22 +02:00
Cédric Verstraeten f4d62af315 Merge branch 'main' into feature/dynamic-sprite-size 2026-09-21 21:37:55 +02:00
Cédric Verstraeten 5f457002b5 Update Chart.yaml 2026-09-21 21:37:00 +02:00
Cédric Verstraeten 440b475966 Fix vault sprite interval configuration
Read the sprite interval from the Kerberos Pipeline values, consistent with the other sprite dimensions.
2026-09-21 20:30:12 +02:00
KilianandCopilot e20c562fc6 Add optional pipeline Deployment annotations and sync-wave examples (#174)
* Add optional pipeline Deployment annotations and sync-wave examples

Expose empty-by-default annotation maps per pipeline service and an opt-in waves 10-70 example. Include a standalone Helm render check; CI workflow wiring is deferred due credential scope.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Keep sync-wave examples inline with each service

Remove the separate overlay and make render checks supply per-service overrides directly. Leave optional defaults and Deployment template support unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-21 17:44:08 +02:00
Kilian c02f503c4e Configure notification audit producer (#173)
* feat(hub): configure notification audit producer

* feat(hub): add per-service audit switches

* refactor(hub): centralize audit transport settings

* docs(hub): clarify audit producer defaults
2026-09-21 16:18:32 +02:00
Kilian 632cf8aa4b feat(hub): configure explicit API audit enablement (#172) 2026-09-21 12:08:36 +02:00
Cédric Verstraeten ad5d3305f2 Fix VAULT_SPRITE env vars and document sprite sizing
Hub API was reading VAULT_SPRITE_WIDTH/HEIGHT from kerberosvault.sprite instead of kerberospipeline.sprite; fixed the mismatch. Also removed the outdated 'hard coded in UI' note and added README/values documentation on sprite tile resolution, aspect ratio requirements, and rollout order for dependent services.
2026-09-19 09:41:14 +02:00
Kilian 1538b8cf12 Clarify Live View motion status flag (#170)
* docs(hub): clarify live view status flag

Clarify that the device status flag controls connectivity and recent-motion state.

* docs(hub): clarify remote recording UI

Clarify the remote-recording value controls the REC action and recording-state overlay.
2026-09-16 15:43:52 +02:00
KilianandCopilot 30378b94bf feat(hub): make classification catalog optional (#169)
Allow deployments to disable the rendered classification catalog so Hub API can read the legacy MongoDB settings catalog directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-16 08:53:20 +02:00
Kilian f6ac0b771c feat(hub): configure API audit producer (#168)
Pass the audit intake queue and dead-letter queue to Hub API when the audit service is enabled, and bump the chart to 0.141.0.
2026-09-15 17:07:51 +02:00
Kilian 065335c0f8 feat(hub): expose live view status flag (#167)
Map the Live View device status badge setting into normal and demo frontend deployments.
2026-09-15 17:07:45 +02:00
KilianandCopilot 456aba7ee5 feat: configure marker category priority (#166)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-15 17:07:41 +02:00
Kilian a364873372 Merge pull request #165 from kerberos-io/feat/audit-intake
Configure durable Hub audit intake
2026-09-15 11:54:09 +02:00
Kilian Boute 3e78109e52 chore(hub): pin audit v1.0.1 2026-09-15 08:48:47 +00:00
Kilian Boute 4a6ea2dc59 chore(hub): bump chart to 0.140.0 2026-09-15 07:46:23 +00:00
Kilian Boute 518ea5732a Merge remote-tracking branch 'origin/main' into feat/audit-intake 2026-09-15 07:45:32 +00:00
Kilian a2b10cde36 Merge pull request #164 from kerberos-io/chore/hub-chart-0.139.0
Bump Hub chart to 0.139.0
2026-09-14 16:24:40 +02:00
Kilian Boute 4ed325fdab feat(hub): configure durable audit intake 2026-09-14 11:51:47 +00:00
Kilian Boute 01f5a7beb3 chore: bump Hub chart to 0.139.0 2026-09-14 11:36:10 +00:00
Kilian 52165fa668 Merge pull request #163 from kerberos-io/feat/device-developer-tools-flag
Add device developer tools feature flag
2026-09-14 13:31:12 +02:00
Kilian Boute 93ba2c6045 feat: add device developer tools flag 2026-09-14 09:51:53 +00:00
Kilian cb7e51e2e3 Merge pull request #162 from kerberos-io/chore/hub-chart-0.138.0
Bump Hub chart to 0.138.0
2026-09-11 16:15:48 +02:00
Kilian Boute 293ae4699c chore: bump Hub chart to 0.138.0 2026-09-11 14:11:43 +00:00
Kilian 142e7d5c54 Merge pull request #160 from kerberos-io/feat/configurable-chart-colors
Add configurable frontend chart colors
2026-09-11 15:50:07 +02:00
Kilian 07fd4c345d Merge pull request #161 from kerberos-io/feat/device-analytics-flag
Add device analytics and dashboard map flags
2026-09-11 15:34:08 +02:00
Kilian Boute 0ed38b82f7 feat: configure dashboard and device flags 2026-09-11 12:46:58 +00:00
Kilian Boute 2c17f5a579 feat: configure frontend chart colors 2026-09-11 11:45:37 +00:00
Kilian ae3d1d26fb Merge pull request #159 from kerberos-io/feat/audit-destinations
Add configurable audit destinations
2026-09-11 10:53:29 +02:00
Kilian Boute dae3d663b4 chore(hub): pin audit v1.0.0 2026-09-11 08:31:09 +00:00
Kilian Boute 3d5c0ecd3e refactor(hub): rename dispatcher to audit service 2026-09-10 08:35:40 +00:00
Kilian Boute 6454e8fa33 feat(hub): add configurable audit destinations 2026-09-09 12:23:36 +00:00
Kilian fbffe917bb Merge pull request #158 from kerberos-io/feat/classification-catalog
Add a configurable classification catalog
2026-09-09 11:57:59 +02:00
Kilian Boute 5c1d233c45 feat: add configurable classification catalog
Render deployer-defined classification keys, labels, and icons into a ConfigMap mounted by Hub API.
2026-09-08 14:27:22 +00:00
Kilian f9de70fce4 Merge pull request #157 from kerberos-io/feat/configurable-notification-expiry
feat: configure notification expiry
2026-09-07 16:20:01 +02:00
Kilian Boute 6c50f1d359 bump chart version 2026-09-07 16:16:52 +02:00
Kilian 9badb35126 Merge pull request #156 from kerberos-io/feat/marker-options-by-date
Expose selected-day marker option filtering
2026-09-07 16:00:48 +02:00
Kilian Boute fb26c6b755 feat(frontend): expose marker option date flag
Add a default-off Helm value for selected-day marker option filtering and inject it into primary and demo frontend deployments.
2026-09-07 12:35:47 +00:00
Kilian BouteandCopilot 3719ec0bbe feat: configure notification expiry
Expose the notification freshness cutoff in minutes through the Hub chart.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-07 12:34:51 +00:00
Kilian c262433aa1 Merge pull request #155 from kerberos-io/feat/audit-event-retention
Configure audit event retention
2026-09-04 16:45:31 +02:00
Kilian Boute f70aedc5d5 feat(hub): expose audit frontend flag 2026-09-04 14:37:46 +00:00
Kilian Boute 2d75d84509 feat(hub): configure audit event retention
Expose AUDIT_EVENT_RETENTION_DAYS for hub-cleanup with a 400-day default and document the setting.
2026-09-04 14:22:20 +00:00
Cédric Verstraeten c5231ca3cf Merge pull request #154 from kerberos-io/feature/add-feature-flag-proxy-enabled
feature/add-feature-flag-proxy-enabled
2026-09-01 16:18:52 +02:00
Cédric Verstraeten 776599cd30 Gate legacy Hub proxy with explicit flag
Add `kerberoshub.proxy.enabled` (default `false`) and require it in the vault proxy template so the legacy proxy only deploys when explicitly enabled. Update values and README wording to reflect the new enable/replica behavior, and bump the chart version to `0.134.1`.
2026-09-01 16:12:29 +02:00
Kilian 8629c701a1 Merge pull request #152 from kerberos-io/fix/remote-recording-default-enabled
fix(frontend): enable remote recording by default
2026-08-28 17:18:37 +02:00
Kilian Boute e3a38a007d fix(frontend): enable remote recording by default
Change the chart default to true, update the values reference, and bump the Hub chart to 0.134.0.
2026-08-28 14:53:01 +00:00
Kilian f097916ceb Merge pull request #151 from kerberos-io/feat/map-tile-overlay-config
Expose frontend map tile overlays
2026-08-28 16:12:52 +02:00
Kilian Boute 678d4d84ea feat(frontend): expose remote recording flag
Add a default-disabled live-view remote recording value, render it into normal and demo frontend Deployments, and document the chart option.
2026-08-28 14:02:42 +00:00
Kilian Boute 0deaf41420 feat(frontend): expose map tile overlays
Add optional XYZ overlay URL, API key, attribution, zoom, and opacity values to normal and demo frontend deployments and bump the Hub chart to 0.133.0.
2026-08-28 13:15:56 +00:00
Cédric Verstraeten 813b008955 Merge pull request #150 from kerberos-io/feature/add-monitor-log-level
feature/add-monitor-log-level
2026-08-28 13:55:55 +02:00
Cédric Verstraeten 32b71a82ca feat(monitor): add log level configuration for monitor 2026-08-28 11:51:46 +00:00
Kilian 7bbf4ae78c Merge pull request #149 from kerberos-io/fix/nest-projects-under-organisations
fix(frontend): align context feature flags
2026-08-24 13:02:18 +02:00
Kilian Boute 6f34bd4735 fix(frontend): align context feature flags
Inject organisation and project feature flags into the demo frontend deployment, document the nested hierarchy, and bump the chart to 0.132.0.
2026-08-24 10:51:20 +00:00
Kilian 24918922fd Merge pull request #148 from kerberos-io/bump-chart
bump-release
2026-08-24 11:28:29 +02:00
Kilian Boute 0fadcb74d0 bump-release 2026-08-21 17:38:54 +02:00
Cédric Verstraeten f834d9b8f5 Merge pull request #143 from kerberos-io/public-release-1786904956
A new public release - 1786904956
2026-08-21 17:36:29 +02:00
Kilian eefe96c679 Merge pull request #147 from kerberos-io/detection-source-flags
detection source flags
2026-08-21 17:32:59 +02:00
Kilian 93888e2855 Merge pull request #144 from kerberos-io/feature/add-org-feature-flags
add-org-feature-flags
2026-08-21 17:29:48 +02:00
Kilian Boute 759ac8dbf8 detection source flags 2026-08-21 17:28:25 +02:00
Cédric Verstraeten 4aaa70f121 Merge pull request #146 from kerberos-io/fix/cors-origin
fix/cors-origin
2026-08-20 22:10:48 +02:00
Cédric Verstraeten ad7ef4ac12 Add frontend URL to Hub API CORS
Initialize CORS origins with the primary frontend URL while retaining support for the legacy frontend URL. Bump the Hub chart to 0.130.0.
2026-08-20 22:09:19 +02:00
Cédric Verstraeten 5ad56f9730 Merge pull request #145 from kerberos-io/feature/add-refresh-tokens-config
feature/add-refresh-tokens-config
2026-08-20 21:44:32 +02:00
Cédric Verstraeten 91eb64a2f3 Update Chart.yaml 2026-08-20 21:35:04 +02:00
Cédric Verstraeten 55ae6cdd3e Add dynamic CORS origins and secure refresh cookie
Update the hub API Helm template to set `REFRESH_COOKIE_SECURE` automatically when the API schema is HTTPS, and generate `CORS_ALLOWED_ORIGINS` from configured frontend URLs (legacy URL, domain list, tenant wildcard domain, and demo URL). This keeps cookie behavior and CORS config aligned with deployment settings.
2026-08-20 21:33:32 +02:00
Kilian Boute f492c14336 add flags 2026-08-20 18:01:19 +02:00
uug4ai d4a13a4cff A new public release - 1786904956 2026-08-16 18:29:18 +00:00
Kilian 98cc8d4f2d Merge pull request #142 from kerberos-io/docs/workflow-source-alignment
docs(workflows): clarify config and database sources
2026-08-13 12:48:11 +02:00
Kilian Boute a5125eee69 docs(workflows): clarify definition sources 2026-08-13 10:23:21 +00:00
Cédric Verstraeten b2ff3e2e20 Merge pull request #141 from kerberos-io/fix/allow-replicas-proxy-default-to-zero
fix/allow-replicas-proxy-default-to-zero
2026-08-11 22:01:40 +02:00
Cédric Verstraeten 277ddde3b4 Disable proxy and reactivation services by default
Default replicas for the hub proxy and reactivation subscriptions services are now set to 0, allowing them to be disabled unless explicitly enabled. This updates the chart metadata and docs to reflect the new default behavior.
2026-08-11 21:51:37 +02:00
Cédric Verstraeten 7878be79d6 Merge pull request #140 from kerberos-io/feature/add-mongodb-tls-support
feature/add-mongodb-tls-support
2026-08-11 14:14:35 +02:00
Cédric Verstraeten 123bde292e Merge pull request #136 from kerberos-io/public-release-1786356403
A new public release - 1786356403
2026-08-11 14:09:02 +02:00
Cédric Verstraeten e76311872e feat(hub): update chart version to 0.127.0 and add MongoDB TLS configuration options 2026-08-11 12:06:27 +00:00
Cédric Verstraeten 0f2176822a feat(hub): add TLS support for MongoDB configuration 2026-08-11 12:05:43 +00:00
Kilian 8ca4c402f8 Merge pull request #139 from kerberos-io/feat/organisation-feature-flags
chore(hub): bump chart version to 0.126.2
2026-08-10 12:36:29 +02:00
Kilian 8d9b943100 chore(hub): bump chart version to 0.126.2 2026-08-10 10:34:08 +00:00
Kilian 3e10489251 Merge pull request #137 from kerberos-io/feat/organisation-feature-flags
Add organisation frontend feature flags
2026-08-10 12:26:01 +02:00
Kilian a7fd8d394f feat(hub): add organisation frontend flags
Expose disabled-by-default Helm values for organisation switching and creation, and map them to the Hub frontend runtime environment.
2026-08-10 10:17:59 +00:00
uug4ai c29647ec62 A new public release - 1786356403 2026-08-10 10:06:45 +00:00
Cédric Verstraeten 685b92e9cc Merge pull request #135 from kerberos-io/feature/add-mock-support
feature/add-mock-support
2026-08-05 22:42:36 +02:00
Cédric Verstraeten 01d1e6866a Bump chart version to 0.126.1 and add MoQ support in live view configuration 2026-08-05 20:31:28 +00:00
Kilian 8cf73bcf1d Merge pull request #134 from kerberos-io/KilianBoute-patch-2
Bump chart version to 0.126.0
2026-08-05 18:07:10 +02:00
Kilian 57f6ab6f3b Bump chart version to 0.126.0 2026-08-05 18:06:58 +02:00
Kilian 2ed7829391 Merge pull request #133 from kerberos-io/feat/workflow-stage-queue-catalog
workflow-stage-queue-catalog?
2026-08-05 17:50:44 +02:00
Kilian Boute 0c9726f21b changer 2026-08-05 17:50:18 +02:00
Kilian 9ae2e1fc86 Merge pull request #131 from kerberos-io/feat/workflow-stage-queue-catalog
Expose workflow stage queues to Hub API
2026-08-05 13:03:48 +02:00
Kilian Boute f3a9886053 feat: expose workflow stage queues to API 2026-08-03 13:59:09 +00:00
Cédric Verstraeten 933fedc080 Merge pull request #130 from kerberos-io/feature/splash-screen-feature-flag
feature/splash-screen-feature-flag
2026-07-31 15:35:02 +02:00
Cédric Verstraeten 2c7f6a89a3 Bump chart version to 0.124.0 2026-07-31 13:28:55 +00:00
Cédric Verstraeten 5af0ffab6c Add splash screen feature flag to frontend configuration 2026-07-31 13:18:06 +00:00
Kilian 0a3cf69c80 Merge pull request #129 from kerberos-io/chore/remove-private-redaction-service
chore(hub): remove private redaction service from public chart
2026-07-31 15:09:19 +02:00
Kilian Boute b8499c97e2 chore(hub): remove private redaction service from public chart
hub-pipeline-redaction is a private, client-specific worker. Remove its Deployment/Service template and the kerberospipeline.redaction values block (plus the generated README rows) from the public chart. Deployment is now documented in the private hub-pipeline-redaction repo.
2026-07-29 14:12:13 +00:00
Cédric Verstraeten 641dc7510c Merge pull request #128 from kerberos-io/fix/hide-frontend-categories
fix/hide-frontend-categories
2026-07-16 11:29:21 +02:00
Cédric Verstraeten 20f4fa24ba Bump chart version to 0.123.1 2026-07-16 09:24:21 +00:00
Cédric Verstraeten b793014f89 Rename media filter category feature environment variable to plural form 2026-07-16 09:23:46 +00:00
Kilian 348bab8f2b Merge pull request #127 from kerberos-io/KilianBoute-patch-2
Bump chart version to 0.123.0
2026-07-10 19:22:23 +02:00
Kilian 8a71bd2a05 Bump chart version to 0.123.0 2026-07-10 19:22:05 +02:00
Kilian 611ddec1f1 Merge pull request #126 from kerberos-io/KilianBoute-patch-2
Bump chart version to 0.122.0
2026-07-10 19:20:23 +02:00
Kilian 34a285666e Bump chart version to 0.122.0 2026-07-10 19:20:09 +02:00
Kilian 59cf74ba66 Merge pull request #125 from kerberos-io/feat/hub-i18n-run-workflow-redact
feat(i18n): add Run workflow and Redact faces case UI strings
2026-07-10 19:10:52 +02:00
Kilian Boute 020f2d969d feat(i18n): add Run workflow and Redact faces case UI strings
Adds the en.json translations for the case Run-workflow dialog
(title/subtitle/labels/empty/submit and the runWorkflow menu item)
and the Redact faces attachment action (k14).
2026-07-10 17:10:14 +00:00
Kilian eeb29e8cac Merge pull request #124 from kerberos-io/feat/hub-api-workflow-definitions-env
feat(hub-api): inject WORKFLOW_DEFINITIONS env so config workflows surface in the API
2026-07-10 19:09:13 +02:00
Kilian Boute 1ec2be4cc7 feat(hub-api): inject WORKFLOW_DEFINITIONS env so config workflows surface in the API
hub-api reads WORKFLOW_DEFINITIONS (the same enabled definition set the
workflows engine consumes, assembled via kerberoshub.workflows.workflowDefinitions)
read-only to surface config workflows alongside DB/user workflows. Previously
only the engine received this env, so hub-api returned an empty config-workflow
list and manual/on-demand config workflows never appeared in GET /workflows.
2026-07-10 17:06:41 +00:00
Kilian b9166ea1ed Merge pull request #123 from kerberos-io/fix/hub-stage-service-separator
fix(hub-stage): render valid YAML when multiple stage workers are enabled
2026-07-08 15:05:56 +02:00
Kilian Boute be2290075c fix(hub-stage): add newline before doc separator so multiple stage workers render valid YAML
The generic stage-worker loop in templates/kerberos-pipeline/hub-stage.yaml
closed each iteration with `{{- end }}`, whose left-trim stripped the newline
after the Service's final line (`app: hub-<name>`). With a single enabled stage
worker this was latent, but with two or more the next worker's `---` separator
was glued onto the previous Service's last line (e.g. `app: hub-loitering---`),
merging two resources into one malformed document.

In production this surfaced as ArgoCD "one or more synchronization tasks are not
valid" once objecttracking was enabled alongside anpr and loitering (3 stage
workers -> 2 glued boundaries: anpr->loitering and loitering->objecttracking).

Fix: emit a trailing newline before the next document separator by using
`{{ end -}}` for the per-service if-close. Rendering the hub chart with the
production values now yields 58 valid documents (previously 56, of which 2 were
Service+Deployment glued together) and 0 malformed separators. Bump chart to
0.121.1 so the fix can be published to charts.kerberos.io.
2026-07-08 12:59:28 +00:00
Kilian cb47ad3f12 Merge pull request #122 from kerberos-io/feat/workflow-definitions-rendering
feat(hub): render WORKFLOW_DEFINITIONS for the workflows engine
2026-07-07 15:09:54 +02:00
Kilian Boute 42bd87d16d feat(hub): render WORKFLOW_DEFINITIONS for the workflows engine
Replace the flat PIPELINE_STAGE_REGISTRY with per-workflow
WORKFLOW_DEFINITIONS, matching hub-workflows' definitions engine:

- _workflows-helpers.tpl: stageRegistry -> workflowDefinitions. Emits a
  JSON array of named workflow objects (name, enabled, source=config,
  triggers defaulting to a bare automatic trigger, and the executable
  stages), one per enabled kerberoshub.workflows.definitions entry. Each
  stage's queue is still taken from the matching services.<operation> so
  dispatch and consume cannot drift.
- hub-workflows.yaml: set WORKFLOW_DEFINITIONS instead of
  PIPELINE_STAGE_REGISTRY.
- hub-stage.yaml: render a worker for every enabled services.<name> other
  than the engine itself (decoupled from routing), and pass through any
  services.<name>.env as container env for per-worker tuning.
- values.yaml: workflows.stages -> workflows.definitions (map keyed by
  workflow name) with an object-tracking + loitering worked example and
  updated docs.

Chart 0.120.0 -> 0.121.0. helm lint + template validated.
2026-07-07 13:05:20 +00:00
Kilian 0ed38add2e Merge pull request #121 from kerberos-io/feat/hls-live-transport-toggle
feat(hub): add hlsEnabled toggle; replace anpr example with loitering
2026-06-25 18:43:14 +02:00
Kilian c8c070d51e chore(hub): bump chart version to 0.120.0 2026-06-25 16:36:52 +00:00
Kilian 0e21755bb8 feat(hub): add hlsEnabled toggle; replace anpr example with loitering
- Add kerberoshub.frontend.features.liveview.hlsEnabled (default true) and
  wire it as FEATURE_HLS_ENABLED on hub-frontend and hub-frontend-demo. When
  'false' the front-end removes the HLS live-transport option.
- Replace the bundled anpr example stage/worker with a commented-out
  hub-loitering example; the chart now ships no enabled custom stage by
  default (stages are values-only, opt-in).
- Update the generic hub-stage comment to the loitering example.
- Make the queue-consistency check self-contained: it synthesises a neutral
  throwaway stage ('queuecheck') via --set instead of relying on a bundled
  example worker.

NOTE: Chart.yaml version intentionally not bumped — repo version/tag state is
already inconsistent (Chart.yaml 0.117.0 vs tags up to hub-0.119.0); pick the
next version at release time.
2026-06-25 16:33:16 +00:00
Kilian 8781ede494 Merge pull request #120 from kerberos-io/fix/disable-email-template-value
fix(hub): align disabled email template value with module name
2026-06-24 13:42:24 +02:00
Kilian Boute 82e3da78dd fix(hub): align disabled email template value with module name
The notification module's GetTemplate only recognizes the template name "disable" (disable.go / disable template file), but email.templates.disabled was set to "disabled". A consumer passing DISABLED_TEMPLATE to GetTemplate would get an empty body. Set the value to "disable" so it resolves correctly.
2026-06-24 11:24:34 +00:00
Kilian 30bbd97b6d Merge pull request #119 from kerberos-io/feat/case-share-email-template
feat(hub): add case-share email template + env wiring
2026-06-24 11:02:21 +02:00
KilianandCopilot Autofix powered by AI b78a2246ee Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-06-24 10:09:06 +02:00
Kilian Boute 33b41cee0e otp share template 2026-06-23 17:13:18 +02:00
Kilian Boute 3a464eeb8e feat(hub): add case-share email template + env wiring
Adds CASE_SHARE_TEMPLATE/CASE_SHARE_TITLE env on hub-api (driven by
email.templates.caseShare/caseShareTitle), the matching values defaults, and
the share_case custom-layout template (html/txt) so the case-share invitation
renders with its dedicated white-label template.
2026-06-23 13:46:54 +00:00
Kilian d599befeaf Merge pull request #118 from kerberos-io/fix/otel-gating-and-workflows-queue
Gate OTel env vars behind opentelemetry.enabled and align workflows queue name
2026-06-19 13:00:21 +02:00
Kilian Boute 9ea9016bfa Gate OTel env vars behind opentelemetry.enabled and align workflows queue name
- Wrap OpenTelemetry tracing env vars in '{{- if .Values.opentelemetry.enabled }}' across hub-api and all pipeline templates so disabled tracing injects no OTEL_* vars.

- Fix the OTLP collector endpoint default to the HTTP port (http://otel-collector:4318); the services use the OTLP HTTP exporter, and a scheme-less/4317 value defaults to TLS and fails against a plaintext collector.

- Rename the workflows queue from 'kcloud-workflows-queue' to 'hub-workflows-queue' and pass WORKFLOWS_QUEUE to the analysis pipeline so analysis and the workflows engine always agree on the queue name.
2026-06-18 15:50:59 +00:00
Kilian 895c190e20 Merge pull request #117 from kerberos-io/workflows->-remove-kind
Remove 'kind' attribute from workflows and update related comments fo…
2026-06-17 12:22:51 +02:00
Kilian Boute f4051f7e6a Remove 'kind' attribute from workflows and update related comments for clarity 2026-06-17 10:07:25 +00:00
Cédric Verstraeten 807369ef01 Merge pull request #116 from kerberos-io/feature/live-view-mode
feature/live-view-mode
2026-06-17 11:56:33 +02:00
Cédric Verstraeten 19cf677a56 Bump hub chart version to 0.117.0
Increment charts/hub Chart.yaml version from 0.116.0 to 0.117.0 to reflect an updated chart release. This follows semantic versioning for chart/template changes.
2026-06-16 17:46:58 +02:00
Cédric Verstraeten 7b39949e5b Add live stream mode env and default value
Expose FEATURE_LIVE_STREAM_MODE environment variable in both hub-frontend and hub-frontend-demo templates, sourcing from .Values.kerberoshub.frontend.features.liveview.liveStreamMode. Add a new liveStreamMode default in charts/hub/values.yaml ("webrtc") to control the transport for LIVE (HD) mode (options: "webrtc" (default) or "hls"). This enables configuring live stream transport without modifying templates.
2026-06-16 17:09:54 +02:00
Kilian 3752c0396e Merge pull request #115 from kerberos-io/default-i18n-en.json-file
Add default i18n en.json file
2026-06-16 09:57:38 +02:00
Kilian 0d55fa5d2f Add default i18n en.json file 2026-06-16 09:49:33 +02:00
Cédric Verstraeten 2949db0a03 Merge pull request #114 from kerberos-io/public-release-1781595581
A new public release - 1781595581
2026-06-16 09:48:31 +02:00
uug4ai 662a2c6a67 A new public release - 1781595581 2026-06-16 07:39:43 +00:00
Cédric Verstraeten 7cf273911e Merge pull request #113 from kerberos-io/KilianBoute-patch-2
Bump chart version to 0.116.0
2026-06-16 09:39:40 +02:00
Kilian 20f1de461d Bump chart version to 0.116.0 2026-06-15 21:26:40 +02:00
Kilian a237f7b4d6 Merge pull request #112 from kerberos-io/KilianBoute-patch-2
Bump chart version from 0.113.0 to 0.115.0
2026-06-15 18:29:07 +02:00
Kilian fae8b028ad Bump chart version from 0.113.0 to 0.115.0 2026-06-15 18:28:54 +02:00
Kilian 31ec7cb6af Merge pull request #111 from kerberos-io/translation-overriding
Translation override support
2026-06-15 18:26:36 +02:00
Kilian Boute 424053f2ce add translation file override support 2026-06-15 13:39:23 +00:00
Kilian Boute 7c17a99240 add translation file override support 2026-06-15 13:39:10 +00:00
Kilian dafba78c94 Merge pull request #110 from kerberos-io/Workflows->-standalone-setup
Refactor workflows configuration to use services structure in hub-sta…
2026-06-12 16:58:48 +02:00
Kilian Boute dc48269807 Refactor workflows configuration to use services structure in hub-stage and hub-workflows templates; update values.yaml for consistency 2026-06-12 14:50:38 +00:00
Kilian eff71c4e24 Merge pull request #109 from kerberos-io/Workflows->-standalone-setup
Workflows > standalone setup
2026-06-12 15:17:20 +02:00
Kilian Boute 642676fcf7 Remove deprecated pipe-anpr and pipe-workflows templates; add generic hub-stage and hub-workflows templates for improved workflow management 2026-06-12 11:51:28 +00:00
Kilian Boute 25bb6d5fdc Enhance workflow stage configuration with needsMode and kind attributes in helpers and values.yaml 2026-06-11 15:34:59 +00:00
Kilian Boute 20b92ffddd Update comments for workflows integration in pipe-analysis and values.yaml 2026-06-09 15:00:02 +00:00
Kilian Boute bb4cc53d90 Add workflows support to pipe-analysis and create pipe-workflows template 2026-06-08 16:20:38 +00:00
Kilian Boute 7b920c3f0e Add hub-workflows configuration to values.yaml 2026-06-08 12:26:15 +00:00
57 changed files with 3825 additions and 239 deletions
@@ -0,0 +1,36 @@
name: Workflows queue and identity 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. Also validates stable workflow identities and API/engine definition
# consistency. Pure `helm template` render checks, no cluster required.
on:
workflow_dispatch:
pull_request:
paths:
- 'charts/hub/**'
- 'scripts/check-workflows-queue-consistency.sh'
- 'scripts/check-workflow-configuration.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 workflow queue and identity consistency
run: ./scripts/check-workflows-queue-consistency.sh charts/hub
- name: Check workflow configuration and classification catalog
run: bash scripts/check-workflow-configuration.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.113.0
version: 0.150.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
+388 -17
View File
@@ -1,6 +1,6 @@
# Kerberos Hub
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 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 Interface](assets/images/hub-dashboard.png)
@@ -31,10 +31,150 @@ Uninstall the Kerberos Hub chart
helm uninstall hub -n kerberos-hub
### Sprite Resolution
`kerberospipeline.sprite.width` and `height` set the pixel dimensions of each
sprite tile, not the complete sheet. Preserve a **16:9 aspect ratio**: for
example, `240x135` (default), `480x270`, or `640x360`.
Deploy the dimension-aware Hub frontend, Hub API, analysis worker, sprite worker,
notification worker, and export worker before increasing resolution. Release the updated shared Go
models and update the Hub API/export worker dependency pins before building those
services. Newly generated sprites carry their own tile dimensions; existing
sprites without dimensions retain the `240x135` fallback and need no migration.
Do not change a global fallback to match the new size. Increasing both dimensions
by two produces four times as many pixels, increasing decoded memory and usually
storage and bandwidth. Hover previews retain their display size; fullscreen
scrubbing uses the higher-resolution tiles.
```yaml
kerberospipeline:
sprite:
width: "480"
height: "270"
```
### Optional Argo CD pipeline ordering
Each listed pipeline service accepts `deploymentAnnotations` in its existing
values section. The default is `{}`, so installing or upgrading the chart alone
does not enable ordering. Commented examples appear beside the service settings
in `values.yaml`; replace the empty map rather than adding duplicate YAML keys.
Annotations render on Deployment `metadata`, not on pod templates or Services.
The inline examples use the following order, leaving gaps of ten:
| Wave | `kerberospipeline` keys |
| --- | --- |
| 10 | `event` |
| 20 | `notify` |
| 30 | `throttler` |
| 40 | `analysis` |
| 50 | `thumbnail`, `sprite`, `dominantColor`, `counting` |
| 60 | `sequence` |
| 70 | `monitor` |
Enable the annotation in each service's existing environment values section.
No separate example values file is required. These annotations do not change
image tags or enable disabled services such as sprite.
Deploy the compatible analysis bridge before replying workers, and select the
fixed counting release; waves do not enforce minimum compatible image versions.
The chart's default image tags are not a compatibility-tested rollout bundle.
Argo CD applies lower waves first and waits for sync/health before proceeding
within the same Application. Unannotated resources remain at wave zero. A
single-service image update does not restart earlier services, but an unhealthy
earlier wave can block it. Separate Applications and external classifier/workflow
workers require their own coordination. Helm itself does not enforce these waves.
Waves are not a substitute for message compatibility, readiness checks, or
stopping old RabbitMQ consumers. This example does not add health probes or
change the Deployment rolling-update strategy.
### Deployment-global workflow identity
`kerberoshub.workflows.definitions` is a map keyed by workflow name. Enabled
definitions are passed as the same `WORKFLOW_DEFINITIONS` JSON to Hub API and the
workflow engine. These deployment-global, ops-managed workflows are read-only
through the API; manage them in Helm values. There are no per-project on/off
overrides.
Supply an explicit, stable `id` for new definitions:
```yaml
kerberoshub:
workflows:
enabled: true
definitions:
tracking-workflow:
id: "ec7e72bc4cedd32018e1f92a"
enabled: true
stages:
- operation: objecttracking
```
The worker and its queue must also be configured under
`kerberoshub.services.objecttracking`. As before, omitted triggers default to a
single automatic trigger and disabled definitions are not sent to either service.
Identity is separate from mutable names and content versions. Keep an explicit
ID unchanged when renaming or revising a workflow; changing it creates an
independent identity, not a new version of the existing workflow. The chart
does not generate random IDs or introduce a version field.
For compatibility, an omitted `id` remains omitted from the rendered JSON. The
backend derives the effective ID as the first 24 hexadecimal characters of
SHA-256 over the **exact map key**, including case and whitespace. Before
renaming an existing ID-less definition, pin its current derived ID explicitly
to preserve references and history. For example:
```sh
printf '%s' 'tracking-workflow' | sha256sum | cut -c1-24
# ec7e72bc4cedd32018e1f92a
```
The chart ships an empty definitions map. Its commented `tracking-workflow` and
`vlm-workflow` examples pin their current name-derived IDs for compatibility.
Explicit IDs must be quoted, non-zero, 24-character hexadecimal strings (Mongo
ObjectIDs); uppercase is accepted and normalized to lowercase in JSON. Empty,
null, all-zero, malformed, and non-string IDs fail Helm rendering with the
definition name and validation requirement. Duplicate effective IDs also fail,
naming both definitions and the colliding ID, whether explicit/explicit or
explicit/name-derived. Validation includes disabled definitions and runs even
when the engine is disabled, so identities remain reserved. Omit the `id` key
entirely to use the legacy fallback; invalid explicit IDs never fall back.
Run the render-only queue and identity checks (Helm and Python 3 required):
```sh
./scripts/check-workflows-queue-consistency.sh charts/hub
```
### Parameters
Below all configuration options and parameters are listed.
When upgrading to `0.144.0`, move `kerberoshub.audit.enabled` and
`kerberoshub.audit.intake` to `audit.enabled` and `audit.intake`. Consumer
workload settings remain under `kerberoshub.audit`.
Auditing is globally disabled by default. Setting `audit.enabled: true` deploys
the audit consumer and enables every supported producer by default. A producer
is enabled only when both the global switch and its service switch are true; set
the service's `audit.enabled` value to `false` only when that service must opt
out.
```yaml
audit:
enabled: true
kerberospipeline:
notify:
audit:
enabled: false # Optional per-service opt-out.
```
| Name | Description | Value |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----- |
| `license` | The license key you received from support@kerberos.io. If not available request one. | `"L/+DAwEBB2xpY2Vuc2UB/4QAAQIBB1BheWxvYWQBCgABCVNpZ25hdHVyZQEKAAAA/gMk/4QB/gEZ/8sQACxnaXRodWIuY29tL3V1Zy1haS9odWItbGljZW5zZS9tb2RlbHMuTGljZW5zZX8DAQEHTGljZW5zZQH/gAABDAECSWQB/4IAAQNLZXkBDAABB0NvbXBhbnkBDAABB0V4cFRpbWUBBAABBERheXMBBAABB0NhbWVyYXMBBAABBVNpdGVzAQQAAQZWYXVsdHMBBAABCk1lZGlhTGltaXQBBAABCVBlcnBldHVhbAECAAEGQWN0aXZlAQIAAQlJcEFkZHJlc3MBDAAAABj/gQEBAQhPYmplY3RJRAH/ggABBgEYAAAy/4AvAQwAAAAAAAAAAAAAAAACDGZyZWUtbGljZW5zZQH81iZi6gH+AtoBEAEUARQDAQAB/gIAfeXxQb6kaPfAgOWeSAE6qEiQviFD6sciNmNfMel1mEL53FeV0GQe4cYBip9wyJag35az8A1yppxSymZD5V4my2FckyN2zmEW4E2sO/v+8eKepiAGYEzrKtfNCLxdWLmrHd0zjYQ3qk+PNfoPyzCOefeulw3aFsqBlzg9wDkF8cRx6tUW0qNTzki6sFGOuLoxS49cWqsftAvZmt+CRWa8u0VArIAjOpywN0RIZCkEYzp5RYF3LSVyWYEyvVhjE19DDnevzpJyCHRsIHTRcpTQkhboeapOdlEz8cx+PaOvxktN8hBWceTAH+nw96FARG7y6Cpjw3xo+NV1xb0tvRXGaGoK77JnErKLhd/haXji98rGvMakDt18WSbQfTVS84+Fw+/gKGsW3uS3fROAaZw1kZj4PgsEPZDvbVkCVyuS0O87UoYqpxH8S4by8cTF3wDP7FwyIRZbEbYjN2wzHSmlADYOBtdsdb1VGm7wvtB85vML9n7ZSlIpJdqfci06mGks102mDyG2LRMhUEvpUW3D/weErIvj2WiAwf6r0EUj+LO8VmAsYkME9da7FXEN6Vg/5f1u485LOpYki332RaDOhDn2eMG9DVb/HnmQSFagX+XXc/QwfsWiehCgKYGk4jQpyklTqoGu8BAt6Sm8CaoH8ngZ3cHLQT5DcZElV36/N/wA"` |
@@ -53,6 +193,11 @@ Below all configuration options and parameters are listed.
| `mongodb.password` | MongoDB user password, by default `'yourmongodbpassword'` | `"yourpassword"` |
| `mongodb.retryWrites` | Enable or disable MongoDB retryable writes. | `"true"` |
| `mongodb.flavor` | Backend engine flavor: `"mongodb"` (native MongoDB / Atlas) or `"documentdb"` (AWS DocumentDB). The `documentdb` flavor disables features DocumentDB does not support (geospatial queries/indexes, complex `$lookup` pipelines). When set to `documentdb`, also set `mongodb.retryWrites: "false"`. | `"mongodb"` |
| `mongodb.tls.enabled` | Enable TLS for MongoDB connections. When `mongodb.uri` is set, the chart appends missing `tls=true` and `tlsCAFile` query parameters. | `false` |
| `mongodb.tls.existingSecret` | Existing Kubernetes Secret containing the MongoDB CA bundle. The Secret is mounted into every workload that consumes `mongodb-config`. | `""` |
| `mongodb.tls.caFileName` | Key and filename of the CA bundle in `mongodb.tls.existingSecret` (for AWS DocumentDB, typically `global-bundle.pem`). | `""` |
| `mongodb.tls.mountPath` | Read-only directory where the MongoDB CA Secret is mounted. | `"/etc/mongodb/tls"` |
| `mongodb.tls.insecureSkipVerify` | Skip MongoDB certificate and hostname verification. This is insecure and intended only for local testing. | `false` |
| `mqtt.host` | MQTT (Vernemq) hostname. | `"mqtt.yourdomain.com"` |
| `mqtt.port` | MQTT (Vernemq) port for WSS (secure sockets), by default `'8443'`. | `"8443"` |
| `mqtt.protocol` | MQTT (Vernemq) protocol, by default `'wss'`. | `"wss"` |
@@ -66,6 +211,12 @@ Below all configuration options and parameters are listed.
| `rabbitmq.username` | RabbitMQ username, by default `'yourusername'` | `"yourusername"` |
| `rabbitmq.password` | RabbitMQ password, by default `'yourpassword'` | `"yourpassword"` |
| `rabbitmq.exchange` | RabbitMQ exchange, by default `''` | `""` |
| `audit.enabled` | Deploy the Hub audit consumer and act as the master switch for all audit producers. | `false` |
| `audit.intake.queue` | Shared durable RabbitMQ queue used by audit producers and the audit consumer. | `"hub-audit-events"` |
| `audit.intake.deadletterQueue` | Shared RabbitMQ queue used for invalid audit events and exhausted persistence retries. | `"hub-audit-dead-letter"` |
| `audit.intake.maxRetries` | Maximum MongoDB persistence retries before an intake event is dead-lettered. | `10` |
| `audit.intake.prefetchCount` | Maximum unacknowledged intake events per audit replica. | `20` |
| `audit.intake.persistTimeout` | Timeout for one MongoDB persistence attempt. | `"10s"` |
| `kafka.broker` | Kafka brokers, by default `'kafka1.yourdomain.com:9094,kafka2.yourdomain.com:9094'` | `"kafka1.yourdomain.com:9094"` |
| `kafka.username` | Kafka username, by default `'yourusername'` | `"yourusername"` |
| `kafka.password` | Kafka password, by default `'yourpassword'` | `"yourpassword"` |
@@ -116,10 +267,16 @@ Below all configuration options and parameters are listed.
| `kerberoshub.api.pullPolicy` | The Docker registry pull policy. | `"IfNotPresent"` |
| `kerberoshub.api.tag` | The Docker image tag/version. | `"v1.9.8"` |
| `kerberoshub.api.replicas` | The number of pods/replicas running for the Kerberos Hub API deployment. | `2` |
| `kerberoshub.api.audit.enabled` | Enable audit publishing from Hub API when global auditing is enabled. | `true` |
| `kerberoshub.api.logLevel` | Log verbosity level for `kerberoshub.api`. | `"info"` |
| `kerberoshub.api.jwtSecret` | A secret that is for generating JWT tokens. | `"this-is-a-secret-please-change-to-random-string"` |
| `kerberoshub.api.schema` | The protocol to serve the Kerberos Hub API, `'http'` or `'https'`. | `"https"` |
| `kerberoshub.api.url` | The Kerberos Hub API ingress to access the API. | `"api.yourdomain.com"` |
| `kerberoshub.api.disableAgentHeartbeat` | Temporarily stop serving the polled `GET /devices` list (hub-api returns `204` without database reads). Devices still load once through `/session/bootstrap`; online/offline status stops refreshing. | `false` |
| `kerberoshub.api.mediaProxy.enabled` | Serve media through hub-api (`/media/proxy`) instead of handing out Vault/storage URLs, hiding the storage endpoint from clients. | `false` |
| `kerberoshub.api.mediaProxy.publicUrl` | Public hub-api base URL used in proxied media URLs. Defaults to `{schema}://{url}`. | `""` |
| `kerberoshub.api.mediaProxy.key` | Key used to encrypt proxied media URLs (`openssl rand -hex 32`). Defaults to a key derived from `jwtSecret`. | `""` |
| `kerberoshub.api.mediaProxy.ttl` | Lifetime of proxied media URLs (Go duration, max `168h`). | `"24h"` |
| `kerberoshub.api.resources.requests.memory` | Memory request for `kerberoshub.api`. | `"100Mi"` |
| `kerberoshub.api.resources.requests.cpu` | CPU request for `kerberoshub.api`. | `"250m"` |
| `kerberoshub.api.resources.limits.memory` | Memory limit for `kerberoshub.api`. | `"100Mi"` |
@@ -197,33 +354,64 @@ Below all configuration options and parameters are listed.
| `kerberoshub.frontend.caseFilterAssigneesDefault` | Default assignee filter behavior for cases in the frontend. | `"false"` |
| `kerberoshub.frontend.features.case.enabled` | Enable or disable the case feature in the frontend. | `"true"` |
| `kerberoshub.frontend.features.darkModeEnabled` | Enable or disable dark mode in the frontend. | `"true"` |
| `kerberoshub.frontend.features.splashScreen.enabled` | Enable or disable the pre-bootstrap splash screen and reveal delay. | `"true"` |
| `kerberoshub.frontend.features.landingPage` | Frontend landing page configuration. | `"/dashboard"` |
| `kerberoshub.frontend.features.i18n.enabled` | Enable or disable the runtime language switcher in the front-end. When `"false"`, `defaultLanguage` is forced and users cannot change it. | `"true"` |
| `kerberoshub.frontend.features.i18n.defaultLanguage` | Default language code used by the front-end (e.g. `en`, `nl`, `pl`, `tr`, `fr`, `sv`, `de`). | `"en"` |
| `kerberoshub.frontend.features.workflows.enabled` | Enable or disable the workflows feature in the frontend. | `"false"` |
| `kerberoshub.frontend.features.audit.enabled` | Enable or disable the owner/admin Audit events page and navigation link. | `"true"` |
| `kerberoshub.frontend.features.organisations.enabled` | Enable or disable the organisation feature family, including projects. When empty, child groups apply independently. | `""` |
| `kerberoshub.frontend.features.organisations.switcherEnabled` | Enable or disable the organisation dropdown and switching. The current organisation remains visible when disabled. | `"false"` |
| `kerberoshub.frontend.features.organisations.creationEnabled` | Enable or disable organisation creation. Requires organisation switching to be enabled. | `"false"` |
| `kerberoshub.frontend.features.organisations.settingsEnabled` | Enable or disable the organisation identity link to organisation settings. | `"false"` |
| `kerberoshub.frontend.features.projects.enabled` | Fallback project group switch used when the organisations umbrella is unset. | `""` |
| `kerberoshub.frontend.features.projects.switcherEnabled` | Enable or disable the read-only project dropdown. | `"false"` |
| `kerberoshub.frontend.features.projects.creationEnabled` | Reserved for the project creation UI. | `"false"` |
| `kerberoshub.frontend.features.projects.settingsEnabled` | Reserved for the project settings UI. | `"false"` |
| `kerberoshub.frontend.features.map.tileUrlLight` | Tile URL used by the map in light mode. | `"https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png"` |
| `kerberoshub.frontend.features.map.tileUrlDark` | Tile URL used by the map in dark mode. | `"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png"` |
| `kerberoshub.frontend.features.map.attribution` | Attribution text displayed on the map tiles. | `"&copy; <a href='https://www.openstreetmap.org/copyright' target='_blank'>OpenStreetMap</a>"` |
| `kerberoshub.frontend.features.map.overlayTileUrl` | Optional XYZ overlay URL supporting `{z}`, `{x}`, `{y}`, and `{apiKey}`. Empty disables the overlay. | `""` |
| `kerberoshub.frontend.features.map.overlayApiKey` | Optional browser-visible API key substituted for `{apiKey}` in the overlay URL. | `""` |
| `kerberoshub.frontend.features.map.overlayAttribution` | Attribution text displayed when the overlay is enabled. | `""` |
| `kerberoshub.frontend.features.map.overlayMinZoom` | Minimum zoom level for overlay tiles. | `"0"` |
| `kerberoshub.frontend.features.map.overlayMaxZoom` | Maximum zoom level for overlay tiles. | `"19"` |
| `kerberoshub.frontend.features.map.overlayOpacity` | Overlay opacity from `0` to `1`. | `"1"` |
| `kerberoshub.frontend.features.liveview.defaultStreamMode` | Liveview behavior setting: `defaultStreamMode`. | `"SD"` |
| `kerberoshub.frontend.features.liveview.liveStreamMode` | Transport backing LIVE mode: `webrtc`, `hls`, or `moq`. | `"webrtc"` |
| `kerberoshub.frontend.features.liveview.hlsEnabled` | Offer HLS as a selectable LIVE transport. | `"true"` |
| `kerberoshub.frontend.features.liveview.moqEnabled` | Offer MoQ as a selectable LIVE transport. | `"false"` |
| `kerberoshub.frontend.features.liveview.remoteRecordingEnabled` | Show the manual REC control and recording-state overlay in live views. | `"true"` |
| `kerberoshub.frontend.features.liveview.deviceStatusEnabled` | Show connectivity and recent-motion status in live view tile headers. | `"true"` |
| `kerberoshub.frontend.features.liveview.moqRelayUrl` | WebTransport URL of the MoQ relay. | `"https://relay.uug.ai/anon"` |
| `kerberoshub.frontend.features.liveview.moqBroadcastPrefix` | Prefix used to build MoQ broadcast names. | `"devices"` |
| `kerberoshub.frontend.features.liveview.paginationMode` | Liveview behavior setting: `paginationMode` (`scroll`, `numbered` or `maxStreams`). | `"scroll"` |
| `kerberoshub.frontend.features.liveview.pageSize` | Liveview behavior setting: `pageSize` (max streams shown per page in `numbered` mode). | `"6"` |
| `kerberoshub.frontend.features.liveview.maxStreams` | Liveview behavior setting: `maxStreams`. | `"-1"` |
| `kerberoshub.frontend.features.chart.colors` | Ordered colors shared by chart series and marker detection overlays. | `["#84569f", "#3ba7ff", "#ff5c8a", "#4ad991", "#ffb84d", "#ff7043"]` |
| `kerberoshub.frontend.features.chart.colorChartSelectionFill` | Fill color for chart selection regions. | `"rgba(132, 86, 159, 0.07)"` |
| `kerberoshub.frontend.features.chart.colorChartSelectionStroke` | Stroke color for chart selection regions. | `"rgba(132, 86, 159, 0.4)"` |
| `kerberoshub.frontend.features.chart.colorChartGridStroke` | Stroke color for chart grid lines. | `"rgba(0, 106, 255, 0.18)"` |
| `kerberoshub.frontend.features.devices.hideAgent` | Hide agent controls in the devices section of the frontend. | `"false"` |
| `kerberoshub.frontend.features.devices.analyticsEnabled` | Enable or disable the Analytics tab on device detail pages. | `"true"` |
| `kerberoshub.frontend.features.devices.developerToolsEnabled` | Enable or disable owner-only live Agent logs and terminal access on device detail pages. | `"true"` |
| `kerberoshub.frontend.features.dashboard.sitesFullscreenEnabled` | Enable or disable the fullscreen Sites map on the Dashboard. | `"true"` |
| `kerberoshub.frontend.features.media.filter.date.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.date`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.sites.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.sites`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.groups.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.groups`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.devices.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.devices`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.objectDetection.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.objectDetection`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.star.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.star`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.analysis.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.analysis`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.region.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.region`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.sort.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.sort`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.category.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.category`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.markers.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.markers`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.markerOptionsByDate.enabled` | Limit marker, category, event, and tag filter options to the selected recordings day. | `"false"` |
| `kerberoshub.frontend.features.media.filter.markerCategoryPriority.enabled` | Order marker filter options from configured categories before other markers. | `"false"` |
| `kerberoshub.frontend.features.media.filter.markerCategoryPriority.categories` | Ordered exact, case-sensitive marker category names to prioritize. | `[]` |
| `kerberoshub.frontend.features.media.filter.events.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.events`. | `"true"` |
| `kerberoshub.frontend.features.liveview.defaultStreamMode` | Default live stream mode: `SD` or `HD`. | `"SD"` |lter.tags`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.tags.enabled` | Enable or disable `kerberoshub.frontend.features.media.filter.tags`. | `"true"` |
| `kerberoshub.frontend.features.media.filter.defaultView` | Default view for the media page: `timeline` or `grid`. | `"timeline"` |
| `kerberoshub.frontend.features.floorplan.enabled` | Enable or disable `kerberoshub.frontend.features.floorplan`. | `"true"` |
| `kerberoshub.frontend.features.floorplan.colorDeviceActive` | Color customization for `floorplan` in the frontend. | `"hsla(131, 31%, 52%, 1)"` |
@@ -240,6 +428,7 @@ Below all configuration options and parameters are listed.
| `kerberoshub.frontend.features.floorplan.colorTrackBoxDrawing` | Color customization for `floorplan` in the frontend. | `"hsla(204, 100%, 50%, 1)"` |
| `kerberoshub.frontend.features.floorplan.colorTrackBoxControlsDelete` | Color customization for `floorplan` in the frontend. | `"hsla(219, 100%, 94%, 1)"` |
| `kerberoshub.frontend.features.faceRedaction.enabled` | Enable or disable `kerberoshub.frontend.features.faceRedaction`. | `"false"` |
| `kerberoshub.frontend.features.faceRedaction.classifierTracksEnabled` | Make classifier-generated tracks available in the redaction modal. | `"true"` |
| `kerberoshub.support.enabled` | Enable or disable in-app support features. | `false` |
| `kerberoshub.oauth2Proxy.enabled` | Enable or disable `kerberoshub.oauth2Proxy`. | `false` |
| `kerberoshub.oauth2Proxy.github.clientId` | Client ID used by `kerberoshub.oauth2Proxy.github`. | `"github-client-id"` |
@@ -271,9 +460,23 @@ Below all configuration options and parameters are listed.
| `kerberoshub.cleanup.globalPassIntervalHours` | Minimum hours between global cleanup passes. | `"0"` |
| `kerberoshub.cleanup.globalPassDeleteBudget` | Max documents deleted during a global pass. | `"0"` |
| `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.auditEventRetentionDays` | Deployment-wide audit-event retention in days, independent of recording plans, inactive-account cleanup, and the optional global pass. Set to `"0"` or a negative value to keep audit events indefinitely. | `"400"` |
| `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 Agents. | `"ghcr.io/uug-ai/hub-monitor-device"` |
| `kerberoshub.audit.repository` | Hub audit service container image repository. | `"ghcr.io/uug-ai/hub-audit"` |
| `kerberoshub.audit.pullPolicy` | Hub audit service image pull policy. | `"IfNotPresent"` |
| `kerberoshub.audit.tag` | Hub audit service image tag. | `"v1.0.1"` |
| `kerberoshub.audit.replicas` | Number of audit service replicas. Per-destination MongoDB leases prevent concurrent delivery. | `1` |
| `kerberoshub.audit.dispatchInterval` | How often the mounted destination configuration is reloaded and eligible destinations are polled. | `"5s"` |
| `kerberoshub.audit.leaseDuration` | Per-destination lease duration. It must exceed every destination timeout by at least 30 seconds. | `"2m"` |
| `kerberoshub.audit.terminationGracePeriodSeconds` | Pod termination grace period. Keep this longer than `leaseDuration` so an in-flight cycle can finish. | `150` |
| `kerberoshub.audit.destinations` | Webhook destinations. Each entry supports `id`, `enabled`, `url`, delivery limits, filters, public `headers`, `bearerTokenSecret`, and arbitrary `secretHeaders`. Destination IDs retain independent checkpoints. | `[]` |
| `kerberoshub.audit.serviceMonitor.enabled` | Create a Prometheus `ServiceMonitor` for audit service metrics. | `true` |
| `kerberoshub.audit.serviceMonitor.interval` | Audit service metrics scrape interval. | `"15s"` |
| `kerberoshub.audit.volumes` | Additional pod volumes, for example a private webhook CA bundle. | `[]` |
| `kerberoshub.audit.volumeMounts` | Additional audit service container volume mounts. | `[]` |
| `kerberoshub.audit.extraEnv` | Additional audit service environment variables. | `[]` |
| `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.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` |
@@ -283,15 +486,16 @@ Below all configuration options and parameters are listed.
| `kerberoshub.reactivate.repository` | Container image repository for `kerberoshub.reactivate`. | `"uugai/hub-reactivatesubscriptions"` |
| `kerberoshub.reactivate.pullPolicy` | Image pull policy for `kerberoshub.reactivate`. | `"IfNotPresent"` |
| `kerberoshub.reactivate.tag` | Container image tag/version for `kerberoshub.reactivate`. | `"v1.0.2"` |
| `kerberoshub.reactivate.replicas` | Number of replicas for `kerberoshub.reactivate`. | `1` |
| `kerberoshub.reactivate.replicas` | Number of replicas for `kerberoshub.reactivate`. Set to `0` to disable. | `0` |
| `kerberoshub.reactivate.logLevel` | Log verbosity level for `kerberoshub.reactivate`. | `"info"` |
| `kerberoshub.reactivate.resources.requests.memory` | Memory request for `kerberoshub.reactivate`. | `"10Mi"` |
| `kerberoshub.reactivate.resources.requests.cpu` | CPU request for `kerberoshub.reactivate`. | `"10m"` |
| `kerberoshub.forwarder.enabled` | Enable or disable the Hub forwarder component. | `false` |
| `kerberoshub.proxy.enabled` | Enable or disable the legacy Hub proxy Deployment and LoadBalancer Service. | `false` |
| `kerberoshub.proxy.repository` | Container image repository for `kerberoshub.proxy`. | `"uugai/hub-proxy"` |
| `kerberoshub.proxy.pullPolicy` | Image pull policy for `kerberoshub.proxy`. | `"IfNotPresent"` |
| `kerberoshub.proxy.tag` | Container image tag/version for `kerberoshub.proxy`. | `"v1.0.0"` |
| `kerberoshub.proxy.replicas` | Number of replicas for `kerberoshub.proxy`. | `1` |
| `kerberoshub.proxy.replicas` | Number of replicas for `kerberoshub.proxy` when enabled. | `0` |
| `kerberoshub.proxy.logLevel` | Log verbosity level for `kerberoshub.proxy`. | `"info"` |
| `kerberoshub.proxy.resources.requests.memory` | Memory request for `kerberoshub.proxy`. | `"10Mi"` |
| `kerberoshub.proxy.resources.requests.cpu` | CPU request for `kerberoshub.proxy`. | `"10m"` |
@@ -306,6 +510,7 @@ Below all configuration options and parameters are listed.
| `kerberospipeline.monitor.pullPolicy` | The Docker registry pull policy. | `"IfNotPresent"` |
| `kerberospipeline.monitor.tag` | The Docker image tag/version. | `"v1.3.9"` |
| `kerberospipeline.monitor.replicas` | Number of replicas for `kerberospipeline.monitor`. | `1` |
| `kerberospipeline.monitor.logLevel` | Monitor log level. Set to `debug` for per-event processing checkpoints. | `"info"` |
| `kerberospipeline.monitor.resources.requests.memory` | Memory request for `kerberospipeline.monitor`. | `"10Mi"` |
| `kerberospipeline.monitor.resources.requests.cpu` | CPU request for `kerberospipeline.monitor`. | `"10m"` |
| `kerberospipeline.sequence.repository` | The [sequencer microservice](https://doc.kerberos.io/hub/pipeline/#sequencer), grouping recordings in chunks/groups. | `"ghcr.io/uug-ai/hub-pipeline-sequence"` |
@@ -325,7 +530,9 @@ Below all configuration options and parameters are listed.
| `kerberospipeline.notify.pullPolicy` | The Docker registry pull policy. | `"IfNotPresent"` |
| `kerberospipeline.notify.tag` | The Docker image tag/version. | `"v1.3.9"` |
| `kerberospipeline.notify.replicas` | Number of replicas for `kerberospipeline.notify`. | `1` |
| `kerberospipeline.notify.audit.enabled` | Enable audit publishing from the notification worker when global auditing is enabled. | `true` |
| `kerberospipeline.notify.logLevel` | Log verbosity level for `kerberospipeline.notify`. | `"info"` |
| `kerberospipeline.notify.notificationExpiryMinutes` | Maximum recording age in minutes before notification delivery. Set to `"0"` to disable the freshness cutoff. | `"15"` |
| `kerberospipeline.notify.resources.requests.memory` | Memory request for `kerberospipeline.notify`. | `"10Mi"` |
| `kerberospipeline.notify.resources.requests.cpu` | CPU request for `kerberospipeline.notify`. | `"10m"` |
| `kerberospipeline.notifyTest.repository` | The notification service for testing, the different channels. | `"uugai/hub-pipeline-notification-test"` |
@@ -377,8 +584,8 @@ Below all configuration options and parameters are listed.
| `kerberospipeline.sprite.replicas` | Number of replicas for `kerberospipeline.sprite`. | `5` |
| `kerberospipeline.sprite.logLevel` | Log verbosity level for `kerberospipeline.sprite`. | `"info"` |
| `kerberospipeline.sprite.interval` | Configuration value for `kerberospipeline.sprite.interval`. | `"1"` |
| `kerberospipeline.sprite.width` | Configuration value for `kerberospipeline.sprite.width`. | `"240"` |
| `kerberospipeline.sprite.height` | Configuration value for `kerberospipeline.sprite.height`. | `"135"` |
| `kerberospipeline.sprite.width` | Tile width in pixels; preserve width:height = 16:9. | `"240"` |
| `kerberospipeline.sprite.height` | Tile height in pixels; preserve width:height = 16:9. | `"135"` |
| `kerberospipeline.sprite.resources.requests.memory` | Memory request for `kerberospipeline.sprite`. | `"512Mi"` |
| `kerberospipeline.sprite.resources.requests.cpu` | CPU request for `kerberospipeline.sprite`. | `"500m"` |
| `kerberospipeline.sprite.resources.limits.memory` | Memory limit for `kerberospipeline.sprite`. | `"2Gi"` |
@@ -390,15 +597,6 @@ Below all configuration options and parameters are listed.
| `kerberospipeline.export.logLevel` | Log verbosity level for `kerberospipeline.export`. | `"info"` |
| `kerberospipeline.export.resources.requests.memory` | Memory request for `kerberospipeline.export`. | `"10Mi"` |
| `kerberospipeline.export.resources.requests.cpu` | CPU request for `kerberospipeline.export`. | `"10m"` |
| `kerberospipeline.redaction.repository` | Container image repository for `kerberospipeline.redaction`. | `"ghcr.io/uug-ai/hub-pipeline-redaction"` |
| `kerberospipeline.redaction.pullPolicy` | Image pull policy for `kerberospipeline.redaction`. | `"IfNotPresent"` |
| `kerberospipeline.redaction.tag` | Container image tag/version for `kerberospipeline.redaction`. | `"v1.0.0"` |
| `kerberospipeline.redaction.replicas` | Number of replicas for `kerberospipeline.redaction`. | `2` |
| `kerberospipeline.redaction.logLevel` | Log verbosity level for `kerberospipeline.redaction`. | `"info"` |
| `kerberospipeline.redaction.resources.requests.memory` | Memory request for `kerberospipeline.redaction`. | `"512Mi"` |
| `kerberospipeline.redaction.resources.requests.cpu` | CPU request for `kerberospipeline.redaction`. | `"500m"` |
| `kerberospipeline.redaction.resources.limits.memory` | Memory limit for `kerberospipeline.redaction`. | `"2Gi"` |
| `kerberospipeline.redaction.resources.limits.cpu` | CPU limit for `kerberospipeline.redaction`. | `"1000m"` |
| `email.provider` | The email service provider for sending out messages over email , use `'mailgun'` or `'smtp'`. | `"mailgun"` |
| `email.from` | The email address that is sending messages in name of, by default `'support@yourdomain.com'`. | `"support@yourdomain.com"` |
| `email.displayName` | The display name that is sending messages in name of, by default `'yourdomain.com'` | `"yourdomain.com"` |
@@ -423,7 +621,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] An Agent's status has been changed"` |
| `email.templates.deviceTitle` | Email subject title for the `device` template. | `"[Device] A Kerberos 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. | `""` |
@@ -467,6 +665,178 @@ As mentioned during the Post installation step, you'll import some `.nosql` file
Within the Kerberos Hub front-end you'll see the option to filter through classifications. This filtered is stored in the `settings` collection. By changing the entries of the `classifications` object, you can add, edit or remove items from the filters.
The default shared classification list lives in
[`configuration/catalogs/classifications.json`](configuration/catalogs/classifications.json),
independently of workflow contracts. Set `classificationCatalog` to a list to
replace those defaults; unset or `null` uses the bundled file. Each entry contains the stable classifier output
`key`, the user-facing `label`, and an `icon` key. Hub API exposes this catalog
to alerts and filters. When the mounted catalog is unavailable, compatible Hub
API versions fall back to the legacy `settings` document and then the built-in
classification list. Set `classificationCatalogEnabled: false` to omit the
ConfigMap and make Hub API use that fallback directly.
```yaml
classificationCatalogEnabled: true
classificationCatalog:
- key: forklift
label: Forklift
icon: vehicle
```
### Workflow condition contracts (preparatory)
This chart defines the initial v1 metadata format and opt-in file packaging.
**Hub API and the frontend do not yet consume these contracts.** Enabling the
packaging only mounts files; it neither enables an editor feature nor registers
workers or changes execution. No new loader environment variable is set.
The earlier device-bound workflow configurations were development fixtures, not
a recommended deployment model. Charts describe capabilities, not selected
devices, project IDs, concrete workflow edges, or user-authored conditions.
Device filtering is optional. Saved selections belong in individual workflows.
The configuration is split into independent shared catalogs and their consumers:
```text
configuration/
catalogs/
classifications.json
workflow-contracts/
start.yaml
schemas/
catalog.schema.json
workflow-contract.schema.json
```
- [Start contract](configuration/workflow-contracts/start.yaml): current device,
site, group and initial-classification presets, plus schedule/condition controls.
- [Custom pose example](examples/workflow-contracts/pose.yaml): hypothetical
worker output, demonstrating a file catalog, inline choices and a numeric field.
This example does not deploy a worker or claim a universal pose-model format.
- [Contract schema](schemas/workflow-contract.schema.json): format, source-name
documentation and YAML editor completion via the `$schema` comment.
- [Catalog schema](schemas/catalog.schema.json): shared JSON lists of
`{key, label, icon?}`, compatible with the current classification file format.
Each contract defines a `stage` type, a `schemaVersion`, a `contractVersion` and
its own `fields`. A field declares `id`, `label`, `path`, scalar `type` and
supported `operators`; `optional`, numeric bounds and `choices` are optional.
The `type` describes each wildcard-selected candidate, not the containing array.
IDs must be unique within a contract. Increment `contractVersion` when changing
its meaning/output shape; the future runtime must bind workflows to compatible
versions rather than silently reinterpreting saved conditions.
#### Choice origins
Use exactly one of these, or omit `choices` for typed user input:
```yaml
choices:
source: project.devices
```
```yaml
choices:
file: ../catalogs/classifications.json
```
```yaml
choices:
values: [standing, sitting, lying]
```
The supported system-resource vocabulary for v1 is:
| `choices.source` | User-facing labels | Stored comparison values |
| --- | --- | --- |
| `project.devices` | Authorized device names | Stable device keys |
| `project.sites` | Authorized site names | Stable site IDs |
| `project.groups` | Authorized group names | Stable group IDs |
These are direct built-in API resolver identifiers, not database paths or
deployer-selected authorization scopes. They still require implementation in the
future loader. Resolvers must reuse existing project/resource authorization and
legacy-owner compatibility; metadata cannot grant access.
File references resolve relative to the mounted contract, within the shared
configuration root. V1 uses `../catalogs/<name>.json`; no URLs, arbitrary file
access or recursive catalog references. Catalog keys are stable strings, with
labels for display; inline lists may contain strings, numbers or booleans matching
the field type. Catalogs are reusable by filters, alerts and other consumers,
not owned by workflows. A library may ship a catalog without implementing an API.
#### Bundled and external packaging
`workflowConfiguration.enabled` defaults to `false`. With it enabled and
`existingConfigMap` empty, the chart mounts the bundled Start contract and the
**effective** classification list under `/etc/kerberos/configuration`, using
the directory layout above. The projected volume reuses the existing
classification ConfigMap, including `classificationCatalog` overrides, rather
than storing another copy in the workflow ConfigMap.
The existing classification ConfigMap, mount path, `CLASSIFICATION_CATALOG_FILE`
and API endpoint remain unchanged. `classificationCatalog: []` remains an
explicit empty list, not a request for chart defaults; the current API falls
back to settings/built-in values for an empty/invalid catalog. The preparatory
bundle does not reproduce that runtime fallback and must not be treated as a
resolved API catalog. The future loader must share the existing classification
loader. Bundled packaging requires `classificationCatalogEnabled: true`; an
external bundle can be packaged independently.
Custom deployers can maintain files in their own repository, create a ConfigMap
in the release namespace, and use the unchanged chart. For example, from this
chart directory (replace the namespace):
```sh
kubectl -n YOUR_NAMESPACE create configmap custom-workflow-configuration \
--from-file=pose.yaml=examples/workflow-contracts/pose.yaml \
--from-file=pose-keypoints.json=examples/catalogs/pose-keypoints.json
```
Then use [the example values overlay](examples/workflow-configuration-values.yaml).
`existingConfigMap` replaces the entire bundled configuration, not individual
files. Include Start and its referenced catalog too if the custom bundle needs
them. `items` explicitly maps ConfigMap keys to files in `workflow-contracts/`
and `catalogs/`; files are mounted read-only, without `subPath`. ConfigMap size
limits apply. Mount the same shared catalog ConfigMap into other consumers as
needed; do not maintain divergent copies of its contents.
The chart checks item paths and required options but cannot inspect an existing
ConfigMap at render time. Validate its files against the supplied schemas.
Bundled changes update the pod checksum. For externally managed ConfigMaps,
manage rollout/reload in your deployment tooling; the chart cannot checksum
their contents.
Helm validates `values.schema.json`, **not** arbitrary bundled YAML/JSON files.
The standalone schemas support authoring validation. The future loader must
also reject duplicate field/catalog keys, unknown sources, missing/invalid
files, incompatible bounds, unsupported contract versions and unsafe paths.
Validation failures must be surfaced, not silently replaced with empty choices.
#### Runtime semantics to preserve when wiring consumers
Start paths refer to sanitized initial context, never credentials. Other stage
paths are relative to that node instance's result. Conditions may use Start and
all available upstream node outputs, not just the immediately preceding stage;
future or unrelated parallel results are unavailable. Node instances remain
distinct. Conditional branches and missing outputs require explicit availability
handling, even for a field not marked `optional`.
Wildcard positive comparisons match any candidate. Independent wildcard
conditions can match different objects; use the existing same-element `anyMatch`
semantics when predicates must match one detection/keypoint.
The existing Start controls retain their behavior: alternatives within a scope
category are OR; populated device/site/group/classification categories, schedule
and advanced conditions are AND. `conditionMode` only combines the advanced
conditions. Empty selections/schedules are unrestricted. Multiple automatic
Start edges are OR for creating one run; only matched entry branches dispatch.
Manual runs are created before continuation conditions are evaluated, and their
automatic trigger settings remain dormant. Schedule uses recording time and an
IANA timezone, not an invented output field. The advanced editor retains logical
groups and existence checks; neither scheduling nor logical groups are scalar
output fields. Contracts do not redefine the saved workflow wire format.
### Indexing
Following indexes should be executed on the MongoDB database (Kerberos) to improve future performance. Within Kerberos Hub several queries are executed, following indexes will improve the loading times. If not applied you might experience application timeouts or reduced performance when storing lots of data.
@@ -487,6 +857,7 @@ Following indexes should be executed on the MongoDB database (Kerberos) to impro
#### Analysis collection
db.getCollection("analysis").createIndex({start:1})
db.getCollection("analysis").createIndex({organisationId:1, projectId:1, key:1}, {name:"analysis_org_project_key"})
db.getCollection("analysis").createIndex({userid:1, key:1})
db.getCollection("analysis").createIndex({userid:1, start:1})
@@ -0,0 +1,11 @@
[
{"key": "animal", "label": "Animal", "icon": "animal"},
{"key": "pedestrian", "label": "Pedestrian", "icon": "pedestrian"},
{"key": "cyclist", "label": "Cyclist", "icon": "cyclist"},
{"key": "motorbike", "label": "Motorbike", "icon": "motorbike"},
{"key": "lorry", "label": "Lorry", "icon": "lorry"},
{"key": "car", "label": "Car", "icon": "car"},
{"key": "handbag", "label": "Handbag", "icon": "handbag"},
{"key": "suitcase", "label": "Suitcase", "icon": "suitcase"},
{"key": "cell phone", "label": "Cell phone", "icon": "cell phone"}
]
@@ -0,0 +1,44 @@
# yaml-language-server: $schema=../../schemas/workflow-contract.schema.json
# Metadata only; no current API loader consumes this contract.
schemaVersion: 1
stage: start
contractVersion: 1
# Paths refer to the safe initial context, not the full event or credentials.
fields:
- id: device
label: Device
path: device.deviceKey
type: string
operators: [eq, in]
choices:
source: project.devices
- id: sites
label: Sites
path: device.siteIds.*
type: string
operators: [in]
choices:
source: project.sites
- id: groups
label: Groups
path: device.groupIds.*
type: string
operators: [in]
choices:
source: project.groups
- id: classifications
label: Initial classifications
path: inputs.classify.details.*.classified
type: string
operators: [in]
optional: true
choices:
file: ../catalogs/classifications.json
# Existing structured controls, not fields produced by Start.
controls:
- id: weeklySchedule
label: Weekly schedule
- id: conditions
label: Advanced conditions
@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: custom-i18n-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: azurefile-premium
resources:
requests:
storage: 25Mi
@@ -0,0 +1,9 @@
{
"nav": {
"cases": "Investigations",
"dashboard": "Home"
},
"login": {
"signInTo": "Sign in to {{domain}} \u2014 Acme Security"
}
}
+42 -14
View File
@@ -351,11 +351,23 @@
"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",
"delete": "Delete",
"openDetail": "Open detail"
"openDetail": "Open detail",
"runWorkflow": "Run workflow"
},
"exportSelection": {
"title": "Export selection",
@@ -367,6 +379,16 @@
"emptyAttachments": "This case has no attachments yet.",
"attachmentsHeading": "Attachments"
},
"runWorkflow": {
"title": "Run workflow",
"subtitle": "Launch a workflow over selected media on this case.",
"workflowLabel": "Workflow",
"loading": "Loading workflows…",
"noWorkflows": "No workflows are available to run on cases.",
"attachmentsSelected": "attachments selected",
"attachmentBadge": "Attachment",
"submit": "Run workflow"
},
"filter": {
"assignees": "Assignees",
"labels": "Labels",
@@ -1104,7 +1126,7 @@
"k1": "Add a Dataset",
"k2": "Are you sure you want to remove device",
"k3": "Search dataset..",
"k4": "Set up your first Agent",
"k4": "Set up your first Kerberos Agent",
"k5": "No devices are available",
"k6": "Subscribe to a plan",
"k7": "Dataset",
@@ -1152,12 +1174,12 @@
},
"new": {
"new": {
"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",
"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",
"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 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",
"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",
"k7": "our documentation website to learn more",
"k8": "A Docker image (amd64, armv7, arm64) is",
"k9": "available on the Docker Hub",
@@ -1167,8 +1189,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 Agent will show up on the devices page.",
"k17": "If you want to manage Agents at scale with Kubernetes, then",
"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",
"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",
@@ -1177,7 +1199,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 Agent you will now add, will show up on your devices page.",
"k26": "All Kerberos 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",
@@ -1603,7 +1625,8 @@
"k10": "or use the upload button below",
"k11": "Delete attachment",
"k12": "Are you sure you want to delete this attachment? This action cannot be undone.",
"k13": "Yes, delete"
"k13": "Yes, delete",
"k14": "Redact faces"
}
},
"media": {
@@ -1676,7 +1699,12 @@
"k11": "Forward 1 second",
"k12": "Frames per second for redaction",
"k13": "Frames per second",
"k14": "fps"
"k14": "fps",
"k15": "Detection run",
"k16": "Select a detection run",
"k17": "No detections",
"k18": "Automatic tracks unavailable",
"k19": "Automatic face tracks are disabled. You can still draw redaction boxes manually on the player and submit."
}
},
"motionmap": {
@@ -2117,9 +2145,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 Agents can store in different regions.",
"k23": "Your media can be stored in one of the following regions. Different Kerberos agents can store in different regions.",
"k24": "Copy",
"k25": "Use the below credentials to connect your Agents to your Kerberos Hub.",
"k25": "Use the below credentials to connect your Kerberos 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",
@@ -2558,7 +2586,7 @@
"k2": "draft",
"k3": "Device detail",
"k4": "Kerberos Factory",
"k5": "Agent",
"k5": "Kerberos Agent",
"k6": "n/a",
"k7": "Remove device"
}
@@ -2794,7 +2822,7 @@
"k2": "PREVIEW",
"k3": "Switch to live [Shift + L]",
"k4": "LIVE",
"k5": "Make sure you are using the latest Agent and a Gold subscription",
"k5": "Make sure you are using the latest Kerberos 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 Agents changed</h2>
padding-right: 0;">One of your Kerberos 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 Agent changed</h3>
width: 480px">The status your Kerberos 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;">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>
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>
<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 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 Kerberos 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 Agents</h2>
padding-right: 0;">High upload detected by one or more Kerberos Agents</h2>
</td>
<td class="corner-td" align="right"></td>
</tr>
@@ -0,0 +1,418 @@
<!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>
@@ -0,0 +1,21 @@
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.
@@ -0,0 +1,425 @@
<!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>
@@ -0,0 +1,21 @@
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.
@@ -0,0 +1,5 @@
[
{"key": "nose", "label": "Nose"},
{"key": "left_shoulder", "label": "Left shoulder"},
{"key": "right_shoulder", "label": "Right shoulder"}
]
@@ -0,0 +1,9 @@
# Packaging only; does not activate contract loading or deploy a pose worker.
workflowConfiguration:
enabled: true
existingConfigMap: custom-workflow-configuration
items:
- key: pose.yaml
path: workflow-contracts/pose.yaml
- key: pose-keypoints.json
path: catalogs/pose-keypoints.json
@@ -0,0 +1,27 @@
# yaml-language-server: $schema=../../schemas/workflow-contract.schema.json
# Hypothetical custom worker output; not an installed stage or universal pose schema.
schemaVersion: 1
stage: pose
contractVersion: 1
fields:
- id: keypoint
label: Keypoint
path: people.*.keypoints.*.name
type: string
operators: [in]
choices:
file: ../catalogs/pose-keypoints.json
- id: posture
label: Posture
path: people.*.posture
type: string
operators: [in]
choices:
values: [standing, sitting, lying]
- id: confidence
label: Keypoint confidence
path: people.*.keypoints.*.confidence
type: number
operators: [gt, gte, lt, lte]
minimum: 0
maximum: 1
+17
View File
@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Hub shared choice catalog v1",
"description": "Compatible with the existing classification JSON format. Keys must be unique; no references to other catalogs.",
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["key", "label"],
"properties": {
"key": {"type": "string", "minLength": 1},
"label": {"type": "string", "minLength": 1},
"icon": {"type": "string", "minLength": 1}
}
}
}
@@ -0,0 +1,142 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Hub workflow condition contract v1 (preparatory)",
"type": "object",
"additionalProperties": false,
"required": ["schemaVersion", "stage", "contractVersion", "fields"],
"properties": {
"schemaVersion": {"const": 1},
"stage": {
"type": "string",
"pattern": "^[a-z][a-z0-9-]*$",
"description": "Stage type, not a node instance. start describes initial context."
},
"contractVersion": {"type": "integer", "minimum": 1},
"fields": {
"type": "array",
"minItems": 1,
"items": {"$ref": "#/definitions/field"}
},
"controls": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "label"],
"properties": {
"id": {"enum": ["weeklySchedule", "conditions"]},
"label": {"type": "string", "minLength": 1}
}
}
}
},
"definitions": {
"choices": {
"type": "object",
"additionalProperties": false,
"properties": {
"source": {
"type": "string",
"description": "Built-in authorized project-resource resolver; not a database path.",
"oneOf": [
{"const": "project.devices", "description": "Device keys as values, device names as labels."},
{"const": "project.sites", "description": "Site IDs as values, site names as labels."},
{"const": "project.groups", "description": "Group IDs as values, group names as labels."}
]
},
"file": {
"type": "string",
"pattern": "^\\.\\./catalogs/[A-Za-z0-9_-][A-Za-z0-9._-]*\\.json$",
"description": "Direct shared catalog file relative to the contract; no URLs or nested references."
},
"values": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {"type": ["string", "number", "boolean"]}
}
},
"oneOf": [
{"required": ["source"]},
{"required": ["file"]},
{"required": ["values"]}
]
},
"field": {
"type": "object",
"additionalProperties": false,
"required": ["id", "label", "path", "type", "operators"],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_-]*$",
"description": "Stable identifier, unique within this contract."
},
"label": {"type": "string", "minLength": 1},
"path": {
"type": "string",
"pattern": "^[A-Za-z_][A-Za-z0-9_-]*(\\.(\\*|[A-Za-z_][A-Za-z0-9_-]*))*$",
"description": "Safe Start-context path, or relative path inside this node instance's output. Wildcards select scalar candidates."
},
"type": {"enum": ["string", "integer", "number", "boolean"]},
"operators": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {"enum": ["eq", "ne", "in", "exists", "contains", "matches", "gt", "gte", "lt", "lte"]}
},
"optional": {
"type": "boolean",
"default": false,
"description": "Field may be absent even when the node completes; branch availability is determined separately."
},
"minimum": {"type": "number"},
"maximum": {"type": "number"},
"choices": {"$ref": "#/definitions/choices"}
},
"allOf": [
{
"if": {"properties": {"type": {"const": "string"}}},
"then": {
"properties": {
"operators": {"items": {"enum": ["eq", "ne", "in", "exists", "contains", "matches"]}},
"choices": {"properties": {"values": {"items": {"type": "string"}}}}
},
"not": {"anyOf": [{"required": ["minimum"]}, {"required": ["maximum"]}]}
}
},
{
"if": {"properties": {"type": {"const": "boolean"}}},
"then": {
"properties": {
"operators": {"items": {"enum": ["eq", "ne", "in", "exists"]}},
"choices": {"properties": {"values": {"items": {"type": "boolean"}}}}
},
"not": {"anyOf": [{"required": ["minimum"]}, {"required": ["maximum"]}]}
}
},
{
"if": {"properties": {"type": {"enum": ["integer", "number"]}}},
"then": {
"properties": {
"operators": {"items": {"enum": ["eq", "ne", "in", "exists", "gt", "gte", "lt", "lte"]}},
"choices": {"properties": {"values": {"items": {"type": "number"}}}}
}
}
},
{
"if": {"properties": {"type": {"const": "integer"}}},
"then": {"properties": {"choices": {"properties": {"values": {"items": {"type": "integer"}}}}}}
},
{
"if": {"properties": {"type": {"enum": ["integer", "number", "boolean"]}}},
"then": {
"properties": {
"choices": {"not": {"anyOf": [{"required": ["file"]}, {"required": ["source"]}]}}
}
}
}
]
}
}
}
+41
View File
@@ -0,0 +1,41 @@
{{/* Keep the legacy endpoint and the preparatory bundle on the same chart data. */}}
{{- define "hub.classificationCatalog" -}}
{{- if kindIs "invalid" .Values.classificationCatalog -}}
{{- .Files.Get "configuration/catalogs/classifications.json" | mustFromJson | toPrettyJson -}}
{{- else if kindIs "slice" .Values.classificationCatalog -}}
{{- .Values.classificationCatalog | toPrettyJson -}}
{{- else -}}
{{- fail "classificationCatalog must be null (bundled defaults) or a list" -}}
{{- end -}}
{{- end -}}
{{- define "hub.workflowConfiguration.validate" -}}
{{- $config := .Values.workflowConfiguration | default dict -}}
{{- if $config.enabled -}}
{{- if $config.existingConfigMap -}}
{{- if not $config.items -}}
{{- fail "workflowConfiguration.items is required with existingConfigMap" -}}
{{- end -}}
{{- $paths := dict -}}
{{- range $config.items -}}
{{- if not (regexMatch "^[A-Za-z0-9._-]+$" .key) -}}
{{- fail "workflowConfiguration.items keys must be valid ConfigMap keys" -}}
{{- end -}}
{{- if or (not (regexMatch "^(workflow-contracts|catalogs)/[A-Za-z0-9._/-]+$" .path)) (ne (clean .path) .path) -}}
{{- fail "workflowConfiguration.items paths must be normalized relative files under workflow-contracts/ or catalogs/" -}}
{{- end -}}
{{- if hasKey $paths .path -}}
{{- fail (printf "duplicate workflowConfiguration.items path %q" .path) -}}
{{- end -}}
{{- $_ := set $paths .path true -}}
{{- end -}}
{{- else -}}
{{- if $config.items -}}
{{- fail "workflowConfiguration.items requires existingConfigMap" -}}
{{- end -}}
{{- if not .Values.classificationCatalogEnabled -}}
{{- fail "bundled workflowConfiguration requires classificationCatalogEnabled; use an existingConfigMap for a custom bundle" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
+48
View File
@@ -0,0 +1,48 @@
{{/* Build the path to the configured MongoDB CA bundle. */}}
{{- define "hub.mongodb.tlsCAFile" -}}
{{- if and .Values.mongodb.tls.enabled .Values.mongodb.tls.existingSecret .Values.mongodb.tls.caFileName -}}
{{- printf "%s/%s" .Values.mongodb.tls.mountPath .Values.mongodb.tls.caFileName | clean -}}
{{- end -}}
{{- end -}}
{{/* Add TLS options to a configured MongoDB URI unless they are already present. */}}
{{- define "hub.mongodb.uri" -}}
{{- $uri := .Values.mongodb.uri | default "" -}}
{{- if and .Values.mongodb.tls.enabled $uri -}}
{{- if not (regexMatch "(?i)(^|[?&])tls=" $uri) -}}
{{- $separator := "?" -}}
{{- if contains "?" $uri -}}
{{- $separator = "&" -}}
{{- end -}}
{{- if or (hasSuffix "?" $uri) (hasSuffix "&" $uri) -}}
{{- $separator = "" -}}
{{- end -}}
{{- $uri = printf "%s%stls=true" $uri $separator -}}
{{- end -}}
{{- $caFile := include "hub.mongodb.tlsCAFile" . -}}
{{- if and $caFile (not (regexMatch "(?i)(^|[?&])tlsCAFile=" $uri)) -}}
{{- $separator := "&" -}}
{{- if not (contains "?" $uri) -}}
{{- $separator = "?" -}}
{{- else if or (hasSuffix "?" $uri) (hasSuffix "&" $uri) -}}
{{- $separator = "" -}}
{{- end -}}
{{- $uri = printf "%s%stlsCAFile=%s" $uri $separator $caFile -}}
{{- end -}}
{{- end -}}
{{- $uri -}}
{{- end -}}
{{/* Render the shared MongoDB CA Secret volume. */}}
{{- define "hub.mongodb.tlsVolume" -}}
- name: mongodb-tls
secret:
secretName: {{ .Values.mongodb.tls.existingSecret }}
{{- end -}}
{{/* Render the shared MongoDB CA volume mount. */}}
{{- define "hub.mongodb.tlsVolumeMount" -}}
- name: mongodb-tls
mountPath: {{ .Values.mongodb.tls.mountPath }}
readOnly: true
{{- end -}}
@@ -0,0 +1,10 @@
{{- if .Values.classificationCatalogEnabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: classification-catalog
namespace: {{ .Release.Namespace }}
data:
classifications.json: |-
{{- include "hub.classificationCatalog" . | nindent 4 }}
{{- end }}
@@ -0,0 +1,51 @@
{{- if .Values.audit.enabled -}}
{{- $destinations := list -}}
{{- $destinationIDs := dict -}}
{{- range $index, $destination := .Values.kerberoshub.audit.destinations -}}
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
{{- if not (regexMatch "^[a-z][a-z0-9-]{0,62}$" $id) -}}
{{- fail (printf "audit destination id %q must match ^[a-z][a-z0-9-]{0,62}$" $id) -}}
{{- end -}}
{{- if hasKey $destinationIDs $id -}}
{{- fail (printf "duplicate audit destination id %q" $id) -}}
{{- end -}}
{{- $_ := set $destinationIDs $id true -}}
{{- $url := required (printf "url is required for audit destination %s" $id) $destination.url -}}
{{- $entry := dict
"id" $id
"type" (default "webhook" $destination.type)
"enabled" $destination.enabled
"url" $url
"timeoutSeconds" (default 10 $destination.timeoutSeconds)
"batchSize" (default 100 $destination.batchSize)
"maxAttempts" (default 10 $destination.maxAttempts)
"startAt" (default "latest" $destination.startAt)
"headers" (default dict $destination.headers)
"filters" (default dict $destination.filters) -}}
{{- $secretRoot := printf "/var/run/secrets/audit-destinations/%s" $id -}}
{{- $bearer := default dict $destination.bearerTokenSecret -}}
{{- if gt (len $bearer) 0 -}}
{{- $secretName := required (printf "bearerTokenSecret.name is required for destination %s" $id) $bearer.name -}}
{{- $secretKey := default "token" $bearer.key -}}
{{- $_ := set $entry "bearerTokenFile" (printf "%s/bearer-%s" $secretRoot (sha256sum (printf "%s:%s" $secretName $secretKey) | trunc 12)) -}}
{{- end -}}
{{- $headerFiles := dict -}}
{{- range $headerName, $secret := (default dict $destination.secretHeaders) -}}
{{- $secretName := required (printf "secretName is required for destination %s header %s" $id $headerName) $secret.secretName -}}
{{- $secretKey := required (printf "secretKey is required for destination %s header %s" $id $headerName) $secret.secretKey -}}
{{- $_ := set $headerFiles $headerName (printf "%s/header-%s" $secretRoot (sha256sum (printf "%s:%s:%s" $headerName $secretName $secretKey) | trunc 12)) -}}
{{- end -}}
{{- if gt (len $headerFiles) 0 -}}
{{- $_ := set $entry "headerFiles" $headerFiles -}}
{{- end -}}
{{- $destinations = append $destinations $entry -}}
{{- end -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: hub-audit
namespace: {{ .Release.Namespace }}
data:
destinations.json: |
{{ dict "version" 1 "destinations" $destinations | toPrettyJson | nindent 4 }}
{{- end }}
+4 -1
View File
@@ -4,7 +4,7 @@ metadata:
name: mongodb-config
namespace: {{ .Release.Namespace }}
data:
MONGODB_URI: "{{ .Values.mongodb.uri }}"
MONGODB_URI: {{ include "hub.mongodb.uri" . | quote }}
MONGODB_HOST: "{{ .Values.mongodb.host }}"
MONGODB_AUTHENTICATION_MECHANISM: "{{ .Values.mongodb.authenticationMechanism }}"
MONGODB_DATABASE_CREDENTIALS: "{{ .Values.mongodb.adminDatabase }}"
@@ -12,4 +12,7 @@ data:
MONGODB_PASSWORD: "{{ .Values.mongodb.password }}"
MONGODB_RETRY_WRITES: "{{ .Values.mongodb.retryWrites }}"
MONGODB_FLAVOR: "{{ .Values.mongodb.flavor | default "mongodb" }}"
MONGODB_TLS: "{{ .Values.mongodb.tls.enabled }}"
MONGODB_TLS_CA_FILE: {{ include "hub.mongodb.tlsCAFile" . | quote }}
MONGODB_TLS_INSECURE_SKIP_VERIFY: "{{ .Values.mongodb.tls.insecureSkipVerify }}"
MONGODB_DATABASE_CLOUD: "Kerberos"
@@ -0,0 +1,12 @@
{{- include "hub.workflowConfiguration.validate" . -}}
{{- $config := .Values.workflowConfiguration | default dict -}}
{{- if and $config.enabled (not $config.existingConfigMap) (or (eq .Values.mode "all") (eq .Values.mode "ui")) }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ printf "%s-workflow-configuration" .Release.Name | trunc 63 | trimSuffix "-" }}
namespace: {{ .Release.Namespace }}
data:
start.yaml: |-
{{- .Files.Get "configuration/workflow-contracts/start.yaml" | nindent 4 }}
{{- end }}
+133 -5
View File
@@ -1,4 +1,5 @@
{{- if or (eq .Values.mode "all") (eq .Values.mode "ui") -}}
{{- $workflowConfiguration := .Values.workflowConfiguration | default dict -}}
{{- if .Values.kerberoshub.api.serviceEnabled }}
apiVersion: v1
kind: Service
@@ -120,6 +121,12 @@ spec:
metadata:
annotations:
checksum/config-mongodb: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
{{- if .Values.classificationCatalogEnabled }}
checksum/classification-catalog: {{ include (print $.Template.BasePath "/configmap-classification-catalog.yaml") . | sha256sum }}
{{- end }}
{{- if and $workflowConfiguration.enabled (not $workflowConfiguration.existingConfigMap) }}
checksum/workflow-configuration: {{ include (print $.Template.BasePath "/configmap-workflow-configuration.yaml") . | sha256sum }}
{{- end }}
labels:
app: hub-api
spec:
@@ -131,8 +138,37 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- $serverTLS := .Values.kerberoshub.api.serverTLS }}
{{- if or .Values.kerberoshub.api.volumes (and $serverTLS.enabled $serverTLS.secretName) }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- $classificationCatalogEnabled := .Values.classificationCatalogEnabled }}
{{- if or $workflowConfiguration.enabled $classificationCatalogEnabled .Values.kerberoshub.api.volumes (and $serverTLS.enabled $serverTLS.secretName) (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- if $workflowConfiguration.enabled }}
- name: workflow-configuration
{{- if $workflowConfiguration.existingConfigMap }}
configMap:
name: {{ $workflowConfiguration.existingConfigMap | quote }}
items:
{{- toYaml $workflowConfiguration.items | nindent 14 }}
{{- else }}
projected:
sources:
- configMap:
name: {{ printf "%s-workflow-configuration" .Release.Name | trunc 63 | trimSuffix "-" }}
items:
- key: start.yaml
path: workflow-contracts/start.yaml
- configMap:
name: classification-catalog
items:
- key: classifications.json
path: catalogs/classifications.json
{{- end }}
{{- end }}
{{- if $classificationCatalogEnabled }}
- name: classification-catalog
configMap:
name: classification-catalog
{{- end }}
{{- with .Values.kerberoshub.api.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
@@ -141,6 +177,9 @@ spec:
secret:
secretName: {{ $serverTLS.secretName }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberoshub.api.topologySpreadConstraints }}
topologySpreadConstraints:
@@ -157,8 +196,18 @@ spec:
ports:
- containerPort: 80
name: http
{{- if or .Values.kerberoshub.api.volumeMounts (and $serverTLS.enabled $serverTLS.secretName) }}
{{- if or $workflowConfiguration.enabled $classificationCatalogEnabled .Values.kerberoshub.api.volumeMounts (and $serverTLS.enabled $serverTLS.secretName) (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- if $workflowConfiguration.enabled }}
- name: workflow-configuration
mountPath: /etc/kerberos/configuration
readOnly: true
{{- end }}
{{- if $classificationCatalogEnabled }}
- name: classification-catalog
mountPath: /etc/kerberos/classifications
readOnly: true
{{- end }}
{{- with .Values.kerberoshub.api.volumeMounts}}
{{- toYaml . | nindent 12 }}
{{- end }}
@@ -167,12 +216,19 @@ spec:
mountPath: {{ $serverTLS.mountPath }}
readOnly: true
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
# Mongodb - loaded from ConfigMap
envFrom:
- configMapRef:
name: mongodb-config
env:
{{- if $classificationCatalogEnabled }}
- name: CLASSIFICATION_CATALOG_FILE
value: /etc/kerberos/classifications/classifications.json
{{- end }}
- name: ENVIRONMENT
value: "{{ .Values.environment }}"
- name: READ_ONLY
@@ -187,6 +243,23 @@ spec:
value: "{{ .Values.kerberoshub.api.schema }}://{{ .Values.kerberoshub.api.url }}"
- name: PUBLIC_URL
value: "{{ .Values.kerberoshub.frontend.schema }}://{{ .Values.kerberoshub.frontend.url }}"
- name: REFRESH_COOKIE_SECURE
value: {{ eq .Values.kerberoshub.api.schema "https" | quote }}
{{- $corsOrigins := list (printf "%s://%s" .Values.kerberoshub.frontend.schema .Values.kerberoshub.frontend.url) }}
{{- with .Values.kerberoshub.frontend.legacyUrl }}
{{- $corsOrigins = append $corsOrigins (printf "%s://%s" $.Values.kerberoshub.frontend.schema .) }}
{{- end }}
{{- range .Values.kerberoshub.frontend.domains }}
{{- $corsOrigins = append $corsOrigins (printf "%s://%s" $.Values.kerberoshub.frontend.schema .) }}
{{- end }}
{{- if and .Values.kerberoshub.frontend.multiTenant .Values.kerberoshub.frontend.tenantBaseDomain }}
{{- $corsOrigins = append $corsOrigins (printf "%s://*.%s" .Values.kerberoshub.frontend.schema .Values.kerberoshub.frontend.tenantBaseDomain) }}
{{- end }}
{{- if and .Values.kerberoshub.frontend.demoEnabled .Values.kerberoshub.frontend.demoUrl }}
{{- $corsOrigins = append $corsOrigins (printf "%s://%s" .Values.kerberoshub.frontend.schema .Values.kerberoshub.frontend.demoUrl) }}
{{- end }}
- name: CORS_ALLOWED_ORIGINS
value: {{ join "," $corsOrigins | quote }}
{{ if .Values.isPrivate }}
- name: KERBEROS_PRIVATE_CLOUD
value: "true"
@@ -233,6 +306,27 @@ spec:
- name: KERBEROS_JWT_SECRET
value: "{{ .Values.kerberoshub.api.jwtSecret }}"
# Temporarily stop serving the polled GET /devices list (204, no DB reads).
- name: DISABLE_AGENT_HEARTBEAT
value: {{ .Values.kerberoshub.api.disableAgentHeartbeat | default false | quote }}
# Media proxy: hide Vault/storage endpoints behind hub-api URLs.
{{- $mediaProxy := .Values.kerberoshub.api.mediaProxy | default dict }}
- name: MEDIA_PROXY_ENABLED
value: {{ $mediaProxy.enabled | default false | quote }}
{{- if $mediaProxy.publicUrl }}
- name: MEDIA_PROXY_PUBLIC_URL
value: {{ $mediaProxy.publicUrl | quote }}
{{- end }}
{{- if $mediaProxy.key }}
- name: MEDIA_PROXY_KEY
value: {{ $mediaProxy.key | quote }}
{{- end }}
{{- if $mediaProxy.ttl }}
- name: MEDIA_PROXY_TTL
value: {{ $mediaProxy.ttl | quote }}
{{- end }}
# SSO (OIDC) setup
- name: SSO_DOMAINS
value: "{{- range .Values.kerberoshub.api.sso }}{{ .domain }};{{- end }}"
@@ -259,6 +353,22 @@ spec:
- name: QUEUE_NAME
value: "{{ .Values.queueName }}"
# Deployment-global workflow definitions (WORKFLOW_DEFINITIONS): the
# SAME set the workflows engine consumes, assembled from the enabled
# definitions under kerberoshub.workflows.definitions (see
# kerberos-pipeline/_workflows-helpers.tpl). hub-api reads these
# read-only to surface config workflows alongside the user workflows
# it stores in the database; the config workflows are never persisted.
- name: WORKFLOW_DEFINITIONS
value: {{ include "kerberoshub.workflows.workflowDefinitions" . | quote }}
# Deployment service routing catalog used by API-owned embedded
# workflows (for example the one-stage case redaction modal flow).
- name: WORKFLOW_STAGE_QUEUES
value: {{ include "kerberoshub.workflows.stageQueues" . | quote }}
# Stages tenants may place in the workflow editor (see workflows.editorStages).
- name: WORKFLOW_EDITOR_STAGES
value: {{ .Values.kerberoshub.workflows.editorStages | default list | toJson | quote }}
# Stripe for billing
- name: STRIPE_KEY
value: "{{ .Values.kerberoshub.api.stripe.privateKey }}"
@@ -319,6 +429,14 @@ spec:
value: "{{ .Values.rabbitmq.username }}"
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
- name: AUDIT_ENABLED
value: {{ and .Values.audit.enabled .Values.kerberoshub.api.audit.enabled | quote }}
{{- if and .Values.audit.enabled .Values.kerberoshub.api.audit.enabled }}
- name: AUDIT_QUEUE
value: {{ .Values.audit.intake.queue | quote }}
- name: AUDIT_DEADLETTER_QUEUE
value: {{ .Values.audit.intake.deadletterQueue | quote }}
{{- end }}
# Slack notifications (this will send events/logs to a specific channel).
- name: SLACK_ENABLED
@@ -353,6 +471,14 @@ 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
@@ -399,15 +525,16 @@ spec:
- name: VAULT_SPRITE_SECRET_KEY
value: "{{ .Values.kerberosvault.sprite.secretKey }}"
- name: VAULT_SPRITE_INTERVAL
value: "{{ .Values.kerberosvault.sprite.interval }}"
value: "{{ .Values.kerberospipeline.sprite.interval }}"
- name: VAULT_SPRITE_WIDTH
value: "{{ .Values.kerberosvault.sprite.width }}"
value: "{{ .Values.kerberospipeline.sprite.width }}"
- name: VAULT_SPRITE_HEIGHT
value: "{{ .Values.kerberosvault.sprite.height }}"
value: "{{ .Values.kerberospipeline.sprite.height }}"
{{- if .Values.kerberoshub.extraEnv }}
{{- toYaml .Values.kerberoshub.extraEnv | nindent 12 }}
{{- end }}
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -415,4 +542,5 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
{{- end }}
@@ -0,0 +1,18 @@
{{- if and .Values.audit.enabled .Values.kerberoshub.audit.serviceMonitor.enabled -}}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: hub-audit-servicemonitor
namespace: {{ .Release.Namespace }}
labels:
service: hub-audit
release: prometheus
spec:
selector:
matchLabels:
service: hub-audit
endpoints:
- port: metrics
interval: {{ .Values.kerberoshub.audit.serviceMonitor.interval }}
path: /metrics
{{- end }}
@@ -0,0 +1,166 @@
{{- if .Values.audit.enabled -}}
{{- if ne (upper .Values.queueProvider) "RABBITMQ" -}}
{{- fail "kerberoshub.audit currently requires queueProvider=RABBITMQ" -}}
{{- end -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: hub-audit
namespace: {{ .Release.Namespace }}
spec:
replicas: {{ .Values.kerberoshub.audit.replicas }}
selector:
matchLabels:
app: hub-audit
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
annotations:
checksum/config-mongodb: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
labels:
app: hub-audit
spec:
terminationGracePeriodSeconds: {{ .Values.kerberoshub.audit.terminationGracePeriodSeconds }}
{{- 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 }}
{{- $mongodbTLS := .Values.mongodb.tls }}
volumes:
- name: destinations
configMap:
name: hub-audit
{{- range $index, $destination := .Values.kerberoshub.audit.destinations }}
{{- $bearer := default dict $destination.bearerTokenSecret }}
{{- $secretHeaders := default dict $destination.secretHeaders }}
{{- if or (gt (len $bearer) 0) (gt (len $secretHeaders) 0) }}
- name: audit-dest-{{ sha256sum $destination.id | trunc 12 }}
projected:
sources:
{{- if gt (len $bearer) 0 }}
- secret:
name: {{ required (printf "bearerTokenSecret.name is required for destination %s" $destination.id) $bearer.name | quote }}
items:
- key: {{ default "token" $bearer.key | quote }}
path: bearer-{{ sha256sum (printf "%s:%s" $bearer.name (default "token" $bearer.key)) | trunc 12 }}
{{- end }}
{{- range $headerName, $secret := $secretHeaders }}
- secret:
name: {{ required (printf "secretName is required for destination %s header %s" $destination.id $headerName) $secret.secretName | quote }}
items:
- key: {{ required (printf "secretKey is required for destination %s header %s" $destination.id $headerName) $secret.secretKey | quote }}
path: header-{{ sha256sum (printf "%s:%s:%s" $headerName $secret.secretName $secret.secretKey) | trunc 12 }}
{{- end }}
{{- end }}
{{- end }}
{{- with .Values.kerberoshub.audit.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- with .Values.kerberoshub.audit.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: hub-audit
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.audit.repository }}:{{ .Values.kerberoshub.audit.tag }}"
imagePullPolicy: {{ .Values.kerberoshub.audit.pullPolicy }}
{{- with .Values.kerberoshub.audit.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: metrics
containerPort: 2112
protocol: TCP
readinessProbe:
httpGet:
path: /healthz
port: metrics
livenessProbe:
httpGet:
path: /healthz
port: metrics
volumeMounts:
- name: destinations
mountPath: /etc/hub-audit
readOnly: true
{{- range $index, $destination := .Values.kerberoshub.audit.destinations }}
{{- $bearer := default dict $destination.bearerTokenSecret }}
{{- $secretHeaders := default dict $destination.secretHeaders }}
{{- if or (gt (len $bearer) 0) (gt (len $secretHeaders) 0) }}
- name: audit-dest-{{ sha256sum $destination.id | trunc 12 }}
mountPath: /var/run/secrets/audit-destinations/{{ $destination.id }}
readOnly: true
{{- end }}
{{- end }}
{{- with .Values.kerberoshub.audit.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
env:
- name: RABBITMQ_HOST
value: {{ .Values.rabbitmq.host | quote }}
- name: RABBITMQ_EXCHANGE
value: {{ .Values.rabbitmq.exchange | quote }}
- name: RABBITMQ_USERNAME
value: {{ .Values.rabbitmq.username | quote }}
- name: RABBITMQ_PASSWORD
value: {{ .Values.rabbitmq.password | quote }}
- name: AUDIT_QUEUE
value: {{ .Values.audit.intake.queue | quote }}
- name: AUDIT_DEADLETTER_QUEUE
value: {{ .Values.audit.intake.deadletterQueue | quote }}
- name: AUDIT_MAX_RETRIES
value: {{ .Values.audit.intake.maxRetries | quote }}
- name: AUDIT_PREFETCH_COUNT
value: {{ .Values.audit.intake.prefetchCount | quote }}
- name: AUDIT_PERSIST_TIMEOUT
value: {{ .Values.audit.intake.persistTimeout | quote }}
- name: DISPATCH_INTERVAL
value: {{ .Values.kerberoshub.audit.dispatchInterval | quote }}
- name: LEASE_DURATION
value: {{ .Values.kerberoshub.audit.leaseDuration | quote }}
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
{{- with .Values.kerberoshub.audit.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberoshub.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: hub-audit
namespace: {{ .Release.Namespace }}
labels:
app: hub-audit
service: hub-audit
spec:
ports:
- name: metrics
port: 2112
targetPort: metrics
protocol: TCP
selector:
app: hub-audit
{{- end }}
@@ -29,10 +29,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberoshub.cleanup.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberoshub.cleanup.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberoshub.cleanup.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberoshub.cleanup.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,10 +51,15 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberoshub.cleanup.volumeMounts }}
{{- if or .Values.kerberoshub.cleanup.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberoshub.cleanup.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
@@ -97,6 +108,8 @@ spec:
value: "{{ .Values.kerberoshub.cleanup.globalPassDeleteBudget }}"
- name: DEFAULT_TASK_RETENTION_DAYS
value: "{{ .Values.kerberoshub.cleanup.defaultTaskRetentionDays }}"
- name: AUDIT_EVENT_RETENTION_DAYS
value: "{{ .Values.kerberoshub.cleanup.auditEventRetentionDays }}"
{{- if .Values.kerberoshub.extraEnv }}
{{- toYaml .Values.kerberoshub.extraEnv | nindent 12 }}
{{- end }}
@@ -193,6 +193,10 @@ spec:
- name: GOOGLEMAPS_KEY
value: "{{ .Values.kerberoshub.frontend.googlemaps.apikey }}"
# features > chart
- name: CHART_COLORS
value: {{ .Values.kerberoshub.frontend.features.chart.colors | toJson | quote }}
# Zendesk for support
- name: ZENDESK_URL
value: "{{ .Values.kerberoshub.frontend.zendesk.url }}"
@@ -229,8 +233,34 @@ spec:
value: "{{ .Values.kerberoshub.frontend.features.case.enabled }}"
- name: FEATURE_WORKFLOWS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.workflows.enabled }}"
- name: FEATURE_AUDIT_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.audit.enabled }}"
- name: FEATURE_DEVICE_ANALYTICS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.devices.analyticsEnabled }}"
- name: FEATURE_DASHBOARD_SITES_FULLSCREEN_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.dashboard.sitesFullscreenEnabled }}"
- name: FEATURE_DEVICE_DEVELOPER_TOOLS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.devices.developerToolsEnabled }}"
- name: FEATURE_ORGANISATIONS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.organisations.enabled }}"
- name: FEATURE_ORGANISATION_SWITCHER_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.organisations.switcherEnabled }}"
- name: FEATURE_ORGANISATION_CREATION_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.organisations.creationEnabled }}"
- name: FEATURE_ORGANISATION_SETTINGS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.organisations.settingsEnabled }}"
- name: FEATURE_PROJECTS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.projects.enabled }}"
- name: FEATURE_PROJECT_SWITCHER_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.projects.switcherEnabled }}"
- name: FEATURE_PROJECT_CREATION_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.projects.creationEnabled }}"
- name: FEATURE_PROJECT_SETTINGS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.projects.settingsEnabled }}"
- name: FEATURE_DARK_MODE
value: "{{ .Values.kerberoshub.frontend.features.darkMode }}"
- name: FEATURE_SPLASH_SCREEN_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.splashScreen.enabled }}"
- name: FEATURE_LANDING_PAGE
value: "{{ .Values.kerberoshub.frontend.features.landingPage }}"
@@ -241,6 +271,20 @@ spec:
value: "{{ .Values.kerberoshub.frontend.features.i18n.defaultLanguage }}"
# features > liveview
- name: FEATURE_LIVE_STREAM_MODE
value: "{{ .Values.kerberoshub.frontend.features.liveview.liveStreamMode }}"
- name: FEATURE_HLS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.liveview.hlsEnabled }}"
- name: FEATURE_MOQ_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqEnabled }}"
- name: FEATURE_REMOTE_RECORDING_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.liveview.remoteRecordingEnabled }}"
- name: FEATURE_LIVE_VIEW_DEVICE_STATUS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.liveview.deviceStatusEnabled }}"
- name: MOQ_RELAY_URL
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqRelayUrl }}"
- name: MOQ_BROADCAST_PREFIX
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqBroadcastPrefix }}"
- name: FEATURE_LIVEVIEW_PAGINATION_MODE
value: "{{ .Values.kerberoshub.frontend.features.liveview.paginationMode }}"
- name: FEATURE_LIVEVIEW_PAGE_SIZE
@@ -255,6 +299,26 @@ spec:
# features > floorplan
- name: FEATURE_FLOORPLAN_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.floorplan.enabled }}"
# features > map
- name: MAP_TILE_URL_LIGHT
value: "{{ .Values.kerberoshub.frontend.features.map.tileUrlLight }}"
- name: MAP_TILE_URL_DARK
value: "{{ .Values.kerberoshub.frontend.features.map.tileUrlDark }}"
- name: MAP_ATTRIBUTION
value: "{{ .Values.kerberoshub.frontend.features.map.attribution }}"
- name: MAP_OVERLAY_TILE_URL
value: "{{ .Values.kerberoshub.frontend.features.map.overlayTileUrl }}"
- name: MAP_OVERLAY_API_KEY
value: "{{ .Values.kerberoshub.frontend.features.map.overlayApiKey }}"
- name: MAP_OVERLAY_ATTRIBUTION
value: "{{ .Values.kerberoshub.frontend.features.map.overlayAttribution }}"
- name: MAP_OVERLAY_MIN_ZOOM
value: "{{ .Values.kerberoshub.frontend.features.map.overlayMinZoom }}"
- name: MAP_OVERLAY_MAX_ZOOM
value: "{{ .Values.kerberoshub.frontend.features.map.overlayMaxZoom }}"
- name: MAP_OVERLAY_OPACITY
value: "{{ .Values.kerberoshub.frontend.features.map.overlayOpacity }}"
- name: COLOR_TRACK_BOX
value: "{{ .Values.kerberoshub.frontend.features.floorplan.colorTrackBox }}"
- name: COLOR_TRACK_BOX_HOVER
@@ -288,7 +352,9 @@ spec:
# features > face redaction
- name: FEATURE_FACE_REDACTION_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.faceRedaction.enabled }}"
value: "{{ .Values.kerberoshub.frontend.features.faceRedaction.enabled }}"
- name: FEATURE_FACE_REDACTION_CLASSIFIER_TRACKS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.faceRedaction.classifierTracksEnabled }}"
# features > media
- name: FEATURE_MEDIA_FILTER_DATE_ENABLED
@@ -303,14 +369,22 @@ spec:
value: "{{ .Values.kerberoshub.frontend.features.media.filter.objectDetection.enabled }}"
- name: FEATURE_MEDIA_FILTER_STAR_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.star.enabled }}"
- name: FEATURE_MEDIA_FILTER_ANALYSIS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.analysis.enabled }}"
- name: FEATURE_MEDIA_FILTER_REGION_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.region.enabled }}"
- name: FEATURE_MEDIA_FILTER_SORT_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.sort.enabled }}"
- name: FEATURE_MEDIA_FILTER_CATEGORY_ENABLED
- name: FEATURE_MEDIA_FILTER_CATEGORIES_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.category.enabled }}"
- name: FEATURE_MEDIA_FILTER_MARKERS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markers.enabled }}"
- name: FEATURE_MEDIA_FILTER_MARKER_OPTIONS_BY_DATE_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markerOptionsByDate.enabled }}"
- name: FEATURE_MEDIA_FILTER_MARKER_CATEGORY_PRIORITY_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markerCategoryPriority.enabled }}"
- name: FEATURE_MEDIA_FILTER_MARKER_CATEGORY_PRIORITY_CATEGORIES
value: {{ .Values.kerberoshub.frontend.features.media.filter.markerCategoryPriority.categories | toJson | replace "\\" "\\\\" | replace "'" "\\u0027" | quote }}
- name: FEATURE_MEDIA_FILTER_EVENTS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.events.enabled }}"
- name: FEATURE_MEDIA_FILTER_TAGS_ENABLED
@@ -312,8 +312,34 @@ spec:
value: "{{ .Values.kerberoshub.frontend.features.case.enabled }}"
- name: FEATURE_WORKFLOWS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.workflows.enabled }}"
- name: FEATURE_AUDIT_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.audit.enabled }}"
- name: FEATURE_DEVICE_ANALYTICS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.devices.analyticsEnabled }}"
- name: FEATURE_DASHBOARD_SITES_FULLSCREEN_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.dashboard.sitesFullscreenEnabled }}"
- name: FEATURE_DEVICE_DEVELOPER_TOOLS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.devices.developerToolsEnabled }}"
- name: FEATURE_ORGANISATIONS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.organisations.enabled }}"
- name: FEATURE_ORGANISATION_SWITCHER_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.organisations.switcherEnabled }}"
- name: FEATURE_ORGANISATION_CREATION_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.organisations.creationEnabled }}"
- name: FEATURE_ORGANISATION_SETTINGS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.organisations.settingsEnabled }}"
- name: FEATURE_PROJECTS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.projects.enabled }}"
- name: FEATURE_PROJECT_SWITCHER_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.projects.switcherEnabled }}"
- name: FEATURE_PROJECT_CREATION_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.projects.creationEnabled }}"
- name: FEATURE_PROJECT_SETTINGS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.projects.settingsEnabled }}"
- name: FEATURE_DARK_MODE
value: "{{ .Values.kerberoshub.frontend.features.darkModeEnabled }}"
- name: FEATURE_SPLASH_SCREEN_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.splashScreen.enabled }}"
- name: FEATURE_LANDING_PAGE
value: "{{ .Values.kerberoshub.frontend.features.landingPage }}"
@@ -326,6 +352,20 @@ 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_HLS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.liveview.hlsEnabled }}"
- name: FEATURE_MOQ_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqEnabled }}"
- name: FEATURE_REMOTE_RECORDING_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.liveview.remoteRecordingEnabled }}"
- name: FEATURE_LIVE_VIEW_DEVICE_STATUS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.liveview.deviceStatusEnabled }}"
- name: MOQ_RELAY_URL
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqRelayUrl }}"
- name: MOQ_BROADCAST_PREFIX
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqBroadcastPrefix }}"
- name: FEATURE_LIVEVIEW_PAGINATION_MODE
value: "{{ .Values.kerberoshub.frontend.features.liveview.paginationMode }}"
- name: FEATURE_LIVEVIEW_PAGE_SIZE
@@ -348,6 +388,18 @@ spec:
value: "{{ .Values.kerberoshub.frontend.features.map.tileUrlDark }}"
- name: MAP_ATTRIBUTION
value: "{{ .Values.kerberoshub.frontend.features.map.attribution }}"
- name: MAP_OVERLAY_TILE_URL
value: "{{ .Values.kerberoshub.frontend.features.map.overlayTileUrl }}"
- name: MAP_OVERLAY_API_KEY
value: "{{ .Values.kerberoshub.frontend.features.map.overlayApiKey }}"
- name: MAP_OVERLAY_ATTRIBUTION
value: "{{ .Values.kerberoshub.frontend.features.map.overlayAttribution }}"
- name: MAP_OVERLAY_MIN_ZOOM
value: "{{ .Values.kerberoshub.frontend.features.map.overlayMinZoom }}"
- name: MAP_OVERLAY_MAX_ZOOM
value: "{{ .Values.kerberoshub.frontend.features.map.overlayMaxZoom }}"
- name: MAP_OVERLAY_OPACITY
value: "{{ .Values.kerberoshub.frontend.features.map.overlayOpacity }}"
- name: COLOR_TRACK_BOX
value: "{{ .Values.kerberoshub.frontend.features.floorplan.colorTrackBox }}"
@@ -377,6 +429,8 @@ spec:
value: "{{ .Values.kerberoshub.frontend.colorDeviceMarkerBorder }}"
# features > chart
- name: CHART_COLORS
value: {{ .Values.kerberoshub.frontend.features.chart.colors | toJson | quote }}
- name: COLOR_CHART_SELECTION_FILL
value: "{{ .Values.kerberoshub.frontend.features.chart.colorChartSelectionFill }}"
- name: COLOR_CHART_SELECTION_STROKE
@@ -390,7 +444,9 @@ spec:
# features > face redaction
- name: FEATURE_FACE_REDACTION_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.faceRedaction.enabled }}"
value: "{{ .Values.kerberoshub.frontend.features.faceRedaction.enabled }}"
- name: FEATURE_FACE_REDACTION_CLASSIFIER_TRACKS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.faceRedaction.classifierTracksEnabled }}"
# features > media
- name: FEATURE_MEDIA_FILTER_DATE_ENABLED
@@ -405,14 +461,22 @@ spec:
value: "{{ .Values.kerberoshub.frontend.features.media.filter.objectDetection.enabled }}"
- name: FEATURE_MEDIA_FILTER_STAR_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.star.enabled }}"
- name: FEATURE_MEDIA_FILTER_ANALYSIS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.analysis.enabled }}"
- name: FEATURE_MEDIA_FILTER_REGION_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.region.enabled }}"
- name: FEATURE_MEDIA_FILTER_SORT_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.sort.enabled }}"
- name: FEATURE_MEDIA_FILTER_CATEGORY_ENABLED
- name: FEATURE_MEDIA_FILTER_CATEGORIES_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.category.enabled }}"
- name: FEATURE_MEDIA_FILTER_MARKERS_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markers.enabled }}"
- name: FEATURE_MEDIA_FILTER_MARKER_OPTIONS_BY_DATE_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markerOptionsByDate.enabled }}"
- name: FEATURE_MEDIA_FILTER_MARKER_CATEGORY_PRIORITY_ENABLED
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markerCategoryPriority.enabled }}"
- name: FEATURE_MEDIA_FILTER_MARKER_CATEGORY_PRIORITY_CATEGORIES
value: {{ .Values.kerberoshub.frontend.features.media.filter.markerCategoryPriority.categories | toJson | replace "\\" "\\\\" | replace "'" "\\u0027" | quote }}
- name: FEATURE_MEDIA_FILTER_DEFAULT_VIEW
value: "{{ .Values.kerberoshub.frontend.features.media.filter.defaultView }}"
- name: FEATURE_MEDIA_FILTER_EVENTS_ENABLED
@@ -29,10 +29,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberoshub.monitordevice.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberoshub.monitordevice.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberoshub.monitordevice.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberoshub.monitordevice.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,10 +51,15 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberoshub.monitordevice.volumeMounts}}
{{- if or .Values.kerberoshub.monitordevice.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberoshub.monitordevice.volumeMounts}}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
@@ -26,10 +26,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberoshub.reactivate.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberoshub.reactivate.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberoshub.reactivate.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberoshub.reactivate.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -38,10 +44,15 @@ spec:
- name: hub-reactivate-subscription
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.reactivate.repository }}:{{ .Values.kerberoshub.reactivate.tag }}"
imagePullPolicy: {{ .Values.kerberoshub.reactivate.pullPolicy }}
{{- with .Values.kerberoshub.reactivate.volumeMounts }}
{{- if or .Values.kerberoshub.reactivate.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberoshub.reactivate.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
@@ -0,0 +1,96 @@
{{/*
Reserve every effective identity, including disabled definitions, so enabling a
workflow later cannot silently alias another workflow's runs or references.
*/}}
{{- define "kerberoshub.workflows.validateDefinitions" -}}
{{- $identities := dict -}}
{{- range $name, $wf := .Values.kerberoshub.workflows.definitions -}}
{{- $id := sha256sum $name | trunc 24 -}}
{{- if hasKey $wf "id" -}}
{{- $invalid := printf "kerberoshub.workflows.definitions[%q].id must be a non-zero 24-character hexadecimal string (Mongo ObjectID); omit id for name-derived compatibility" $name -}}
{{- if not (kindIs "string" $wf.id) -}}
{{- fail $invalid -}}
{{- end -}}
{{- if or (not (regexMatch "^[0-9a-fA-F]{24}$" $wf.id)) (eq $wf.id "000000000000000000000000") -}}
{{- fail $invalid -}}
{{- end -}}
{{- $id = lower $wf.id -}}
{{- end -}}
{{- if hasKey $identities $id -}}
{{- fail (printf "kerberoshub.workflows.definitions: duplicate effective workflow id %q for %q and %q (including disabled definitions)" $id (index $identities $id) $name) -}}
{{- end -}}
{{- $_ := set $identities $id $name -}}
{{- end -}}
{{- end -}}
{{/*
Assemble the deployment-global workflow definitions (WORKFLOW_DEFINITIONS) as a
JSON array from every *enabled* workflow under kerberoshub.workflows.definitions.
This is the engine's boot-loaded configuration source and deployment stage
catalog: several distinct config workflows can run over one recording — each
opens its own run and dispatches only its own stages. Organisation-scoped
database workflows are discovered separately at runtime.
Each enabled definition contributes one workflow object:
id optional explicit stable identity, normalized to lowercase. Omitted
for legacy definitions; their effective id is SHA256(name)[:24].
name the map key (mutable display name when an explicit id is supplied).
enabled always true here (a disabled definition is skipped entirely).
source "config" — provenance marking a Helm-defined, deployment-global,
ops-managed workflow (read-only in the API, no owning organisation).
triggers how a run OPENS. Defaults to a single bare automatic trigger
(opens for every recording); narrow with device/schedule triggers.
Per-stage `needs` (below) decide which stages then FIRE.
stages the executable stage set, each contributing the same routing
descriptor the stageRegistry emits:
operation the stage's operation (unique within the workflow).
dispatch "always" (default) | "conditional".
queue from the matching services.<operation>.queue
(authoritative; omitted when unset so the engine
derives "kcloud-<operation>-queue.fifo").
needs conditional stages only: upstream dependencies, each
{operation?, condition?}, carried through verbatim.
needsMode conditional stages: "any" (default) | "all".
*/}}
{{- define "kerberoshub.workflows.workflowDefinitions" -}}
{{- include "kerberoshub.workflows.validateDefinitions" . -}}
{{- $defs := list -}}
{{- $services := .Values.kerberoshub.services | default dict -}}
{{- range $name, $wf := .Values.kerberoshub.workflows.definitions -}}
{{- if $wf.enabled -}}
{{- $stages := list -}}
{{- range $stage := $wf.stages -}}
{{- $op := $stage.operation -}}
{{- $entry := dict "operation" $op "dispatch" (default "always" $stage.dispatch) -}}
{{- $service := index $services $op -}}
{{- if $service }}{{- with $service.queue }}{{- $_ := set $entry "queue" . -}}{{- end }}{{- end }}
{{- with $stage.needs }}{{- $_ := set $entry "needs" . -}}{{- end }}
{{- with $stage.needsMode }}{{- $_ := set $entry "needsMode" . -}}{{- end }}
{{- $stages = append $stages $entry -}}
{{- end -}}
{{- $def := dict "name" $name "enabled" true "source" "config" "triggers" (default (list (dict "type" "automatic")) $wf.triggers) "stages" $stages -}}
{{- if hasKey $wf "id" }}{{- $_ := set $def "id" (lower $wf.id) -}}{{- end }}
{{- $defs = append $defs $def -}}
{{- end -}}
{{- end -}}
{{- $defs | toJson -}}
{{- end -}}
{{/*
Expose the deployment's operation→queue catalog to API producers that seed
embedded WorkflowRuns. Unlike WORKFLOW_DEFINITIONS this includes services that
are enabled for internal flows but are absent from user-visible workflow
definitions. The workflows engine remains authoritative for dispatch; producers
use this only to embed the same queue on a synthetic stage.
*/}}
{{- define "kerberoshub.workflows.stageQueues" -}}
{{- $queues := dict -}}
{{- range $operation, $service := (.Values.kerberoshub.services | default dict) -}}
{{- if ne $operation "workflows" -}}
{{- with $service.queue -}}
{{- $_ := set $queues $operation . -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $queues | toJson -}}
{{- end -}}
@@ -0,0 +1,161 @@
{{- /*
Generic workflow-stage worker.
Renders a Deployment + Service for every enabled worker under
kerberoshub.services.<name> other than the `workflows` engine itself. A custom
stage joins the pipeline by values alone — declare its worker here and route to
it from a kerberoshub.workflows.definitions stage of the same operation; no
per-stage template is 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 worker
named "loitering" gets LOITERING_QUEUE, "my-stage" gets MY_STAGE_QUEUE). To run
a worker outside the chart instead, leave services.<name>.enabled unset (or
false) and point its workflow stage at the queue you publish.
All stages receive the Vault read credentials (KERBEROS_STORAGE_URI /
ACCESS_KEY / SECRET). A stage that also writes an artefact back to Vault (e.g.
redaction) declares its destination provider with the named field
services.<name>.storageProvider, rendered as KERBEROS_STORAGE_PROVIDER; a
read-only stage omits it and gets no provider env.
Worker-specific settings go in services.<name>.env (plain values) and
services.<name>.secretEnv (references to keys in an existing Secret).
*/ -}}
{{- if and (or (eq .Values.mode "all") (eq .Values.mode "pipeline")) .Values.kerberoshub.workflows.enabled -}}
{{- $root := . -}}
{{- $services := .Values.kerberoshub.services | default dict -}}
{{- range $name, $svc := $services -}}
{{- if and (ne $name "workflows") $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 }}"
{{- with $svc.storageProvider }}
# Destination Vault provider (KERBEROS_STORAGE_PROVIDER) — only stages
# that write an artefact back (e.g. redaction) set services.<name>.
# storageProvider; read-only stages omit it and get no provider env.
- name: KERBEROS_STORAGE_PROVIDER
value: {{ . | quote }}
{{- end }}
# Per-stage tuning knobs. Any key/value under services.<name>.env is
# rendered verbatim as container env, so a worker can be tuned from
# values without a per-stage template. These override the image's own
# ENV defaults; the fixed contract env above is not overridable here.
{{- range $key, $value := $svc.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }} {{- with $svc.secretEnv }}
# Per-stage secret env. Each key under services.<name>.secretEnv maps a
# container env var to a key in an existing Kubernetes Secret
# ({name, key}), so credentials such as a model API key stay out of
# values. The Secret must exist in the release namespace.
{{- range $key, $ref := . }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ required (printf "kerberoshub.services.%s.secretEnv.%s.name is required" $name $key) $ref.name | quote }}
key: {{ required (printf "kerberoshub.services.%s.secretEnv.%s.key is required" $name $key) $ref.key | quote }}
{{- end }}
{{- end }}
---
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 }}
@@ -0,0 +1,135 @@
{{- include "kerberoshub.workflows.validateDefinitions" . -}}
{{- 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 }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberoshub.services.workflows.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberoshub.services.workflows.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- 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 }}
{{- if or .Values.kerberoshub.services.workflows.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberoshub.services.workflows.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- 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 set of
# named workflows it runs (WORKFLOW_DEFINITIONS): each with its own
# trigger and executable stages, assembled from the enabled definitions
# under kerberoshub.workflows.definitions (see _workflows-helpers.tpl).
# Definitions are the engine's boot-loaded config source and deployment
# stage catalog. Organisation-scoped database workflows are read per
# recording; an in-cluster engine refuses to start only when both the
# config definitions and the stage-queue catalog below are empty.
- name: WORKFLOWS_QUEUE
value: "{{ .Values.kerberoshub.services.workflows.queue }}"
- name: WORKFLOW_DEFINITIONS
value: {{ include "kerberoshub.workflows.workflowDefinitions" . | quote }}
# Operation→queue catalog so DB workflows reach workers no config workflow references.
- name: WORKFLOW_STAGE_QUEUES
value: {{ include "kerberoshub.workflows.stageQueues" . | 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 }}
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-analysis
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.analysis.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.analysis.replicas }}
selector:
@@ -29,10 +33,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberospipeline.analysis.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberospipeline.analysis.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberospipeline.analysis.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberospipeline.analysis.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,10 +55,15 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberospipeline.analysis.volumeMounts }}
{{- if or .Values.kerberospipeline.analysis.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberospipeline.analysis.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
@@ -81,6 +96,18 @@ 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
@@ -94,6 +121,7 @@ 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 }}"
@@ -101,6 +129,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-counting
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.counting.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.counting.replicas }}
selector:
@@ -77,6 +81,7 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -84,6 +89,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-dominantcolor
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.dominantColor.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.dominantColor.replicas }}
selector:
@@ -77,6 +81,7 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -84,6 +89,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-event
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.event.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.event.replicas }}
selector:
@@ -29,10 +33,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberospipeline.event.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberospipeline.event.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberospipeline.event.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberospipeline.event.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,10 +55,15 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberospipeline.event.volumeMounts }}
{{- if or .Values.kerberospipeline.event.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberospipeline.event.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
ports:
- containerPort: 8080
envFrom:
@@ -86,6 +101,7 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -93,6 +109,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -29,10 +29,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberospipeline.export.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberospipeline.export.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberospipeline.export.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberospipeline.export.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,10 +51,15 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberospipeline.export.volumeMounts }}
{{- if or .Values.kerberospipeline.export.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberospipeline.export.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
@@ -122,6 +133,7 @@ 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 }}"
@@ -129,6 +141,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-monitor
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.monitor.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.monitor.replicas }}
selector:
@@ -29,10 +33,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberospipeline.monitor.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberospipeline.monitor.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberospipeline.monitor.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberospipeline.monitor.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,14 +55,23 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberospipeline.monitor.volumeMounts }}
{{- if or .Values.kerberospipeline.monitor.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberospipeline.monitor.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
env:
# Application
- name: LOG_LEVEL
value: "{{ .Values.kerberospipeline.monitor.logLevel }}"
# Queue
- name: QUEUE_SYSTEM
value: "{{ .Values.queueProvider }}"
@@ -103,6 +122,7 @@ 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 }}"
@@ -110,6 +130,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -29,10 +29,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberospipeline.notifyTest.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberospipeline.notifyTest.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberospipeline.notifyTest.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberospipeline.notifyTest.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,10 +51,15 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberospipeline.notifyTest.volumeMounts}}
{{- if or .Values.kerberospipeline.notifyTest.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberospipeline.notifyTest.volumeMounts}}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
@@ -110,6 +121,7 @@ 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 }}"
@@ -117,6 +129,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-notify
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.notify.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.notify.replicas }}
selector:
@@ -29,10 +33,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberospipeline.notify.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberospipeline.notify.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberospipeline.notify.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberospipeline.notify.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,16 +55,23 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberospipeline.notify.volumeMounts}}
{{- if or .Values.kerberospipeline.notify.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberospipeline.notify.volumeMounts}}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
env:
- name: LOG_LEVEL
value: "{{ .Values.kerberospipeline.notify.logLevel }}"
- name: NOTIFICATION_EXPIRY_MINUTES
value: "{{ .Values.kerberospipeline.notify.notificationExpiryMinutes }}"
# Queue
- name: QUEUE_SYSTEM
value: "{{ .Values.queueProvider }}"
@@ -80,6 +97,14 @@ spec:
value: "{{ .Values.rabbitmq.username }}"
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
- name: AUDIT_ENABLED
value: {{ and .Values.audit.enabled .Values.kerberospipeline.notify.audit.enabled | quote }}
{{- if and .Values.audit.enabled .Values.kerberospipeline.notify.audit.enabled }}
- name: AUDIT_QUEUE
value: {{ .Values.audit.intake.queue | quote }}
- name: AUDIT_DEADLETTER_QUEUE
value: {{ .Values.audit.intake.deadletterQueue | quote }}
{{- end }}
# Kerberos Vault
- name: STORAGE_URI
@@ -142,6 +167,7 @@ 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 }}"
@@ -149,6 +175,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -1,114 +0,0 @@
{{- if or (eq .Values.mode "all") (eq .Values.mode "pipeline") -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: pipe-redaction
namespace: {{ .Release.Namespace }}
spec:
replicas: {{ .Values.kerberospipeline.redaction.replicas }}
selector:
matchLabels:
app: pipe-redaction
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
app: pipe-redaction
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.kerberospipeline.redaction.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberospipeline.redaction.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: pipe-redaction
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.redaction.repository }}:{{ .Values.kerberospipeline.redaction.tag }}"
imagePullPolicy: {{ .Values.kerberospipeline.redaction.pullPolicy }}
{{- with .Values.kerberospipeline.redaction.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberospipeline.redaction.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
env:
- name: LOG_LEVEL
value: "{{ .Values.kerberospipeline.redaction.logLevel }}"
- name: CLOUD_PROVIDER
value: "{{ .Values.cloudProvider }}"
- name: QUEUE_SYSTEM
value: "{{ .Values.queueProvider }}"
# Kafka settings
- name: KAFKA_BROKER
value: "{{ .Values.kafka.broker }}"
- name: KAFKA_USERNAME
value: "{{ .Values.kafka.username }}"
- name: KAFKA_PASSWORD
value: "{{ .Values.kafka.password }}"
- name: KAFKA_MECHANISM
value: "{{ .Values.kafka.mechanism }}"
- name: KAFKA_SECURITY
value: "{{ .Values.kafka.security }}"
# 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 (used to download the source media and upload the redacted artifact).
- name: KERBEROS_STORAGE_URI
value: "{{ .Values.kerberosvault.uri }}"
- name: KERBEROS_STORAGE_PROVIDER
value: "{{ .Values.kerberosvault.provider }}"
- name: KERBEROS_STORAGE_ACCESS_KEY
value: "{{ .Values.kerberosvault.accesskey }}"
- name: KERBEROS_STORAGE_SECRET
value: "{{ .Values.kerberosvault.secretkey }}"
# 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 }}"
---
apiVersion: v1
kind: Service
metadata:
name: pipe-redaction
namespace: {{ .Release.Namespace }}
labels:
app: pipe-redaction
service: pipe
spec:
ports:
- name: hub-metrics
port: 8080
targetPort: 8080
protocol: TCP
selector:
app: pipe-redaction
{{- end }}
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-sequence
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.sequence.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.sequence.replicas }}
selector:
@@ -29,10 +33,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberospipeline.sequence.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberospipeline.sequence.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberospipeline.sequence.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberospipeline.sequence.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,10 +55,15 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberospipeline.sequence.volumeMounts }}
{{- if or .Values.kerberospipeline.sequence.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberospipeline.sequence.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
@@ -80,6 +95,7 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -87,6 +103,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-sprite
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.sprite.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.sprite.replicas }}
selector:
@@ -100,6 +104,7 @@ 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 }}"
@@ -107,6 +112,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-throttler
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.throttler.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.throttler.replicas }}
selector:
@@ -29,10 +33,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberospipeline.throttler.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberospipeline.throttler.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberospipeline.throttler.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberospipeline.throttler.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -45,10 +55,15 @@ spec:
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.kerberospipeline.throttler.volumeMounts }}
{{- if or .Values.kerberospipeline.throttler.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberospipeline.throttler.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
@@ -82,6 +97,7 @@ spec:
- name: RABBITMQ_PASSWORD
value: "{{ .Values.rabbitmq.password }}"
{{- if .Values.opentelemetry.enabled }}
# Open Telemetry tracing
- name: OTEL_EXPORTED_OTLP_ENABLED
value: "{{ .Values.opentelemetry.enabled }}"
@@ -89,6 +105,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
kind: Service
@@ -4,6 +4,10 @@ kind: Deployment
metadata:
name: pipe-thumbnail
namespace: {{ .Release.Namespace }}
{{- with .Values.kerberospipeline.thumbnail.deploymentAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.kerberospipeline.thumbnail.replicas }}
selector:
@@ -103,6 +107,7 @@ 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 }}"
@@ -110,6 +115,7 @@ spec:
value: "{{ .Values.opentelemetry.routingEnabled }}"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "{{ .Values.opentelemetry.collector.endpoint }}"
{{- end }}
---
apiVersion: v1
@@ -27,10 +27,16 @@ spec:
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.kerberoshub.forwarder.volumes }}
{{- $mongodbTLS := .Values.mongodb.tls }}
{{- if or .Values.kerberoshub.forwarder.volumes (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumes:
{{- with .Values.kerberoshub.forwarder.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
{{- end }}
{{- end }}
{{- with .Values.kerberoshub.forwarder.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
@@ -43,10 +49,15 @@ spec:
requests:
memory: 10Mi
cpu: 10m
{{- with .Values.kerberoshub.forwarder.volumeMounts }}
{{- if or .Values.kerberoshub.forwarder.volumeMounts (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
volumeMounts:
{{- with .Values.kerberoshub.forwarder.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
{{- end }}
{{- end }}
envFrom:
- configMapRef:
name: mongodb-config
@@ -1,4 +1,4 @@
{{- if eq .Values.mode "all" -}}
{{- if and (eq .Values.mode "all") .Values.kerberoshub.proxy.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
+32
View File
@@ -0,0 +1,32 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"classificationCatalog": {"type": ["array", "null"]},
"workflowConfiguration": {
"type": "object",
"additionalProperties": false,
"required": ["enabled", "existingConfigMap", "items"],
"properties": {
"enabled": {"type": "boolean"},
"existingConfigMap": {
"type": "string",
"maxLength": 253,
"pattern": "^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"
},
"items": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["key", "path"],
"properties": {
"key": {"type": "string", "pattern": "^[A-Za-z0-9._-]+$"},
"path": {"type": "string", "pattern": "^(workflow-contracts|catalogs)/[A-Za-z0-9_-][A-Za-z0-9._-]*\\.(yaml|json)$"}
}
}
}
}
}
}
}
+466 -52
View File
@@ -60,13 +60,41 @@ mongodb:
# and indexes, complex $lookup pipelines, etc.). When using DocumentDB you
# should also set retryWrites: "false".
flavor: "mongodb"
# TLS for MongoDB-compatible backends. When uri is set, missing TLS query
# parameters are appended automatically. AWS DocumentDB requires TLS and a
# trusted RDS CA bundle, typically stored in an existing Kubernetes Secret.
tls:
enabled: false
existingSecret: ""
caFileName: ""
mountPath: "/etc/mongodb/tls"
insecureSkipVerify: false
# Classifications supported by Hub filters, alerts, markers, and other
# downstream processing. Classifier output keys must match these values.
# Disable this to use the legacy classification catalog stored in MongoDB.
classificationCatalogEnabled: true
# classificationCatalog is optional and omitted by default, which uses
# configuration/catalogs/classifications.json. Set it to a list to override.
# Explicit null also uses defaults; [] retains the API fallback behavior.
# Preparatory configuration packaging only: the API does not yet load workflow
# contracts. Enabling this mounts files but does not activate a new editor/runtime.
workflowConfiguration:
enabled: false
# Empty uses the bundled Start contract and effective classification catalog.
# Otherwise mount this existing ConfigMap instead; it must be in this namespace.
existingConfigMap: ""
# Required only with existingConfigMap: map ConfigMap keys to relative files
# under /etc/kerberos/configuration (workflow-contracts/... or catalogs/...).
items: []
###################################################
# MQTT configuration (bi-directional communication)
###################################################
# 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 Agents and Kerberos Hub)
host: "mqtt.yourdomain.com" # this needs to be a public accessible DNS name (it's used to communicate between Kerberos Agents and Kerberos Hub)
port: "8443"
protocol: "wss"
username: "yourusername"
@@ -93,6 +121,16 @@ rabbitmq:
username: "yourusername"
password: "yourpassword"
exchange: ""
# Shared audit transport. The master switch controls the consumer and all
# producers; individual services may opt out through their own audit setting.
audit:
enabled: false
intake:
queue: "hub-audit-events"
deadletterQueue: "hub-audit-dead-letter"
maxRetries: 10
prefetchCount: 20
persistTimeout: "10s"
# If you already have a Kafka cluster you might use this instead of RabbitMQ.
kafka:
broker: "kafka1.yourdomain.com:9094" # can be internal dns name or external
@@ -121,7 +159,12 @@ opentelemetry:
enabled: false
routingEnabled: false
collector:
endpoint: "http://otel-collector:4317"
# 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"
############################################
# OpenAI configuration (semantic search)
#
@@ -226,8 +269,10 @@ kerberoshub:
api:
repository: ghcr.io/uug-ai/hub-api
pullPolicy: IfNotPresent
tag: "v1.9.8"
tag: "v1.9.51"
replicas: 2
audit:
enabled: true
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Set to false to skip rendering the hub-api Service (e.g. when an
# external/shared Service is managed elsewhere).
@@ -236,6 +281,19 @@ kerberoshub:
jwtSecret: "this-is-a-secret-please-change-to-random-string" # change to a random value, this is for generating JWT tokens.
schema: "https"
url: "api.yourdomain.com"
# Temporarily stop serving the polled GET /devices list: hub-api answers 204
# without querying MongoDB. The frontend still loads devices once through
# /session/bootstrap, but device online/offline status stops refreshing.
disableAgentHeartbeat: false
# Media proxy: when enabled, hub-api wraps every media URL it receives from
# Kerberos Vault into an opaque hub-api URL ({schema}://{url}/media/proxy?t=...)
# and streams the object itself, so clients never see Vault or the
# underlying storage endpoint. Increases hub-api egress/bandwidth.
mediaProxy:
enabled: false
publicUrl: "" # Optional override of the public hub-api base URL; defaults to {schema}://{url}.
key: "" # Optional encryption key (openssl rand -hex 32); defaults to a key derived from jwtSecret. Must be identical on all replicas.
ttl: "" # Optional lifetime of proxied URLs (Go duration, e.g. "12h"); defaults to 24h, max 168h.
resources:
requests:
memory: 100Mi
@@ -339,7 +397,7 @@ kerberoshub:
frontend:
repository: ghcr.io/uug-ai/hub-frontend
pullPolicy: IfNotPresent
tag: "v1.9.11"
tag: "v1.13.9"
replicas: 2
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Set to false to skip rendering the hub-frontend Service (e.g. when an
@@ -372,8 +430,8 @@ kerberoshub:
# - hosts:
# - "demo.yourdomain.com"
# secretName: kerberoshub-demo
# You can disable the Agent buttons, this make sense
# in a white-label setup, or where you are managing the Agents for your customers.
# 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.
hideAddAgent: "false"
# Multi tenancy (domains)
# By default the Kerberos Hub allows multi-tenancy through the concept
@@ -391,17 +449,32 @@ 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. 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.
# 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.)
#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
# mountPath: /usr/share/nginx/html/assets/i18n-custom
#volumes:
# - name: custom-layout
# persistentVolumeClaim:
@@ -433,6 +506,8 @@ kerberoshub:
features:
# General
darkModeEnabled: "true" # Enable or disable dark mode toggle 'true' or 'false'
splashScreen:
enabled: "true" # Enable or disable the pre-bootstrap splash screen 'true' or 'false'
landingPage: "/dashboard" # Landing page after login '/dashboard', '/liveview', '/media', '/devices', '/sites', '/groups'
# Internationalization (i18n): controls the runtime language behaviour
# of the front-end. When `enabled` is "false" the language switcher is
@@ -446,20 +521,52 @@ kerberoshub:
# Workflows allow you to define automated processes and actions in the front-end.
workflows:
enabled: "false" # Enable or disable workflows feature 'true' or 'false'
# Audit events are visible to owner/admin users when enabled.
audit:
enabled: "true" # Enable or disable the Audit events page and navigation link 'true' or 'false'
# Organisation controls remain visible as a read-only current organisation when switching is disabled.
organisations:
enabled: "" # Enable or disable the organisation feature family, including projects; when empty, child groups apply independently
switcherEnabled: "false" # Enable or disable organisation switching 'true' or 'false'
creationEnabled: "false" # Enable or disable organisation creation; requires switcherEnabled 'true' or 'false'
settingsEnabled: "false" # Enable or disable the organisation settings link 'true' or 'false'
projects:
enabled: "" # Fallback project group switch used only when organisations.enabled is empty
switcherEnabled: "false" # Enable or disable the read-only project dropdown 'true' or 'false'
creationEnabled: "false" # Reserved for project creation UI 'true' or 'false'
settingsEnabled: "false" # Reserved for project settings UI 'true' or 'false'
# Map tile configuration
map:
tileUrlLight: "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png" # Map tile URL for light mode
tileUrlDark: "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png" # Map tile URL for dark mode
attribution: "&copy; <a href='https://www.openstreetmap.org/copyright' target='_blank'>OpenStreetMap</a>" # Map attribution
overlayTileUrl: "" # Optional XYZ overlay URL; supports {z}, {x}, {y}, and {apiKey}
overlayApiKey: "" # Optional browser-visible API key substituted into overlayTileUrl
overlayAttribution: "" # Attribution displayed when the overlay is enabled
overlayMinZoom: "0" # Minimum overlay zoom level
overlayMaxZoom: "19" # Maximum overlay zoom level
overlayOpacity: "1" # Overlay opacity from 0 to 1
# 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), 'hls' or 'moq'
hlsEnabled: "true" # Offer HLS as a selectable LIVE transport 'true' or 'false'. When 'false' the HLS option is removed from the front-end and streams use webrtc
moqEnabled: "false" # Offer MoQ as a selectable LIVE transport 'true' or 'false'
remoteRecordingEnabled: "true" # Show the manual REC control and recording-state overlay in live views
deviceStatusEnabled: "true" # Show connectivity and recent-motion status in live view tile headers
moqRelayUrl: "https://relay.uug.ai/anon" # WebTransport URL of the MoQ relay
moqBroadcastPrefix: "devices" # Prefix used to build devices/<deviceKey>/live.hang broadcast names
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
# Device page
devices:
hideAgent: "false" # Hide the 'add agent' button in the front-end
analyticsEnabled: "true" # Enable or disable the Analytics tab on device detail pages 'true' or 'false'
developerToolsEnabled: "true" # Enable or disable device logs and terminal access 'true' or 'false'
# Dashboard page
dashboard:
sitesFullscreenEnabled: "true" # Enable or disable the fullscreen Sites map 'true' or 'false'
# Media page
media:
filter:
@@ -475,6 +582,8 @@ kerberoshub:
enabled: "true" # Enable or disable object detection filter in media 'true' or 'false'
star:
enabled: "true" # Enable or disable star filter in media 'true' or 'false'
analysis:
enabled: "true" # Enable or disable the "with analysis" filter (hides recordings without markers, description or detections) in media 'true' or 'false'
region:
enabled: "true" # Enable or disable region filter in media 'true' or 'false'
sort:
@@ -483,6 +592,11 @@ kerberoshub:
enabled: "true" # Enable or disable category filter in media 'true' or 'false'
markers:
enabled: "true" # Enable or disable markers filter in media 'true' or 'false'
markerOptionsByDate:
enabled: "false" # Limit marker-related filter options to the selected recording day
markerCategoryPriority:
enabled: "false" # Order marker options from configured categories before other markers
categories: [] # Ordered marker category names, for example ["security", "traffic"]
events:
enabled: "true" # Enable or disable events filter in media 'true' or 'false'
tags:
@@ -490,6 +604,13 @@ kerberoshub:
defaultView: "timeline" # Default view for media page 'timeline' or 'grid'
# Chart feature
chart:
colors:
- "#84569f"
- "#3ba7ff"
- "#ff5c8a"
- "#4ad991"
- "#ffb84d"
- "#ff7043"
colorChartSelectionFill: "rgba(132, 86, 159, 0.07)"
colorChartSelectionStroke: "rgba(132, 86, 159, 0.4)"
colorChartGridStroke: "rgba(0, 106, 255, 0.18)"
@@ -517,6 +638,7 @@ kerberoshub:
# Face redaction feature
faceRedaction:
enabled: "false" # Enable or disable face redaction 'true' or 'false'
classifierTracksEnabled: "true" # Make classifier-generated tracks available in the redaction modal
# Optional integrations
mixpanel: # We can keep track logging in Mixpanel as well
apikey: "xxx"
@@ -552,7 +674,7 @@ kerberoshub:
cleanup:
repository: ghcr.io/uug-ai/hub-cleanup
pullPolicy: IfNotPresent
tag: "v1.4.13"
tag: "v1.4.19"
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).
@@ -588,14 +710,285 @@ kerberoshub:
# Tasks with `legal_hold=true` are never deleted. Must match the value
# used by `kerberoshub.api.defaultTaskRetentionDays`.
defaultTaskRetentionDays: "0"
# Deployment-wide retention for audit events. This is independent from
# recording plans, inactive-account cleanup, and the optional global pass.
# Set to "0" or a negative value to retain audit events indefinitely.
auditEventRetentionDays: "400"
resources:
requests:
memory: 10Mi
cpu: 10m
# Dispatches canonical audit_events to one or more external webhook sinks.
# Credentials are always read from existing Secrets and never stored here.
audit:
repository: ghcr.io/uug-ai/hub-audit
pullPolicy: IfNotPresent
tag: "v1.0.1"
replicas: 1
dispatchInterval: "5s"
leaseDuration: "2m"
terminationGracePeriodSeconds: 150 # Keep above leaseDuration so an in-flight cycle can finish.
topologySpreadConstraints: []
volumes: []
volumeMounts: []
extraEnv: []
serviceMonitor:
enabled: true
interval: 15s
destinations: []
# - id: primary-siem
# type: webhook
# enabled: true
# url: https://siem.example.com/api/audit
# timeoutSeconds: 10
# batchSize: 100
# maxAttempts: 10
# startAt: latest # latest | beginning, applied only on first creation
# headers:
# X-Source: kerberos-hub
# bearerTokenSecret:
# name: audit-primary-siem
# key: token
# secretHeaders:
# X-Api-Key:
# secretName: audit-primary-siem
# secretKey: api-key
# filters:
# organisationIds: []
# projectIds: []
# actions: []
# categories: []
# outcomes: [] # success | failure
# resources:
# requests:
# memory: 32Mi
# cpu: 10m
# hub-workflows is the standalone, queue-driven workflow engine. It consumes
# pipeline events and dispatches the stages declared in its workflow
# definitions, 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 workflow
# definitions 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.
#
# -----------------------------------------------------------------------
# Global workflow definitions — the named workflows the engine runs.
#
# `definitions` is the engine's deployment-global configuration source and
# stage catalog: several distinct workflows can run over one recording, each
# opening its own run and dispatching only its own stages. Database-backed
# organisation workflows, when present, are read separately per recording.
# This is a MAP keyed by workflow name (names are unique and merge cleanly
# across -f / --set overrides). Ships empty; the commented block is a worked
# example of an object-tracking + loitering pipeline. Add more keys to run
# more config workflows.
#
# Each definition:
# id optional stable identity: quote a non-zero 24-hex Mongo ObjectID.
# Recommended for new definitions; rendered lowercase. Omit for
# legacy SHA256(exact map key)[:24] identity. Pin that old ID before
# renaming an existing workflow; changing id creates a new identity.
# Invalid IDs and duplicate effective IDs fail Helm rendering,
# including disabled definitions. Name/version are not identity
# when id is explicit. No per-project on/off overrides are provided.
# enabled include this workflow (soft-delete toggle).
# source always rendered as "config" (a Helm-defined, ops-managed,
# deployment-global workflow — read-only in the API).
# triggers how a run OPENS. Omit for a single bare automatic trigger
# (opens for every recording); the per-stage `needs` then decide
# which stages FIRE. Narrow with device/schedule triggers, e.g.
# `- {type: automatic, devices: [{key: <device-key>}]}`.
# stages the executable stages, each {operation, dispatch?, needs?,
# needsMode?}. dispatch is "always" (default) or "conditional";
# a conditional stage's `needs` are upstream dependencies, each
# {operation?, condition?} — operation is the readiness GATE (the
# upstream op whose data must be present before the condition is
# read; omit for a check on the run root itself), condition is
# {path, op, value} where path is ABSOLUTE from the run root;
# a `*` segment fans out across array elements. needsMode combines
# multiple needs: "any" (default;
# fire on the first match) or "all" (a join; fire once every need
# has resolved and matched). The queue is taken from the matching
# services.<operation> entry, so dispatch and consume cannot drift.
#
# Every stage `operation` must have a deployed worker under
# kerberoshub.services.<operation> (deploy the objecttracking / loitering
# workers below).
definitions: {}
# Replace {} above with the indented examples below to enable them. Their IDs
# are pinned to the existing name-derived identities; do not regenerate them.
# tracking-workflow:
# id: "ec7e72bc4cedd32018e1f92a"
# enabled: true
# triggers:
# - type: automatic
# stages:
# - operation: objecttracking
# dispatch: always
# - operation: loitering
# dispatch: conditional
# # Fire loitering once objecttracking has resolved — a readiness join
# # (no condition ⇒ gate on the upstream's presence, not a value).
# needs:
# - operation: objecttracking
# vlm-workflow:
# id: "9e3666c56854b2befdb1f645"
# # Describe every recording with the hub-vlm worker (services.vlm below).
# enabled: true
# triggers:
# - type: automatic
# stages:
# - operation: vlm
# dispatch: always
# Stages tenants may place on the workflow editor canvas next to the device
# node. Each entry needs a deployed worker (services.<operation>.queue);
# entries without one are hidden. Empty means the editor offers no stages.
editorStages: []
# - operation: vlm
# name: Describe recording
# description: Summarise the recording with a vision language model.
# - operation: forwarder
# name: Forward to my system
# description: Send the result to the external destination configured for the forwarder.
# 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. 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 (a workflows.definitions stage of the same operation) — it runs only
# when its own `enabled` is true AND the workflows engine is enabled.
# Worker-specific settings go in `env` (plain key/values) and `secretEnv`
# (ENV_NAME: {name, key} of an existing Secret, for credentials).
services:
# hub-workflows — the workflows engine (orchestrator). Consumes the engine
# queue, evaluates the boot-loaded config workflows (WORKFLOW_DEFINITIONS)
# plus organisation-scoped database workflows, 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
# ---------------------------------------------------------------------
# EXAMPLE custom stage worker (commented out) — hub-loitering.
#
# Companion deployment for the workflows.definitions example above (the
# loitering stage of tracking-workflow). Uncomment to deploy the demo
# worker. It ships as its own repository/module.
# See https://github.com/uug-ai/hub-loitering.
#loitering:
# # Deploy the hub-loitering worker.
# enabled: true
# repository: ghcr.io/uug-ai/hub-loitering
# 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 (LOITERING_QUEUE). This
# # same value is taken into the matching workflows.definitions stage, so the
# # engine dispatches and the worker consumes the same queue with no drift.
# # Convention: "kcloud-<operation>-queue.fifo".
# queue: "kcloud-loitering-queue.fifo"
# resources:
# requests:
# memory: 10Mi
# cpu: 10m
# ---------------------------------------------------------------------
# EXAMPLE stage worker (commented out) for the workflows.definitions example
# above — hub-objecttracking. Uncomment the worker whose operation a
# definition references, so the engine dispatches and the worker consumes the
# same queue with no drift.
#objecttracking:
# # Deploy the object-tracking worker (operation "objecttracking").
# enabled: true
# repository: ghcr.io/uug-ai/hub-objecttracking
# pullPolicy: IfNotPresent
# tag: "v1.0.0"
# replicas: 1 # Number of pods for the worker.
# topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# logLevel: "info" # possible values: trace, debug, info, warn, error
# # Queue this worker consumes dispatched messages from (OBJECTTRACKING_QUEUE).
# queue: "kcloud-objecttracking-queue.fifo"
# resources:
# requests:
# memory: 10Mi
# cpu: 10m
# ---------------------------------------------------------------------
# EXAMPLE stage worker (commented out) — hub-vlm (operation "vlm").
#
# Samples frames from the recording, describes them with an Azure OpenAI
# vision model and emits timeline markers plus a recording description.
# Route to it with a workflows.definitions stage `- operation: vlm`.
# See https://github.com/uug-ai/hub-vlm for every VLM_* / AZURE_OPENAI_*
# setting. Create the API key Secret first, e.g.
# kubectl create secret generic hub-vlm-azure-openai \
# --from-literal=apiKey=<azure-openai-api-key>
#vlm:
# # Deploy the hub-vlm worker.
# enabled: true
# repository: ghcr.io/uug-ai/hub-vlm
# pullPolicy: IfNotPresent
# tag: "v1.0.0"
# replicas: 1 # Number of pods for the worker.
# topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# logLevel: "info" # possible values: trace, debug, info, warn, error
# # Queue this worker consumes dispatched messages from (VLM_QUEUE).
# queue: "hub-vlm-queue"
# env:
# AZURE_OPENAI_ENDPOINT: "https://<resource>.openai.azure.com"
# AZURE_OPENAI_DEPLOYMENT: "gpt-5.4-mini" # Name of the model deployment.
# AZURE_OPENAI_REASONING_EFFORT: "low" # none | minimal | low | medium | high
# VLM_MAX_FRAMES: "8" # Frames sampled per recording (max 32).
# VLM_FRAME_WIDTH: "1024" # Maximum frame width in pixels.
# VLM_SET_DESCRIPTION: "true" # Write the summary to the recording description.
# # The API key is read from an existing Secret, never from values.
# secretEnv:
# AZURE_OPENAI_API_KEY:
# name: hub-vlm-azure-openai
# key: apiKey
# resources:
# requests:
# memory: 128Mi
# cpu: 100m
monitordevice:
repository: ghcr.io/uug-ai/hub-monitor-device
pullPolicy: IfNotPresent
tag: "v1.4.0"
tag: "v1.4.2"
replicas: 1 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
logLevel: "info" # possible values: trace, debug, info, warn, error
@@ -615,7 +1008,7 @@ kerberoshub:
repository: uugai/hub-reactivatesubscriptions
pullPolicy: IfNotPresent
tag: "v1.0.2"
replicas: 1 # Number of pods for the service.
replicas: 0 # Number of pods for the service. Set to 0 to disable.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
#volumes:
@@ -648,10 +1041,11 @@ kerberoshub:
# cpu: 10m
# This proxy is legacy for the old agent, will be migrated in the new Hub API.
proxy:
enabled: false
repository: uugai/hub-proxy
pullPolicy: IfNotPresent
tag: "v1.0.0"
replicas: 1 # Number of pods for the service.
replicas: 0 # Number of pods for the service when enabled.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
#volumes:
@@ -671,9 +1065,13 @@ kerberoshub:
# The pipeline is a critical component of Kerberos Hub, and should be monitored closely.
kerberospipeline:
event:
deploymentAnnotations: {}
# Example (opt-in Argo CD ordering):
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "10"
repository: ghcr.io/uug-ai/hub-pipeline-event
pullPolicy: IfNotPresent
tag: "v1.3.0"
tag: "v1.3.1"
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).
@@ -690,8 +1088,11 @@ kerberospipeline:
cpu: 10m
monitor:
repository: ghcr.io/uug-ai/hub-pipeline-monitor
deploymentAnnotations: {}
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "70"
pullPolicy: IfNotPresent
tag: "v1.3.9"
tag: "v1.3.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).
@@ -701,14 +1102,18 @@ kerberospipeline:
#volumeMounts:
# - name: extra
# mountPath: /data
logLevel: "info" # possible values: debug, info, warn, error
resources:
requests:
memory: 10Mi
cpu: 10m
sequence:
repository: ghcr.io/uug-ai/hub-pipeline-sequence
deploymentAnnotations: {}
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "60"
pullPolicy: IfNotPresent
tag: "v1.6.18"
tag: "v1.6.26"
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).
@@ -724,8 +1129,11 @@ kerberospipeline:
cpu: 10m
throttler:
repository: uugai/hub-pipeline-throttler
deploymentAnnotations: {}
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "30"
pullPolicy: IfNotPresent
tag: "v1.2.0"
tag: "v1.2.1"
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).
@@ -742,11 +1150,17 @@ kerberospipeline:
cpu: 10m
notify:
repository: ghcr.io/uug-ai/hub-pipeline-notification
deploymentAnnotations: {}
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "20"
pullPolicy: IfNotPresent
tag: "v1.3.9"
tag: "v1.3.18"
replicas: 1 # Number of pods for the service.
audit:
enabled: true
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
logLevel: "info" # possible values: trace, debug, info, warn, error
notificationExpiryMinutes: "15" # Maximum recording age before notification delivery; 0 disables expiry.
resources:
requests:
memory: 10Mi
@@ -762,7 +1176,7 @@ kerberospipeline:
notifyTest:
repository: uugai/hub-pipeline-notification-test
pullPolicy: IfNotPresent
tag: "v1.2.1"
tag: "v1.2.2"
replicas: 1 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
resources:
@@ -779,8 +1193,11 @@ kerberospipeline:
# claimName: custom-layout-claim
analysis:
repository: ghcr.io/uug-ai/hub-pipeline-analysis
deploymentAnnotations: {}
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "40"
pullPolicy: IfNotPresent
tag: "v1.7.8"
tag: "v1.8.5"
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).
@@ -797,8 +1214,11 @@ kerberospipeline:
cpu: 10m
dominantColor:
repository: ghcr.io/uug-ai/hub-pipeline-dominantcolors
deploymentAnnotations: {}
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "50"
pullPolicy: IfNotPresent
tag: "v2.0.2"
tag: "v2.0.3"
replicas: 3 # Number of pods for the service.
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
# Optional extra volumes / volumeMounts for this deployment (empty = none).
@@ -818,8 +1238,11 @@ kerberospipeline:
cpu: 1000m
thumbnail:
repository: ghcr.io/uug-ai/hub-pipeline-thumbnail
deploymentAnnotations: {}
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "50"
pullPolicy: IfNotPresent
tag: "v1.3.4"
tag: "v1.3.10"
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).
@@ -844,8 +1267,11 @@ kerberospipeline:
cpu: 1000m
counting:
repository: uugai/hub-pipeline-counting
deploymentAnnotations: {}
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "50"
pullPolicy: IfNotPresent
tag: "v1.6.3"
tag: "v2.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).
@@ -862,9 +1288,12 @@ kerberospipeline:
cpu: 10m
sprite:
enabled: false # Enable or disable the sprite generation 'true' or 'false
deploymentAnnotations: {}
# deploymentAnnotations:
# argocd.argoproj.io/sync-wave: "50"
repository: ghcr.io/uug-ai/hub-pipeline-sprite
pullPolicy: IfNotPresent
tag: "v1.1.12"
tag: "v1.1.16"
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).
@@ -876,8 +1305,10 @@ kerberospipeline:
# mountPath: /data
logLevel: "info" # possible values: trace, debug, info, warn,
interval: "1" # Number of secondes between each thumbnail in the sprite
width: "240" # Should not be changed for the moment (hard coded in UI)
height: "135" # Should not be changed for the moment (hard coded in UI)
# Tile size in pixels. Preserve width:height = 16:9 (e.g. 240x135, 480x270, 640x360).
# Deploy dimension-aware sprite/analysis workers, Hub API and frontend before increasing.
width: "240"
height: "135"
resources:
requests:
memory: 512Mi
@@ -888,7 +1319,7 @@ kerberospipeline:
export:
repository: ghcr.io/uug-ai/hub-pipeline-export
pullPolicy: IfNotPresent
tag: "v1.2.4"
tag: "v1.2.10"
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
@@ -904,27 +1335,6 @@ kerberospipeline:
requests:
memory: 10Mi
cpu: 10m
redaction:
repository: ghcr.io/uug-ai/hub-pipeline-redaction
pullPolicy: IfNotPresent
tag: "v1.0.0"
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).
#volumes:
# - name: extra
# emptyDir: {}
#volumeMounts:
# - name: extra
# mountPath: /data
logLevel: "info" # possible values: trace, debug, info, warn, error
resources:
requests:
memory: 512Mi
cpu: 500m
limits:
memory: 2Gi
cpu: 1000m
###########################################################################
# Email configuration
#
@@ -952,11 +1362,15 @@ 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: "disabled"
disabled: "disable"
highupload: "highupload"
device: "device"
alertTitle: "[Alert] Kerberos Hub detected something an event"
deviceTitle: "[Device] An Agent's status has been changed"
deviceTitle: "[Device] A Kerberos Agent's status has been changed"
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
set -euo pipefail
CHART_DIR="${1:-charts/hub}"
# Supply per-service test overrides without requiring a separate values file.
WAVE_FLAGS=()
for setting in event:10 notify:20 throttler:30 analysis:40 thumbnail:50 sprite:50 dominantColor:50 counting:50 sequence:60 monitor:70; do
service="${setting%%:*}"
wave="${setting#*:}"
WAVE_FLAGS+=(--set-string "kerberospipeline.${service}.deploymentAnnotations.argocd\\.argoproj\\.io/sync-wave=${wave}")
done
# Only read Deployment metadata, never pod-template or Service annotations.
extract_waves() {
awk '
/^---/ { deployment=0; metadata=0; name="" }
/^kind: Deployment$/ { deployment=1 }
/^metadata:$/ { metadata=1; next }
/^[^ #]/ { metadata=0 }
deployment && metadata && /^ name:/ { name=$2 }
deployment && metadata && /^ argocd.argoproj.io\/sync-wave:/ {
value=$2
gsub(/"/, "", value)
print name, value
}
' | sort
}
assert_waves() {
local rendered="$1"
local expected="$2"
local actual
actual="$(printf '%s\n' "$rendered" | extract_waves)"
if [[ "$actual" != "$expected" ]]; then
printf 'Unexpected Deployment waves:\n%s\nExpected:\n%s\n' "$actual" "$expected" >&2
return 1
fi
local all_count expected_count
all_count="$(printf '%s\n' "$rendered" | grep -c 'argocd.argoproj.io/sync-wave:' || true)"
expected_count="$(printf '%s\n' "$expected" | awk 'NF { count++ } END { print count+0 }')"
if [[ "$all_count" != "$expected_count" ]]; then
echo "Sync-wave annotation leaked outside expected Deployment metadata" >&2
return 1
fi
}
expected="$(printf '%s\n' \
'pipe-event 10' 'pipe-notify 20' 'pipe-throttler 30' 'pipe-analysis 40' \
'pipe-thumbnail 50' 'pipe-sprite 50' 'pipe-dominantcolor 50' \
'pipe-counting 50' 'pipe-sequence 60' 'pipe-monitor 70' | sort)"
for mode in all pipeline; do
default_render="$(helm template hub "$CHART_DIR" --set "mode=$mode" --set kerberospipeline.sprite.enabled=true)"
assert_waves "$default_render" ""
if printf '%s\n' "$default_render" | grep -q '^ annotations: null'; then
echo "Empty annotations must be omitted" >&2
exit 1
fi
configured_render="$(helm template hub "$CHART_DIR" "${WAVE_FLAGS[@]}" --set "mode=$mode" --set kerberospipeline.sprite.enabled=true)"
assert_waves "$configured_render" "$expected"
echo "OK ($mode): defaults opt out; per-service values assign all ten Deployment waves"
done
disabled_sprite="$(helm template hub "$CHART_DIR" "${WAVE_FLAGS[@]}" --set mode=pipeline)"
assert_waves "$disabled_sprite" "$(printf '%s\n' "$expected" | grep -v '^pipe-sprite ')"
ui_render="$(helm template hub "$CHART_DIR" "${WAVE_FLAGS[@]}" --set mode=ui --set kerberospipeline.sprite.enabled=true)"
assert_waves "$ui_render" ""
# Exercise zero/negative strings and an unrelated annotation on the same map.
for wave in 0 -10; do
override="$(helm template hub "$CHART_DIR" --set mode=pipeline \
--set-string "kerberospipeline.event.deploymentAnnotations.argocd\\.argoproj\\.io/sync-wave=$wave" \
--set-string 'kerberospipeline.event.deploymentAnnotations.example\.com/owner=queue-team')"
assert_waves "$override" "pipe-event $wave"
printf '%s\n' "$override" | grep -q "^ argocd.argoproj.io/sync-wave: \"$wave\"$"
printf '%s\n' "$override" | grep -q '^ example.com/owner: queue-team$'
done
echo "All pipeline Deployment annotation checks passed."
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env bash
# Chart-only checks; no cluster or application loader required.
set -euo pipefail
CHART_DIR="${1:-charts/hub}"
assert_contains() {
if ! grep -Fq -- "$2" <<<"$1"; then
printf 'FAIL: expected rendered output to contain %s\n' "$2" >&2
exit 1
fi
}
assert_absent() {
if grep -Fq -- "$2" <<<"$1"; then
printf 'FAIL: unexpected rendered output contains %s\n' "$2" >&2
exit 1
fi
}
expect_failure() {
local expected="$1"
shift
local output
if output="$(helm template hub "$CHART_DIR" "$@" 2>&1)"; then
printf 'FAIL: render unexpectedly succeeded: %s\n' "$*" >&2
exit 1
fi
assert_contains "$output" "$expected"
}
if grep -q '^classificationCatalog:' "$CHART_DIR/values.yaml"; then
echo "FAIL: classificationCatalog should be omitted from default values" >&2
exit 1
fi
default_out="$(helm template hub "$CHART_DIR")"
assert_absent "$default_out" "name: workflow-configuration"
assert_absent "$default_out" "checksum/workflow-configuration"
assert_contains "$default_out" "value: /etc/kerberos/classifications/classifications.json"
legacy_out="$(helm template hub "$CHART_DIR" --set workflowConfiguration=null)"
if [ "$default_out" != "$legacy_out" ]; then
echo "FAIL: absent workflowConfiguration changed the default render" >&2
exit 1
fi
null_out="$(helm template hub "$CHART_DIR" --set classificationCatalog=null)"
if [ "$default_out" != "$null_out" ]; then
echo "FAIL: omitted and null classificationCatalog render differently" >&2
exit 1
fi
# A file-backed default must render identically to the previous inline-list form.
explicit_out="$(helm template hub "$CHART_DIR" \
--set-json "classificationCatalog=$(tr -d '\n' <"$CHART_DIR/configuration/catalogs/classifications.json")")"
if [ "$default_out" != "$explicit_out" ]; then
echo "FAIL: bundled defaults and explicit classification list render differently" >&2
exit 1
fi
override_flags=(--set-json 'classificationCatalog=[{"key":"forklift","label":"Forklift","icon":"vehicle"}]')
override_out="$(helm template hub "$CHART_DIR" "${override_flags[@]}")"
assert_contains "$override_out" '"key": "forklift"'
assert_absent "$override_out" '"key": "pedestrian"'
empty_out="$(helm template hub "$CHART_DIR" --set-json 'classificationCatalog=[]')"
assert_contains "$empty_out" " []"
assert_absent "$empty_out" '"key": "pedestrian"'
disabled_out="$(helm template hub "$CHART_DIR" --set classificationCatalogEnabled=false)"
assert_absent "$disabled_out" "name: classification-catalog"
assert_absent "$disabled_out" "CLASSIFICATION_CATALOG_FILE"
bundle_out="$(helm template hub "$CHART_DIR" --set workflowConfiguration.enabled=true)"
assert_contains "$bundle_out" "checksum/workflow-configuration:"
assert_contains "$bundle_out" "mountPath: /etc/kerberos/configuration"
assert_contains "$bundle_out" "path: workflow-contracts/start.yaml"
assert_contains "$bundle_out" "path: catalogs/classifications.json"
assert_contains "$bundle_out" "source: project.devices"
assert_contains "$bundle_out" "file: ../catalogs/classifications.json"
assert_absent "$bundle_out" "WORKFLOW_CONTRACTS"
bundle_config="$(helm template hub "$CHART_DIR" --set workflowConfiguration.enabled=true \
--show-only templates/configmap-workflow-configuration.yaml)"
assert_absent "$bundle_config" "classifications.json: |-"
override_bundle="$(helm template hub "$CHART_DIR" --set workflowConfiguration.enabled=true "${override_flags[@]}")"
assert_contains "$override_bundle" '"key": "forklift"'
assert_absent "$override_bundle" '"key": "pedestrian"'
external_flags=(-f "$CHART_DIR/examples/workflow-configuration-values.yaml")
external_out="$(helm template hub "$CHART_DIR" "${external_flags[@]}" --set classificationCatalogEnabled=false)"
assert_contains "$external_out" 'name: "custom-workflow-configuration"'
assert_contains "$external_out" "path: workflow-contracts/pose.yaml"
assert_contains "$external_out" "path: catalogs/pose-keypoints.json"
assert_absent "$external_out" "name: hub-workflow-configuration"
assert_absent "$external_out" "checksum/workflow-configuration"
ui_out="$(helm template hub "$CHART_DIR" --set mode=ui --set workflowConfiguration.enabled=true)"
assert_contains "$ui_out" "name: hub-workflow-configuration"
pipeline_out="$(helm template hub "$CHART_DIR" --set mode=pipeline --set workflowConfiguration.enabled=true)"
assert_absent "$pipeline_out" "name: hub-workflow-configuration"
assert_absent "$pipeline_out" "name: workflow-configuration"
expect_failure "requires classificationCatalogEnabled" \
--set workflowConfiguration.enabled=true --set classificationCatalogEnabled=false
expect_failure "items is required" \
--set workflowConfiguration.enabled=true --set workflowConfiguration.existingConfigMap=custom
expect_failure "items requires existingConfigMap" "${external_flags[@]}" --set workflowConfiguration.existingConfigMap=
expect_failure "duplicate workflowConfiguration.items path" "${external_flags[@]}" \
--set 'workflowConfiguration.items[1].path=workflow-contracts/pose.yaml'
expect_failure "workflowConfiguration" "${external_flags[@]}" \
--set 'workflowConfiguration.items[0].path=../../secret.yaml'
expect_failure "workflowConfiguration" "${external_flags[@]}" \
--set 'workflowConfiguration.items[0].path=/etc/secret.yaml'
expect_failure "classificationCatalog" --set classificationCatalog=not-a-list
echo "All workflow configuration and classification catalog checks passed."
+233
View File
@@ -0,0 +1,233 @@
#!/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.
#
# Also checks workflow identity validation and identical WORKFLOW_DEFINITIONS
# in the API and engine. Requires Helm and Python 3 (standard library only).
#
# 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. The chart
# ships NO enabled stage worker by default (custom stages are values-only and
# opt-in), so we synthesise a throwaway stage purely to exercise the generic
# hub-stage path. The name is a neutral fixture ("queuecheck") on purpose: any
# arbitrary stage key must render the same way, so the check must not depend on
# a specific bundled worker.
STAGE="queuecheck"
RENDER_FLAGS=(
--set mode=all
--set kerberoshub.workflows.enabled=true
--set "kerberoshub.workflows.stages.${STAGE}.enabled=true"
--set "kerberoshub.services.${STAGE}.enabled=true"
--set "kerberoshub.services.${STAGE}.repository=example.invalid/queuecheck"
--set "kerberoshub.services.${STAGE}.tag=test"
--set "kerberoshub.services.${STAGE}.queue=queuecheck-fixture-queue"
)
# 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
assert_definitions() {
local rendered="$1" expected="$2" label="$3"
printf '%s\n' "$rendered" | python3 -c '
import json
import re
import sys
expected = json.loads(sys.argv[1])
label = sys.argv[2]
actual = {}
for document in sys.stdin.read().split("\n---"):
match = re.search(r"(?m)^[ \t]*- name: WORKFLOW_DEFINITIONS[ \t]*\n[ \t]*value: (\".*\")$", document)
if not match:
continue
name = re.search(r"(?m)^metadata:\n name: (.+)$", document).group(1)
assert name not in actual, f"duplicate WORKFLOW_DEFINITIONS in {name}"
actual[name] = json.loads(json.loads(match.group(1)))
assert set(actual) == {"hub-api", "hub-workflows"}, f"unexpected deployments: {set(actual)}"
for name, definitions in actual.items():
assert definitions == expected, f"{label}: {name}: {definitions!r} != {expected!r}"
print(f"OK ({label}): API and engine receive identical expected workflow definitions")
' "$expected" "$label"
}
assert_render_failure() {
local label="$1" expected="$2"; shift 2
local output
if output="$(helm template hub "${CHART_DIR}" "${RENDER_FLAGS[@]}" "$@" 2>&1)"; then
echo "FAIL (${label}): Helm accepted invalid workflow identities" >&2
return 1
fi
if [[ "$output" != *"$expected"* ]]; then
printf 'FAIL (%s): expected diagnostic %s, got:\n%s\n' "$label" "$expected" "$output" >&2
return 1
fi
echo "OK (${label}): Helm rejected invalid workflow identities"
}
assert_definitions "$default_out" '[]' "empty defaults"
echo "== Checking explicit and legacy workflow definitions =="
identity_fixture='{
" Legacy Workflow ": {
"enabled": true,
"stages": [
{"operation": "queuecheck"},
{"operation": "external", "dispatch": "conditional", "needs": [{"operation": "queuecheck"}], "needsMode": "all"}
]
},
"explicit": {
"id": "ABCDEF0123456789ABCDEF01",
"enabled": true,
"triggers": [{"type": "automatic", "devices": [{"key": "fixture-device"}]}],
"stages": []
},
"disabled": {"id": "abcdef0123456789abcdef02", "enabled": false}
}'
expected_definitions='[
{
"name": " Legacy Workflow ", "enabled": true, "source": "config",
"triggers": [{"type": "automatic"}],
"stages": [
{"operation": "queuecheck", "dispatch": "always", "queue": "queuecheck-fixture-queue"},
{"operation": "external", "dispatch": "conditional", "needs": [{"operation": "queuecheck"}], "needsMode": "all"}
]
},
{
"name": "explicit", "id": "abcdef0123456789abcdef01", "enabled": true, "source": "config",
"triggers": [{"type": "automatic", "devices": [{"key": "fixture-device"}]}],
"stages": []
}
]'
identity_out="$(helm template hub "${CHART_DIR}" "${RENDER_FLAGS[@]}" \
--set-json "kerberoshub.workflows.definitions=$identity_fixture")"
assert_definitions "$identity_out" "$expected_definitions" "explicit ID lowercase; legacy omission; disabled filtering; triggers and stages unchanged"
renamed_out="$(helm template hub "${CHART_DIR}" "${RENDER_FLAGS[@]}" \
--set-json "kerberoshub.workflows.definitions=${identity_fixture/\"explicit\"/\"renamed\"}")"
assert_definitions "$renamed_out" "${expected_definitions/\"explicit\"/\"renamed\"}" "explicit identity survives rename"
legacy_only_out="$(helm template hub "${CHART_DIR}" "${RENDER_FLAGS[@]}" \
--set-json 'kerberoshub.workflows.definitions={"legacy":{"enabled":true}}')"
assert_definitions "$legacy_only_out" \
'[{"name":"legacy","enabled":true,"source":"config","triggers":[{"type":"automatic"}],"stages":[]}]' \
"legacy-only values unchanged"
echo "== Checking bundled example identity pins =="
python3 - "$CHART_DIR/values.yaml" <<'PY'
import hashlib
import pathlib
import re
import sys
values = pathlib.Path(sys.argv[1]).read_text()
for name in ("tracking-workflow", "vlm-workflow"):
expected = hashlib.sha256(name.encode()).hexdigest()[:24]
match = re.search(r"(?m)^ # " + re.escape(name) + r':\n # id: "([^"]+)"$', values)
assert match and match.group(1) == expected, f"{name} must preserve its name-derived ID {expected}"
print(f"OK ({name}): example pins existing identity {expected}")
PY
echo "== Rejecting invalid explicit IDs, including disabled definitions =="
invalid_ids=('""' 'null' '"000000000000000000000000"' '0' '123' 'true' 'false' '[]' '{}'
'"abc"' '"zzzzzzzzzzzzzzzzzzzzzzzz"' '"abcdef0123456789abcdef01 "'
'"abcdef0123456789abcdef0"' '"abcdef0123456789abcdef012"')
invalid_diagnostic='kerberoshub.workflows.definitions["invalid"].id must be a non-zero 24-character hexadecimal string'
for enabled in true false; do
for id in "${invalid_ids[@]}"; do
assert_render_failure "id=$id, enabled=$enabled" "$invalid_diagnostic" \
--set-json "kerberoshub.workflows.definitions={\"invalid\":{\"enabled\":$enabled,\"id\":$id}}"
done
done
# Validation must not depend on either consumer being rendered.
for mode in all ui pipeline; do
assert_render_failure "engine disabled, mode=$mode" "$invalid_diagnostic" \
--set "mode=$mode" --set kerberoshub.workflows.enabled=false \
--set-json 'kerberoshub.workflows.definitions={"invalid":{"enabled":false,"id":null}}'
done
echo "== Rejecting duplicate effective IDs =="
legacy_id="$(python3 -c 'import hashlib; print(hashlib.sha256(b" Legacy Workflow ").hexdigest()[:24])')"
for first_enabled in true false; do
for second_enabled in true false; do
assert_render_failure "explicit collision, enabled=$first_enabled/$second_enabled" \
'duplicate effective workflow id "abcdef0123456789abcdef01" for "first" and "second"' \
--set-json "kerberoshub.workflows.definitions={\"first\":{\"enabled\":$first_enabled,\"id\":\"ABCDEF0123456789ABCDEF01\"},\"second\":{\"enabled\":$second_enabled,\"id\":\"abcdef0123456789abcdef01\"}}"
assert_render_failure "explicit/derived collision, enabled=$first_enabled/$second_enabled" \
"duplicate effective workflow id \"$legacy_id\" for \" Legacy Workflow \" and \"explicit\"" \
--set-json "kerberoshub.workflows.definitions={\" Legacy Workflow \":{\"enabled\":$first_enabled},\"explicit\":{\"enabled\":$second_enabled,\"id\":\"${legacy_id^^}\"}}"
done
done
echo "All workflow queue and identity checks passed."