Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
63 changes: 63 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Test

on:
pull_request:
branches: [main]
push:
branches: [main]

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: azure/setup-helm@v4
with:
version: v3.16.4

- name: Add chart repos
run: |
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm repo add cerbos https://download.cerbos.dev/helm-charts
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update

- name: Build dependencies
run: |
helm dependency build .
# helm 3 leaves sub-charts as .tgz; some helm versions / chart
# configurations error with "missing in charts/ directory" until the
# archives are extracted. Unpack so `helm lint` and `helm template`
# see the dirs.
for tgz in charts/*.tgz; do
tar -xzf "$tgz" -C charts/
done
ls -la charts/

- name: Lint
run: helm lint .

- name: Template (dev profile)
run: helm template plinth . --values values/dev.values.yaml > /tmp/dev.yaml

- name: Template (defaults)
run: helm template plinth . > /tmp/defaults.yaml

- name: Smoke — assert key resources are rendered
run: |
set -euo pipefail
for needle in \
"kind: Cluster" \
"name: plinth-cerbos-policies" \
"kind: Deployment" \
"kind: CustomResourceDefinition" ; do
if ! grep -q "$needle" /tmp/dev.yaml; then
echo "missing: $needle" >&2
exit 1
fi
done
echo "all expected resources present"
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Helm
# Helm — sub-chart deps are downloaded by `helm dependency build`, not committed
*.tgz
charts/*.tgz
charts/*/charts/
charts/*/
Chart.lock

# Local values overrides
values.local.yaml
Expand Down
15 changes: 15 additions & 0 deletions .helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.DS_Store
.git/
.gitignore
.idea/
.vscode/
.github/
*.swp
*.swo
*.bak
*.tmp
*.tgz
charts/*.tgz
README.md.tmpl
values/staging.values.yaml
values/prod.values.yaml
50 changes: 50 additions & 0 deletions Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
apiVersion: v2
name: plinth
description: >-
The Plinth substrate — an opinionated Kubernetes baseline that the
Plinth starters target. v0.1.0 ships a walking skeleton: CloudNativePG
for Postgres, Cerbos for authorisation, and the OpenTelemetry Collector.
The full reference architecture (Vault, Authentik, SigNoz, Wazuh,
Falco, Trivy, Argo CD, Backstage, etc.) lands incrementally — see the
roadmap in the README.

type: application
version: 0.1.0
appVersion: "0.1.0"

home: https://plinth.run
sources:
- https://github.com/plinth-dev/platform
maintainers:
- name: plinth-dev
url: https://github.com/plinth-dev

icon: https://plinth.run/manuscript-favicon.png

keywords:
- plinth
- internal-tools
- postgres
- authorisation
- opentelemetry
- bank-grade

# Sub-chart dependencies are pulled from upstream chart repos via
# `helm dependency update`. Pinned to specific minor versions so a
# release is reproducible — bump the pin deliberately, never floatingly.
dependencies:
- name: cloudnative-pg
version: 0.28.0
repository: https://cloudnative-pg.github.io/charts

- name: cerbos
version: 0.52.1
repository: https://download.cerbos.dev/helm-charts

- name: opentelemetry-collector
version: 0.153.0
repository: https://open-telemetry.github.io/opentelemetry-helm-charts

annotations:
category: Platform
licenses: MIT
113 changes: 84 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,105 @@
# Plinth — Substrate

> **Status: not yet released — Phase D begins after Phase B/C.**
> No chart is published to `oci://ghcr.io/plinth-dev/platform` yet; the install command below is the **target** flow. Track progress on the [roadmap](https://github.com/plinth-dev/.github/blob/main/ROADMAP.md). Architecture reference (already complete): [plinth.run/architecture](https://plinth.run/architecture/).
The Helm umbrella chart that bootstraps the Plinth substrate on a Kubernetes cluster. v0.1.0 is a walking skeleton: CloudNativePG for Postgres, Cerbos for authorisation, and the OpenTelemetry Collector. The full reference architecture lands incrementally — see [Roadmap](#roadmap) below.

The Helm umbrella chart and Talos cluster bootstrap that will bring up the entire Plinth reference architecture on a fresh Kubernetes cluster.
## Install (dev profile)

```bash
# Target — Phase D
helm install plinth oci://ghcr.io/plinth-dev/platform --values dev.values.yaml
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm repo add cerbos https://download.cerbos.dev/helm-charts
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts

git clone https://github.com/plinth-dev/platform && cd platform
helm dependency build .
helm install plinth . --namespace plinth --create-namespace --values values/dev.values.yaml
```

## What it will contain (target)
Once the install settles, you'll have:

One umbrella chart, sub-charts per concern. Upstream Helm releases are used where they exist; vendored only when version pinning matters.
- A 1-instance CloudNativePG `Cluster` named `plinth-postgres` exposing Service `plinth-postgres-rw:5432`
- A 1-replica Cerbos PDP at `plinth-cerbos:3592` (HTTP) and `:3593` (gRPC), with a placeholder `items` policy
- A 1-replica OpenTelemetry Collector at `plinth-opentelemetry-collector:4317` (gRPC) and `:4318` (HTTP), exporting traces/metrics/logs to stdout

| Concern | Components |
| --- | --- |
| Identity | Vault (HA Raft), Authentik, Ory Oathkeeper, cert-manager |
| Authorization | Cerbos PDP |
| Data | CloudNativePG, MinIO, NATS JetStream, Redis Sentinel, OpenSearch |
| Observability | SigNoz (ClickHouse-backed), OpenTelemetry Collector |
| Security | Wazuh, Falco, Trivy Operator, Kyverno |
| GitOps + DevX | Argo CD, Argo Rollouts, Backstage |
Pointing the [`starter-api`](https://github.com/plinth-dev/starter-api) at this substrate is one env block:

Optional sub-charts (default off): Temporal, kube-prometheus-stack (LGTM-style), GitLab CE.
```bash
DATABASE_URL=postgresql://plinth@plinth-postgres-rw:5432/plinth
CERBOS_ADDR=plinth-cerbos:3593
OTEL_EXPORTER_OTLP_ENDPOINT=http://plinth-opentelemetry-collector:4318
```

## What v0.1.0 ships

| Concern | Component | Sub-chart | Version |
|---|---|---|---|
| Data | CloudNativePG operator + `Cluster` CR | `cnpg/cloudnative-pg` | 0.28.0 |
| Authorisation | Cerbos PDP | `cerbos/cerbos` | 0.52.1 |
| Observability | OpenTelemetry Collector | `open-telemetry/opentelemetry-collector` | 0.153.0 |

The umbrella adds two Plinth-specific resources: a `Cluster` CR (bootstrapped database + role) and a `plinth-cerbos-policies` ConfigMap that's mounted into the Cerbos pod at `/policies`.

## Profiles

| Profile | Shape |
| --- | --- |
| `dev` | Single node, no HA, all defaults turned on |
| `staging` | 3 nodes, no DR, full feature set |
| `prod` | Full HA, DR site replication, hardened defaults |
| Profile | Status | Shape |
|---|---|---|
| `dev` | shipped | Single node, no HA, all defaults turned on |
| `staging` | stub — see `values/staging.values.yaml` | 3 nodes, no DR, full feature set |
| `prod` | stub — see `values/prod.values.yaml` | Full HA, DR site replication, hardened defaults |

## Layout
`values/staging.values.yaml` and `values/prod.values.yaml` carry comments describing the intended shape; the actual values land in subsequent chart versions.

## Customise

Plinth-specific knobs live under the top-level `plinth` key in `values.yaml`:

```yaml
plinth:
name: plinth
postgres:
enabled: true
instances: 1
storage:
size: 5Gi
database: plinth
owner: plinth
cerbosPoliciesEnabled: true
cerbosPolicies:
items.yaml: |
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: items
rules: # ...
```
.
├── Chart.yaml # Umbrella chart with sub-chart deps
├── values.yaml # Defaults
├── values/ # Profile values: dev / staging / prod
├── charts/ # Vendored sub-charts where pinning matters
├── bootstrap/ # Argo app-of-apps + initial Cerbos / Wazuh seed
└── cluster-bootstrap/ # talosctl manifests, Omni link, walkthrough

Sub-chart values (e.g. `cerbos.replicaCount`, `opentelemetry-collector.config.exporters`) are forwarded directly to upstream charts — see each upstream chart's README for the full schema.

## Roadmap

Tracked in the umbrella's GitHub Issues. Planned for subsequent versions:

| Concern | Components |
|---|---|
| Identity | Vault (HA Raft), Authentik, Ory Oathkeeper, cert-manager |
| Data | MinIO, NATS JetStream, Redis Sentinel, OpenSearch |
| Observability | SigNoz (ClickHouse-backed), kube-prometheus-stack |
| Security | Wazuh, Falco, Trivy Operator, Kyverno |
| GitOps + DevX | Argo CD, Argo Rollouts, Backstage |
| Bootstrap | Talos manifests + Omni link, Argo app-of-apps |
| Release | OCI publish to `oci://ghcr.io/plinth-dev/platform` |

Optional sub-charts (default off): Temporal, GitLab CE.

## Develop

```bash
helm dependency build .
helm lint .
helm template plinth . --values values/dev.values.yaml | less
```

CI runs the same three steps plus a smoke check that asserts the rendered output contains the expected `kind: Cluster`, `kind: CustomResourceDefinition`, and Plinth-specific ConfigMap.

## Related

- [`plinth.run`](https://plinth.run) — full architecture reference, tutorials, ADRs.
Expand Down
30 changes: 30 additions & 0 deletions templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Plinth substrate v{{ .Chart.Version }} installed as release "{{ .Release.Name }}" in namespace "{{ .Release.Namespace }}".

What just came up:

{{- if .Values.plinth.postgres.enabled }}
* CloudNativePG operator + a {{ .Values.plinth.postgres.instances }}-instance cluster
named "{{ include "plinth.fullname" . }}-postgres". The default database is
"{{ .Values.plinth.postgres.database }}" owned by role "{{ .Values.plinth.postgres.owner }}".
Connection details land in two secrets:
kubectl get secret {{ include "plinth.fullname" . }}-postgres-app -n {{ .Release.Namespace }}
kubectl get secret {{ include "plinth.fullname" . }}-postgres-superuser -n {{ .Release.Namespace }}
{{- end }}

* Cerbos PDP listening on :3592 (HTTP) and :3593 (gRPC). Policies are mounted
from the "plinth-cerbos-policies" ConfigMap; tweak via `plinth.cerbosPolicies`
in values.

* OpenTelemetry Collector accepting OTLP/HTTP on :4318 and OTLP/gRPC on :4317.
Dev profile exports to logs only — wire the exporter to your tracing
backend via the `opentelemetry-collector.config.exporters` block and the
matching pipeline `exporters: [...]` lists.

Wire the starter-api environment to these services:
DATABASE_URL=postgresql://{{ .Values.plinth.postgres.owner }}@{{ include "plinth.fullname" . }}-postgres-rw:5432/{{ .Values.plinth.postgres.database }}
CERBOS_ADDR={{ include "plinth.fullname" . }}-cerbos:3593
OTEL_EXPORTER_OTLP_ENDPOINT=http://{{ include "plinth.fullname" . }}-opentelemetry-collector:4318

Roadmap: Vault, Authentik, SigNoz, Wazuh, Falco, Trivy, Argo CD, Backstage,
and the Talos cluster bootstrap land in subsequent versions —
https://github.com/plinth-dev/platform.
34 changes: 34 additions & 0 deletions templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "plinth.name" -}}
{{- default "plinth" .Values.plinth.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Fully-qualified resource name: <release>-<chart>.
Used so multiple installs of the umbrella into the same namespace don't
collide. Truncated to 63 chars (Kubernetes label limit).
*/}}
{{- define "plinth.fullname" -}}
{{- $name := default .Chart.Name .Values.plinth.name -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}

{{/*
Standard label set applied to every resource we own.
*/}}
{{- define "plinth.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
app.kubernetes.io/name: {{ include "plinth.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/part-of: plinth
{{- end -}}
45 changes: 45 additions & 0 deletions templates/cerbos-policies-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{{- if .Values.plinth.cerbosPoliciesEnabled -}}
# ConfigMap of Cerbos policies mounted into the Cerbos pod at /policies.
# `plinth.cerbosPolicies` is a map of `<filename>: <yaml-string>`; entries
# end up as files in the mount, which the Cerbos config below points at as
# its policy directory.
#
# The default carries an example "items" resource policy matching what the
# starter-api scaffold ships. Override entirely from your install values
# once you've defined your own resources.
apiVersion: v1
kind: ConfigMap
metadata:
name: plinth-cerbos-policies
labels:
{{- include "plinth.labels" . | nindent 4 }}
data:
{{- if .Values.plinth.cerbosPolicies }}
{{- range $name, $body := .Values.plinth.cerbosPolicies }}
{{ $name }}: |
{{ $body | indent 4 }}
{{- end }}
{{- else }}
items.yaml: |
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: items
rules:
- actions: ["read", "list"]
effect: EFFECT_ALLOW
roles:
- viewer
- editor
- admin
- actions: ["create", "update", "delete"]
effect: EFFECT_ALLOW
roles:
- editor
- admin
- actions: ["delete"]
effect: EFFECT_ALLOW
roles:
- admin
{{- end }}
{{- end }}
Loading
Loading