docs(helm): annotate values.yaml, add chart README, ArgoCD example, update root README and AGENTS.md
This commit is contained in:
@@ -38,6 +38,7 @@ Frontend UI for self-hosted Honcho instances — browse memories, peers, session
|
||||
| `packages/web/src/test/` | Vitest unit/integration tests + setup |
|
||||
| `packages/web/e2e/` | Playwright e2e specs |
|
||||
| `packages/desktop/` | Tauri shell that bundles the built web app |
|
||||
| `charts/openconcho/` | Helm 3 chart for self-hosting on Kubernetes (OCI artifact on GHCR) |
|
||||
| `.claude/rules/` | Coding conventions (auto-loaded; stack-agnostic, applies to all agents) |
|
||||
| `docs/` | Architecture and references |
|
||||
|
||||
|
||||
28
README.md
28
README.md
@@ -114,6 +114,34 @@ profiles: `make up` runs the `dev` profile (`build: .`), `make prod` runs the
|
||||
(comma-separated host globs) for when you expose the proxy. Full details and env
|
||||
vars are in [`docs/docker.md`](docs/docker.md).
|
||||
|
||||
### Kubernetes (Helm)
|
||||
|
||||
The chart is published as an OCI artifact to GHCR on every tagged release.
|
||||
|
||||
```bash
|
||||
helm install openconcho oci://ghcr.io/offendingcommit/charts/openconcho \
|
||||
--version 0.14.0 \
|
||||
--create-namespace --namespace openconcho \
|
||||
--set honcho.defaultUrl=https://honcho.example.com
|
||||
```
|
||||
|
||||
Enable an Ingress and TLS:
|
||||
|
||||
```bash
|
||||
helm install openconcho oci://ghcr.io/offendingcommit/charts/openconcho \
|
||||
--version 0.14.0 \
|
||||
--create-namespace --namespace openconcho \
|
||||
--set honcho.defaultUrl=https://honcho.example.com \
|
||||
--set ingress.enabled=true \
|
||||
--set ingress.className=nginx \
|
||||
--set 'ingress.hosts[0].host=openconcho.example.com' \
|
||||
--set 'ingress.hosts[0].paths[0].path=/' \
|
||||
--set 'ingress.tls[0].secretName=openconcho-tls' \
|
||||
--set 'ingress.tls[0].hosts[0]=openconcho.example.com'
|
||||
```
|
||||
|
||||
Full chart documentation, configuration reference, and an ArgoCD Application example are in [`charts/openconcho/README.md`](charts/openconcho/README.md).
|
||||
|
||||
### Connecting to your instance
|
||||
|
||||
1. Enter the base URL of your Honcho instance (e.g. `http://localhost:8000`)
|
||||
|
||||
229
charts/openconcho/README.md
Normal file
229
charts/openconcho/README.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# openconcho Helm Chart
|
||||
|
||||
Helm 3 chart for self-hosting the [openconcho](https://github.com/offendingcommit/openconcho) web UI on Kubernetes.
|
||||
|
||||
The chart deploys a single nginx-unprivileged container (port 8080, UID 101) that serves the React SPA and reverse-proxies Honcho API calls under `/api` to avoid browser CORS issues.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.25+
|
||||
- Helm 3.10+
|
||||
- A running [Honcho](https://github.com/plastic-labs/honcho) instance reachable from within the cluster (or via a configured ingress)
|
||||
|
||||
## Installing
|
||||
|
||||
Add the chart repository:
|
||||
|
||||
```bash
|
||||
helm registry login ghcr.io --username <github-username> --password <github-token>
|
||||
```
|
||||
|
||||
Install the chart:
|
||||
|
||||
```bash
|
||||
helm install openconcho oci://ghcr.io/offendingcommit/charts/openconcho \
|
||||
--version 0.14.0 \
|
||||
--set honcho.defaultUrl=https://honcho.example.com
|
||||
```
|
||||
|
||||
Or with a values file (recommended):
|
||||
|
||||
```bash
|
||||
helm install openconcho oci://ghcr.io/offendingcommit/charts/openconcho \
|
||||
--version 0.14.0 \
|
||||
-f my-values.yaml
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
```bash
|
||||
helm upgrade openconcho oci://ghcr.io/offendingcommit/charts/openconcho \
|
||||
--version <new-version> \
|
||||
-f my-values.yaml
|
||||
```
|
||||
|
||||
## Uninstalling
|
||||
|
||||
```bash
|
||||
helm uninstall openconcho
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
All values with their defaults are documented in [`values.yaml`](values.yaml). Key options:
|
||||
|
||||
| Value | Default | Description |
|
||||
|---|---|---|
|
||||
| `replicaCount` | `1` | Number of pod replicas |
|
||||
| `image.repository` | `ghcr.io/offendingcommit/openconcho-web` | Container image |
|
||||
| `image.tag` | `""` | Tag; defaults to chart `appVersion` |
|
||||
| `image.pullPolicy` | `IfNotPresent` | Image pull policy |
|
||||
| `honcho.defaultUrl` | `""` | Honcho URL pre-seeded in the UI |
|
||||
| `honcho.upstreamAllowlist` | `""` | SSRF guard (comma-separated host globs) |
|
||||
| `service.type` | `ClusterIP` | `ClusterIP` / `NodePort` / `LoadBalancer` |
|
||||
| `service.port` | `80` | Service port |
|
||||
| `ingress.enabled` | `false` | Enable Ingress resource |
|
||||
| `ingress.className` | `""` | IngressClass name |
|
||||
| `autoscaling.enabled` | `false` | Enable HorizontalPodAutoscaler |
|
||||
| `podDisruptionBudget.enabled` | `false` | Enable PodDisruptionBudget |
|
||||
| `networkPolicy.enabled` | `false` | Enable NetworkPolicy (same-namespace only) |
|
||||
| `resources.requests.memory` | `32Mi` | Memory request |
|
||||
| `resources.limits.memory` | `128Mi` | Memory limit |
|
||||
|
||||
## Examples
|
||||
|
||||
### Minimal (ClusterIP, no ingress)
|
||||
|
||||
```yaml
|
||||
honcho:
|
||||
defaultUrl: http://honcho.honcho.svc.cluster.local:8000
|
||||
```
|
||||
|
||||
### With Ingress and TLS (cert-manager)
|
||||
|
||||
```yaml
|
||||
honcho:
|
||||
defaultUrl: https://honcho.example.com
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
hosts:
|
||||
- host: openconcho.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: openconcho-tls
|
||||
hosts:
|
||||
- openconcho.example.com
|
||||
```
|
||||
|
||||
### With autoscaling and disruption budget
|
||||
|
||||
```yaml
|
||||
replicaCount: 2
|
||||
|
||||
autoscaling:
|
||||
enabled: true
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 70
|
||||
|
||||
podDisruptionBudget:
|
||||
enabled: true
|
||||
minAvailable: 1
|
||||
```
|
||||
|
||||
### With NetworkPolicy
|
||||
|
||||
> **Note:** When `networkPolicy.enabled=true` and `ingress.enabled=true`, you must add
|
||||
> a policy that allows traffic from the ingress-controller namespace. Run
|
||||
> `helm status <release>` for the exact `kubectl edit` command after install.
|
||||
|
||||
```yaml
|
||||
networkPolicy:
|
||||
enabled: true
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
hosts:
|
||||
- host: openconcho.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
### Private registry
|
||||
|
||||
```yaml
|
||||
image:
|
||||
repository: registry.example.com/myorg/openconcho-web
|
||||
tag: "0.14.0"
|
||||
pullPolicy: Always
|
||||
|
||||
imagePullSecrets:
|
||||
- name: registry-credentials
|
||||
```
|
||||
|
||||
## ArgoCD Application
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: openconcho
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: ghcr.io/offendingcommit/charts
|
||||
chart: openconcho
|
||||
targetRevision: 0.14.0
|
||||
helm:
|
||||
valuesObject:
|
||||
honcho:
|
||||
defaultUrl: https://honcho.example.com
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
hosts:
|
||||
- host: openconcho.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: openconcho-tls
|
||||
hosts:
|
||||
- openconcho.example.com
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: openconcho
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
```
|
||||
|
||||
> OCI chart sources require ArgoCD 2.10+ (OCI Helm support GA).
|
||||
|
||||
## Helm tests
|
||||
|
||||
After install, run the bundled tests to verify the deployment is healthy:
|
||||
|
||||
```bash
|
||||
helm test openconcho
|
||||
```
|
||||
|
||||
Two test pods run and exit 0 on success:
|
||||
|
||||
| Test | What it checks |
|
||||
|---|---|
|
||||
| `test-healthz` | `GET /healthz` body equals `ok` |
|
||||
| `test-spa-root` | `GET /` returns HTTP 200 |
|
||||
|
||||
Pass `--logs` to see output from failing pods:
|
||||
|
||||
```bash
|
||||
helm test openconcho --logs
|
||||
```
|
||||
|
||||
## Security posture
|
||||
|
||||
| Control | Value |
|
||||
|---|---|
|
||||
| Run as UID/GID | 101 (nginx-unprivileged) |
|
||||
| `runAsNonRoot` | `true` |
|
||||
| `readOnlyRootFilesystem` | `true` |
|
||||
| Linux capabilities | all dropped |
|
||||
| `seccompProfile` | `RuntimeDefault` |
|
||||
| `allowPrivilegeEscalation` | `false` |
|
||||
| `automountServiceAccountToken` | `false` |
|
||||
| Writable paths | `/var/cache/nginx`, `/var/run`, `/tmp` (tmpfs) |
|
||||
@@ -1,23 +1,40 @@
|
||||
# Number of pod replicas. Increase for high availability or use autoscaling instead.
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
# Container image repository. Override to use a custom registry or fork.
|
||||
repository: ghcr.io/offendingcommit/openconcho-web
|
||||
# Image tag. Defaults to the chart appVersion when left empty.
|
||||
tag: ""
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
# Secrets for pulling images from private registries.
|
||||
# Example: [{ name: my-registry-secret }]
|
||||
imagePullSecrets: []
|
||||
|
||||
# Override the name portion used in resource names and labels.
|
||||
nameOverride: ""
|
||||
# Override the full resource name (normally release-name + chart-name).
|
||||
fullnameOverride: ""
|
||||
|
||||
serviceAccount:
|
||||
# Create a dedicated ServiceAccount for the pod.
|
||||
create: true
|
||||
# Disable automatic ServiceAccount token mounting — the app never calls the Kubernetes API.
|
||||
automount: false
|
||||
# Annotations to add to the ServiceAccount (e.g. for IRSA, Workload Identity, Vault).
|
||||
annotations: {}
|
||||
# Use a pre-existing ServiceAccount instead of creating one. Ignored when create is true.
|
||||
name: ""
|
||||
|
||||
# Annotations applied to every pod (not the Deployment). Useful for Prometheus scraping,
|
||||
# Vault agent injection, Datadog unified service tagging, etc.
|
||||
podAnnotations: {}
|
||||
# Extra labels applied to every pod.
|
||||
podLabels: {}
|
||||
|
||||
# Pod-level security context shared by all containers.
|
||||
# UID/GID 101 matches the nginx-unprivileged base image — do not change without rebuilding.
|
||||
podSecurityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 101
|
||||
@@ -26,37 +43,62 @@ podSecurityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
# Container-level security context.
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
|
||||
# Directories mounted as ephemeral tmpfs (in-memory) to satisfy nginx's write requirements
|
||||
# when the root filesystem is read-only. Add entries for any additional writable paths.
|
||||
tmpfsMounts:
|
||||
- mountPath: /var/cache/nginx
|
||||
- mountPath: /var/run
|
||||
- mountPath: /tmp
|
||||
|
||||
service:
|
||||
# Kubernetes Service type. Options: ClusterIP | NodePort | LoadBalancer
|
||||
type: ClusterIP
|
||||
# Port exposed by the Service (what the Ingress or other pods target).
|
||||
port: 80
|
||||
# Port the container actually listens on (nginx-unprivileged default).
|
||||
containerPort: 8080
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
# IngressClass name. Leave empty to accept the cluster default.
|
||||
# Examples: nginx | traefik | alb | kong
|
||||
className: ""
|
||||
# Annotations forwarded verbatim to the Ingress resource.
|
||||
# Example (cert-manager + nginx-ingress):
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
annotations: {}
|
||||
hosts:
|
||||
- host: openconcho.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
# TLS configuration. Provide a Secret containing the certificate.
|
||||
# Example:
|
||||
# - secretName: openconcho-tls
|
||||
# hosts:
|
||||
# - openconcho.example.com
|
||||
tls: []
|
||||
|
||||
honcho:
|
||||
# Default Honcho instance URL pre-populated in the UI on first load.
|
||||
# Users can change or add instances at runtime; this only seeds the initial value.
|
||||
# Example: https://honcho.example.com
|
||||
defaultUrl: ""
|
||||
# Optional SSRF guard: comma-separated host globs the nginx proxy is allowed to forward to.
|
||||
# Leave empty to allow any upstream. Applies only when the proxy is publicly reachable.
|
||||
# Example: honcho.example.com,*.internal.example.com
|
||||
upstreamAllowlist: ""
|
||||
|
||||
# CPU and memory requests / limits for the web container.
|
||||
# The SPA is static HTML/JS, so memory is the primary concern and CPU is negligible at rest.
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
@@ -82,16 +124,55 @@ autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 5
|
||||
# Scale up when average CPU utilization across pods exceeds this percentage.
|
||||
targetCPUUtilizationPercentage: 80
|
||||
|
||||
podDisruptionBudget:
|
||||
enabled: false
|
||||
# Minimum number of pods that must remain available during voluntary disruptions
|
||||
# (node drains, rolling upgrades). Set maxUnavailable instead to flip the direction.
|
||||
minAvailable: 1
|
||||
# Uncomment to use maxUnavailable instead — cannot set both simultaneously.
|
||||
# maxUnavailable: 1
|
||||
|
||||
# Restrict pod-to-pod traffic at the network layer. When enabled, only pods in the
|
||||
# same namespace may reach the web container.
|
||||
# WARNING: if ingress is also enabled, you must add a policy that allows traffic from
|
||||
# the ingress-controller namespace — run `helm status <release>` to see the reminder.
|
||||
networkPolicy:
|
||||
enabled: false
|
||||
|
||||
# Spread pods across failure domains (availability zones, nodes, etc.) to reduce
|
||||
# the blast radius of a single-node or single-zone failure.
|
||||
# Example (zone spread):
|
||||
# - maxSkew: 1
|
||||
# topologyKey: topology.kubernetes.io/zone
|
||||
# whenUnsatisfiable: DoNotSchedule
|
||||
# labelSelector:
|
||||
# matchLabels:
|
||||
# app.kubernetes.io/name: openconcho
|
||||
topologySpreadConstraints: []
|
||||
|
||||
# Constrain pods to nodes whose labels match these key/value pairs.
|
||||
# Example: kubernetes.io/arch: amd64
|
||||
nodeSelector: {}
|
||||
|
||||
# Allow pods to be scheduled on tainted nodes.
|
||||
# Example:
|
||||
# - key: dedicated
|
||||
# operator: Equal
|
||||
# value: web
|
||||
# effect: NoSchedule
|
||||
tolerations: []
|
||||
|
||||
# Advanced pod affinity / anti-affinity rules.
|
||||
# Example (soft anti-affinity — prefer spreading across different nodes):
|
||||
# podAntiAffinity:
|
||||
# preferredDuringSchedulingIgnoredDuringExecution:
|
||||
# - weight: 100
|
||||
# podAffinityTerm:
|
||||
# labelSelector:
|
||||
# matchLabels:
|
||||
# app.kubernetes.io/name: openconcho
|
||||
# topologyKey: kubernetes.io/hostname
|
||||
affinity: {}
|
||||
|
||||
Reference in New Issue
Block a user