Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
b662b26
build: ⬆️ refresh the frontend dependencies (Vite 8, Vitest 5, ESLint…
fungiboletus Oct 4, 2026
ebeaf53
feat: ✨ serve the web UI from SensApp under /ui/
fungiboletus Oct 4, 2026
62a8b13
feat: ✨ ask for a token in the UI when the API answers 401 or 403
fungiboletus Oct 4, 2026
cda37db
test: ✅ fail when frontend/openapi.json is not the OpenAPI document o…
fungiboletus Oct 4, 2026
7ff3a44
feat: ✨ aggregate wide chart ranges, and polish the UI for small screens
fungiboletus Oct 4, 2026
c8455c2
docs: 📝 close the frontend task with its results
fungiboletus Oct 4, 2026
e316060
feat: ✨ page the series list, follow the OS dark theme, drop the expl…
fungiboletus Oct 4, 2026
7f1b381
feat: ✨ choose the step and the aggregation of the chart
fungiboletus Oct 4, 2026
76a5096
feat: ✨ keep the explorer in the address, draw booleans, add chart st…
fungiboletus Oct 4, 2026
8cf5605
Merge branch 'main' into frontend-good-enough
fungiboletus Oct 5, 2026
d84f141
test: ✅ the freshest-rows test no longer depends on the clock
fungiboletus Oct 5, 2026
1d65fde
fix: 🐛 remote read hints: only aggregate when Prometheus gets the rig…
fungiboletus Oct 5, 2026
1358322
docs: 📝 what the remote read hints are answered with, and why
fungiboletus Oct 5, 2026
e23c5ec
test: ✅ live Prometheus harness runs v3.15.0, the latest release
fungiboletus Oct 5, 2026
33b168c
perf: ⚡️ frontend: one variable Roboto file instead of three weights …
fungiboletus Oct 5, 2026
c75189e
test: ✅ live Prometheus harness defaults to v3.13.4, the LTS line
fungiboletus Oct 5, 2026
a3d95f0
feat: ✨ frontend: no series count in the chart header, no health badg…
fungiboletus Oct 5, 2026
04fcf3d
feat: ✨ frontend: a preset range ends now, not when the page opened
fungiboletus Oct 5, 2026
df30d80
fix: 🐛 frontend: the chart keeps its data while a moved range loads
fungiboletus Oct 5, 2026
6c367cb
feat: ✨ frontend: series by label dimension, a color each, no chart l…
fungiboletus Oct 5, 2026
01157c8
feat: ✨ frontend: the SensApp logo and favicon, and a theme taken fro…
fungiboletus Oct 5, 2026
5a3e2d2
docs: 📝 the third frontend review round: what changed, what was check…
fungiboletus Oct 5, 2026
8ce0dcc
feat: ✨ frontend: one time window, moved from below the chart, zoomed…
fungiboletus Oct 5, 2026
ded2536
feat: ✨ frontend: sorted series list, labels that are on every series…
fungiboletus Oct 5, 2026
e90a6b6
feat: ✨ frontend: 24 colors, a highlight on hover, select all
fungiboletus Oct 5, 2026
a1a4935
docs: 📝 the fourth frontend review round: the time window, the colors…
fungiboletus Oct 5, 2026
29e56eb
feat: ✨ frontend: the whole window width, quiet controls with one bor…
fungiboletus Oct 5, 2026
d810735
feat: ✨ frontend: controls in the headers, a radio per metric, the li…
fungiboletus Oct 5, 2026
e4ef571
feat: ✨ frontend: the same space all around, one hover for the button…
fungiboletus Oct 5, 2026
f53b50b
feat: ✨ frontend: code snippets from the state of the explorer (Pytho…
fungiboletus Oct 5, 2026
14c4462
feat: ✨ frontend: a Code button, with the Python SDK and curl, in Jet…
fungiboletus Oct 5, 2026
698931d
feat: ✨ frontend: code snippets with a PEP 723 header for uv, and the…
fungiboletus Oct 5, 2026
91bc81b
feat: ✨ frontend: the code of the Code dialog wraps instead of scroll…
fungiboletus Oct 5, 2026
67c645f
feat: ✨ frontend: a Load Data tab, with Python SDK, Telegraf, Prometh…
fungiboletus Oct 5, 2026
52ddb82
feat: ✨ frontend: Load Data is a plain full-width page, with underlin…
fungiboletus Oct 5, 2026
44f115d
feat: ✨ frontend: Load Data scrolls, loses its title and subtitle, an…
fungiboletus Oct 5, 2026
d801c06
feat: ✨ auth: SensApp no longer runs open by default
fungiboletus Oct 5, 2026
fb7d0a5
feat: ✨ auth: admin scope, key rotation, token ids, and the subject i…
fungiboletus Oct 5, 2026
d4889a5
feat: ✨ auth: POST /api/v1/admin/tokens makes tokens for admin tokens
fungiboletus Oct 5, 2026
0f178aa
feat: ✨ frontend: a Credentials tab to make tokens, and a link that s…
fungiboletus Oct 5, 2026
3f8d9f4
feat: ✨ auth: chart, container, CI and docs follow the secure default
fungiboletus Oct 5, 2026
244feb3
feat: ✨ auth: sensor names with commas, and the scopes in the OpenAPI…
fungiboletus Oct 5, 2026
9009019
feat: ✨ frontend: Credentials in the layout of Load Data, with the co…
fungiboletus Oct 5, 2026
8aab696
feat: ✨ auth: the suggested admin token also reads
fungiboletus Oct 5, 2026
db26817
fix: 🐛 frontend: the scope checkboxes of Credentials are the blue of …
fungiboletus Oct 5, 2026
d8b8121
feat: ✨ frontend: a dice for the name of a token, and the token under…
fungiboletus Oct 5, 2026
3c3bde8
feat: ✨ frontend: the two ways to copy a token look the same and come…
fungiboletus Oct 5, 2026
9ca383a
feat: ✨ frontend: a cross closes the token, and so does any change of…
fungiboletus Oct 5, 2026
65b9e45
feat: ✨ frontend: the header is aligned on one line, the current tab …
fungiboletus Oct 5, 2026
48946e6
fix: 🐛 frontend: the line under the current tab goes down to the bott…
fungiboletus Oct 5, 2026
892157c
feat: ✨ frontend: the joined buttons keep one edge between them, disa…
fungiboletus Oct 5, 2026
60f47d5
chore: 🔧 frontend: the full-colour logos the fs8 ones are made from
fungiboletus Oct 5, 2026
fc610e8
feat: ✨ frontend: the way out is on the far right of the header
fungiboletus Oct 5, 2026
20bee2d
feat: 📝 improve readme
fungiboletus Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ compose.test-services.yml
*.duckdb
*.sqlite
*.log
frontend/node_modules
frontend/dist
14 changes: 13 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: '22'
node-version: '24'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json

Expand Down Expand Up @@ -393,10 +393,22 @@ jobs:
cache-from: type=gha,scope=runtime-smoke
cache-to: type=gha,scope=runtime-smoke,mode=max

- name: The image refuses to start without authentication
if: matrix.name == 'runtime'
run: |
# No secret and no SENSAPP_AUTH_DISABLED: the image listens on 0.0.0.0, which is not safe open
if docker run --rm sensapp-ci:runtime > refusal.log 2>&1; then
echo "The image started without authentication"
exit 1
fi
cat refusal.log
grep -q SENSAPP_JWT_SECRET refusal.log

- name: Start runtime image against ClickHouse
if: matrix.name == 'runtime'
run: >-
docker run --detach --name sensapp-ci-runtime --network host
--env SENSAPP_AUTH_DISABLED=true
--env SENSAPP_STORAGE_CONNECTION_STRING=clickhouse://default:password@127.0.0.1:8123/sensapp_test
sensapp-ci:runtime

Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ arrow-array = "60"
anyhow = "1.0"
async-trait = "0.1"
thiserror = "2.0"
sha2 = "0.10"
axum = { version = "0.8", features = ["multipart", "macros"] }
base64 = "0.22"
futures = "0.3"
Expand All @@ -60,6 +61,7 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
uuid = { version = "1.18", features = ["serde"] }
fastrand = "2"
getrandom = "0.3"
csv-async = "1.3"
rust_decimal = "1.39"
geo = "0.32"
Expand Down
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# The web UI is static files: build them once, on the build platform, whatever the target.
FROM --platform=$BUILDPLATFORM node:24-slim AS frontend

WORKDIR /frontend

COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci

COPY frontend/ ./
RUN npm run build

FROM rust:1.98-slim-bookworm AS chef

ARG DUCKDB_DOWNLOAD_LIB="1"
Expand Down Expand Up @@ -77,8 +88,14 @@ COPY --from=builder /app/target/release/sensapp /usr/local/bin/sensapp
COPY --from=builder /out/lib/ /usr/local/lib/
RUN ldconfig

# Served under /ui/, see SENSAPP_UI_ENABLED
COPY --from=frontend /frontend/dist /usr/share/sensapp/ui

# The image listens on every address, so it does not start without authentication: give it
# SENSAPP_JWT_SECRET (`sensapp generate-secret` makes one), or SENSAPP_AUTH_DISABLED=true.
ENV SENSAPP_ENDPOINT=0.0.0.0 \
SENSAPP_PORT=3000 \
SENSAPP_UI_DIR=/usr/share/sensapp/ui \
SENSAPP_STORAGE_CONNECTION_STRING=sqlite:///var/lib/sensapp/sensapp.db \
RUST_LOG=info

Expand Down
32 changes: 18 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ![SensApp](./docs/sensapp_logo.png)

SensApp is an open-source sensor data platform developed by SINTEF.
SensApp is an open-source sensor data platform developed by [SINTEF](https://www.sintef.no/).

It handles time-series data ingestion, storage, and retrieval. From small edge devices to big data digital twins, SensApp *may* be useful.

Expand All @@ -10,22 +10,23 @@ SensApp is compatible with Prometheus and InfluxDB, but with an alternative arch

Dealing with system statistics for the last 24 hours? InfluxDB or Prometheus are excellent choices. Fetching average bathroom temperatures over the last 10 years grouped by day? SensApp will compute that instantly while InfluxDB or Prometheus will take a little while.

But you don't have to chose, both InfluxDB and Prometheus can replicate their data to SensApp for long-term storage and analysis. So you get the best of both worlds.
But you don't have to chose, both InfluxDB and Prometheus can replicate their data to SensApp for long-term storage and analysis. So you can get the best of both worlds.

You can also use SensApp as a standalone time-series database.
You can also use SensApp as a standalone time-series database. It's designed to work from the edge to the big data workloads in the cloud, by relying on different databases.

## Quickstart
![GUI Screenshot](./docs/screenshot.png)

The quickest way to run SensApp is with SQLite so no external database is required.
## Quickstart

Start SensApp with SQLite:
The simplest way to test SensApp is using it with SQLite and no authentication:

```bash
SENSAPP_STORAGE_CONNECTION_STRING=sqlite://sensapp.db \
cargo run
export SENSAPP_STORAGE_CONNECTION_STRING=sqlite://sensapp.db
export SENSAPP_AUTH_DISABLED=true
cargo run --release
```

By default, SensApp listens on [http://127.0.0.1:3000](http://127.0.0.1:3000).
By default, SensApp listens on [http://127.0.0.1:3000](http://127.0.0.1:3000) for both its API and user interface.

Ingest one sample:

Expand Down Expand Up @@ -59,7 +60,7 @@ async def main():
asyncio.run(main())
```

### Using Containers
## Software Containers and Kubernetes

```bash
docker compose up
Expand All @@ -76,6 +77,7 @@ Published releases also provide the chart through GHCR as an OCI package; see th
## Features

- **HTTP REST API**
- **Graphical User Interface**
- **Prometheus Compatibility**
- **Prometheus Remote Write**: Prometheus can push data to SensApp.
- **Prometheus Remote Read**: Prometheus can also read data from SensApp.
Expand All @@ -102,7 +104,7 @@ SensApp's architecture is relatively simple as the complex problems are delegate

Most of the complexity lies in the [database schema design](docs/DATAMODEL.md). After that, it's mostly some code glue.

- On the **edge**, SensApp can be deployed as a single lightweight instance with an embedded SQLite database.
- On the **edge**, SensApp can be deployed as a single instance with a SQLite database.
- For **medium** deployments, SensApp can be deployed with a PostgreSQL database.
- For **larger** deployments, many SensApp instances can be deployed behind a load balancer, connected to a ClickHouse database cluster. A message queue and some middleware can be considered to ingest data at scale.

Expand All @@ -112,11 +114,13 @@ Check the [ARCHITECTURE.md](docs/ARCHITECTURE.md) file for more details.

## Authentication

SensApp supports **optional JWT authentication**. By default, all endpoints are open. Visit [docs/JWT_AUTH.md](./docs/JWT_AUTH.md) for the authentication documentation.
SensApp uses JSON Web Tokens (JWT) for its authentication. Visit [docs/JWT_AUTH.md](./docs/JWT_AUTH.md) for the detailed authentication documentation.

While you can start sensapp with authentication disabled, it is recommended to use the authentication feature, in command line or through the web interface. By default, SensApp will print an admin token and a link that you can use to generate more tokens.

## Built With Rust™️

SensApp is developed using Rust, a language known for its performance, memory safety, and annoying borrow checker. SensApp used to be written in Scala, but the new author prefers Rust.
SensApp is developed using Rust, a language known for its performance, memory safety, and annoying borrow checker. SensApp used to be written in Scala, but the new authors prefers Rust.

Another reason is from the results from the paper [Energy efficiency across programming languages: how do energy, time, and memory relate?](https://dl.acm.org/doi/10.1145/3136014.3136031), which shows Rust as one of the most energy-efficient programming languages while having memory safety.

Expand All @@ -136,7 +140,7 @@ The SensApp software is provided "as is," with no warranties, and the creators o

## You May Not Want to Use It in Production (Yet)

SensApp is currently under development. It is not ready for production.
SensApp is currently under development. It is getting ready for production.

## Acknowledgements

Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This file tracks the main remaining work for SensApp. Detailed task history lives in `current_tasks/`, `ideas/` and `done/`.

The core is in place: HTTP-only ingestion, DCAT/query/export endpoints, Prometheus and InfluxDB compatibility, health endpoints, Prometheus service metrics, JWT sensor authorization, HTTP resource limits, data lifecycle controls, cross-series aggregation, a Python SDK, Helm chart and container images, and a broad integration test suite run against every backend in CI.
The core is in place: HTTP-only ingestion, DCAT/query/export endpoints, Prometheus and InfluxDB compatibility, health endpoints, Prometheus service metrics, secure-by-default JWT authentication (sensor authorization, an admin scope, secret rotation, a Credentials tab), HTTP resource limits, data lifecycle controls, cross-series aggregation, a Python SDK, Helm chart and container images, and a broad integration test suite run against every backend in CI.

The next phase is not to add more features. It is to make the existing system solid enough for pre-production, with ClickHouse as the reference backend. The acceptance criteria and sequencing are in [docs/PREPRODUCTION_RELEASE_PLAN.md](docs/PREPRODUCTION_RELEASE_PLAN.md).

Expand Down
17 changes: 17 additions & 0 deletions charts/sensapp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,20 @@ Supported URL forms in the default container image include:
| ClickHouse HTTPS | `clickhouses://user:password@clickhouse.example.com:8443/sensapp` |

For a quick setup, you can set `storage.connectionString` directly instead of referencing a Secret. Helm stores that value in the release configuration, so use an existing Secret for credentials. External databases own their storage and lifecycle; leave `persistence.enabled=false` for these backends.

## Authentication

SensApp does not run open: the container image listens on every address, so it refuses to start without a secret. By default the chart **makes a random secret on the first install** and keeps it across upgrades (it reads it back from the Secret of the release). Make an admin token, which makes the other tokens in the UI (Credentials tab):

```bash
kubectl exec deploy/sensapp -- sensapp generate-token me --scope read,admin
```

| Value | Effect |
| --- | --- |
| `auth.jwtSecret` | A secret of your choice (at least 32 characters). Stored in the Secret of the release and in the release values: prefer `existingSecret`. |
| `auth.existingSecret`, `auth.existingSecretKey` | A Secret of yours holding `SENSAPP_JWT_SECRET`. Takes precedence over `jwtSecret`. |
| `auth.previousSecrets` | Comma-separated secrets that still verify tokens but never sign: the way to rotate the secret, see [JWT_AUTH.md](../../docs/JWT_AUTH.md#rotating-the-secret-and-revoking-tokens). |
| `auth.disabled` | Every endpoint is open (`SENSAPP_AUTH_DISABLED`), for a network that authenticates in front of SensApp. Ignored when a secret is given. |

The made secret needs the chart to read the cluster (`lookup`). Tools that only render the chart (`helm template`, Argo CD) cannot, and would make a new secret at each render: for those, set `auth.jwtSecret` or `auth.existingSecret`.
23 changes: 16 additions & 7 deletions charts/sensapp/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,22 @@
echo http://127.0.0.1:3000
{{- end }}

{{- if .Values.auth.jwtSecret }}
2. JWT authentication is ENABLED.
Generate a token with:
sensapp generate-token <subject> --scope readwrite --duration 3600
{{- if and .Values.auth.disabled (not .Values.auth.jwtSecret) (not .Values.auth.existingSecret) }}
2. Authentication is DISABLED (auth.disabled): every endpoint is open to whoever can reach it.
To require tokens, remove auth.disabled.
{{- else }}
2. Authentication is ENABLED: the endpoints need a token.
{{- if .Values.auth.existingSecret }}
The secret is the key {{ .Values.auth.existingSecretKey }} of the Secret {{ .Values.auth.existingSecret }}.
{{- else if .Values.auth.jwtSecret }}
The secret is auth.jwtSecret.
{{- else }}
The chart made the secret, and keeps it across upgrades.
{{- end }}
Make an admin token (it makes the other tokens, in the UI under Credentials or with POST /api/v1/admin/tokens):
kubectl exec --namespace {{ .Release.Namespace }} deploy/{{ include "sensapp.fullname" . }} -- sensapp generate-token me --scope read,admin
Or a token for a client:
kubectl exec --namespace {{ .Release.Namespace }} deploy/{{ include "sensapp.fullname" . }} -- sensapp generate-token <subject> --scope readwrite --duration 86400
Use it in requests:
curl -H "Authorization: Bearer <token>" http://<sensapp-url>/metrics
{{- else }}
2. JWT authentication is DISABLED. All endpoints are open.
To enable, set auth.jwtSecret to a random string (≥ 32 chars).
{{- end }}
32 changes: 32 additions & 0 deletions charts/sensapp/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,35 @@ app.kubernetes.io/instance: {{ .Release.Name }}
{{- default "default" .Values.serviceAccount.name -}}
{{- end -}}
{{- end -}}

{{/*
"true" when the chart makes the JWT secret itself: authentication is on, and no secret was given
(neither `auth.jwtSecret` nor `auth.existingSecret`). Empty otherwise.
*/}}
{{- define "sensapp.generateJwtSecret" -}}
{{- if and (not .Values.auth.disabled) (not .Values.auth.jwtSecret) (not .Values.auth.existingSecret) -}}
true
{{- end -}}
{{- end -}}

{{/*
The JWT secret the chart makes: the one of the Secret of the release when it exists, so that an
upgrade keeps it (and the tokens made with it), a new random one on the first install. `helm
template` and the tools that only render (Argo CD) cannot look the Secret up: they would make
a new secret at each render, so give `auth.jwtSecret` or `auth.existingSecret` there.
*/}}
{{- define "sensapp.jwtSecret" -}}
{{- $existing := lookup "v1" "Secret" .Release.Namespace (include "sensapp.fullname" .) -}}
{{- if and $existing $existing.data (hasKey $existing.data "SENSAPP_JWT_SECRET") -}}
{{- index $existing.data "SENSAPP_JWT_SECRET" | b64dec -}}
{{- else -}}
{{- randAlphaNum 48 -}}
{{- end -}}
{{- end -}}

{{/* "true" when the Secret of the release holds something */}}
{{- define "sensapp.hasSecret" -}}
{{- if or (not .Values.storage.existingSecret) .Values.auth.jwtSecret .Values.auth.previousSecrets .Values.secretEnv (include "sensapp.generateJwtSecret" .) -}}
true
{{- end -}}
{{- end -}}
3 changes: 3 additions & 0 deletions charts/sensapp/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ metadata:
{{- include "sensapp.labels" . | nindent 4 }}
data:
SENSAPP_PORT: {{ .Values.service.port | quote }}
{{- if .Values.auth.disabled }}
SENSAPP_AUTH_DISABLED: "true"
{{- end }}
{{- range $key, $value := .Values.env }}
{{- if ne $key "SENSAPP_PORT" }}
{{ $key }}: {{ $value | quote }}
Expand Down
11 changes: 9 additions & 2 deletions charts/sensapp/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ spec:
envFrom:
- configMapRef:
name: {{ include "sensapp.fullname" . }}
{{- if or (not .Values.storage.existingSecret) .Values.auth.jwtSecret .Values.secretEnv }}
{{- if include "sensapp.hasSecret" . }}
- secretRef:
name: {{ include "sensapp.fullname" . }}
{{- end }}
{{- with .Values.extraEnvFrom }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if or .Values.storage.existingSecret .Values.extraEnv }}
{{- if or .Values.storage.existingSecret .Values.auth.existingSecret .Values.extraEnv }}
env:
{{- if .Values.storage.existingSecret }}
- name: SENSAPP_STORAGE_CONNECTION_STRING
Expand All @@ -59,6 +59,13 @@ spec:
name: {{ .Values.storage.existingSecret | quote }}
key: {{ .Values.storage.existingSecretKey | quote }}
{{- end }}
{{- if .Values.auth.existingSecret }}
- name: SENSAPP_JWT_SECRET
valueFrom:
secretKeyRef:
name: {{ .Values.auth.existingSecret | quote }}
key: {{ .Values.auth.existingSecretKey | quote }}
{{- end }}
{{- with .Values.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
Expand Down
7 changes: 6 additions & 1 deletion charts/sensapp/templates/secret.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{- if or (not .Values.storage.existingSecret) .Values.auth.jwtSecret .Values.secretEnv }}
{{- if include "sensapp.hasSecret" . }}
apiVersion: v1
kind: Secret
metadata:
Expand All @@ -12,6 +12,11 @@ stringData:
{{- end }}
{{- if .Values.auth.jwtSecret }}
SENSAPP_JWT_SECRET: {{ .Values.auth.jwtSecret | quote }}
{{- else if include "sensapp.generateJwtSecret" . }}
SENSAPP_JWT_SECRET: {{ include "sensapp.jwtSecret" . | quote }}
{{- end }}
{{- if .Values.auth.previousSecrets }}
SENSAPP_JWT_PREVIOUS_SECRETS: {{ .Values.auth.previousSecrets | quote }}
{{- end }}
{{- range $key, $value := .Values.secretEnv }}
{{ $key }}: {{ $value | quote }}
Expand Down
25 changes: 19 additions & 6 deletions charts/sensapp/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,27 @@ storage:
existingSecret: ""
existingSecretKey: SENSAPP_STORAGE_CONNECTION_STRING

# Optional JWT authentication.
# When jwtSecret is set, read/write endpoints require a valid Bearer token.
# Health checks, docs, and prometheus scrape remain open.
# Use `sensapp generate-token <subject>` to create tokens.
# Authentication. SensApp does not run open unless told to: the protected endpoints need a JWT
# signed with a secret. Health checks, docs, the UI files and the Prometheus scrape stay open.
#
# By default the chart makes a random secret on the first install and keeps it across upgrades
# (it is read back from the Secret of the release). Make an admin token with:
# kubectl exec deploy/<release name> -- sensapp generate-token me --scope read,admin
# Tools that only render the chart (Argo CD, `helm template`) cannot read it back and would make a
# new secret each time: for those, give jwtSecret or existingSecret.
auth:
# Set to a random string of at least 32 characters to enable JWT auth.
# Leave empty (or omit) to run without authentication (default).
# A secret of at least 32 characters (`sensapp generate-secret` makes one). Stored in the Secret
# of the release, and in the release values: prefer existingSecret.
jwtSecret: ""
# A Secret of yours holding the key below. Takes precedence over jwtSecret.
existingSecret: ""
existingSecretKey: SENSAPP_JWT_SECRET
# Secrets that still verify tokens but never sign them, separated by commas: the way to rotate
# the secret. Dropping one revokes the tokens it signed.
previousSecrets: ""
# Run with every endpoint open (SENSAPP_AUTH_DISABLED). For a network that authenticates
# in front of SensApp. Ignored when a secret is given.
disabled: false

secretEnv: {}
extraEnv: []
Expand Down
2 changes: 2 additions & 0 deletions compose.prometheus-roleplay.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ services:
container_name: sensapp-roleplay
environment:
SENSAPP_ENDPOINT: 0.0.0.0
# A demo on a laptop, with clients that send no token
SENSAPP_AUTH_DISABLED: "true"
SENSAPP_PORT: 3017
SENSAPP_STORAGE_CONNECTION_STRING: duckdb:///workspaces/sensapp/python/roleplay-datascience/roleplay-session.duckdb
RUST_LOG: info
Expand Down
Loading
Loading