Skip to content

feat(charts): add openab-line Helm chart#1105

Open
chenhan-agent wants to merge 5 commits into
openabdev:mainfrom
chenhan-agent:feat/openab-line-chart
Open

feat(charts): add openab-line Helm chart#1105
chenhan-agent wants to merge 5 commits into
openabdev:mainfrom
chenhan-agent:feat/openab-line-chart

Conversation

@chenhan-agent

Copy link
Copy Markdown

What problem does this solve?

There is no official Helm chart for deploying OpenAB as a LINE Messaging API bot. Users who want to self-host a LINE bot have to manually compose Kubernetes manifests, wire up a gateway sidecar, and configure Cloudflare Tunnel — a significant barrier to adoption.

This PR adds charts/openab-line, a batteries-included Helm chart that deploys the OpenAB agent + gateway as a single unit with one helm install.

Discord Discussion URL: https://discord.com/channels/1491295327620169908/1511661981508305027/1512121004720394261

At a Glance

LINE Messaging API
       │  HTTPS webhook
       ▼
┌─────────────────────────────────────────────────┐
│  Kubernetes Pod (openab-line)                   │
│                                                 │
│  ┌──────────────┐    WebSocket    ┌───────────┐ │
│  │  openab      │◄───────────────►│  gateway  │ │
│  │  (agent)     │  shared /home/agent   │ (sidecar) │ │
│  └──────────────┘  /.openab/media └───────────┘ │
│                                        ▲         │
│  ┌────────────┐                        │         │
│  │ cloudflared│────────────────────────┘         │
│  │ (sidecar)  │  forwards :443 → gateway:8080    │
│  └────────────┘                                  │
└─────────────────────────────────────────────────┘

Why sidecar (not separate Deployment)?
Gateway stores media files at $HOME/.openab/media/inbound/ and passes the file path over the internal WebSocket. The agent reads the file and base64-encodes it before sending to the LLM. This requires gateway and agent to share the same filesystem — i.e. the same Pod. A separate-Deployment approach would break image delivery.

Prior Art & Industry Research

OpenClaw:
OpenClaw uses a similar gateway-sidecar pattern for platform adapters. Platform-specific adapters run in the same process/container as the core agent, sharing memory for media payloads. See: https://github.com/openclaw/openclaw

Hermes Agent:
Hermes Agent handles multi-platform messaging but delivers all media as base64 inline in the message event — no local file storage. This avoids the filesystem-sharing constraint but increases WebSocket payload size significantly for images. See: https://github.com/NousResearch/hermes-agent

Key design difference from both:
OpenAB's gateway deliberately uses local file storage + path passing to keep the WebSocket protocol lean. This makes the sidecar colocation requirement explicit (as documented in gateway/src/store.rs).

Proposed Solution

A new Helm chart charts/openab-line that:

  • Deploys a single Pod with three containers: openab (agent), gateway (LINE adapter), cloudflared (Tunnel, optional)
  • data volume (PVC or emptyDir) is always mounted to both openab and gateway containers, ensuring a writable $HOME even with readOnlyRootFilesystem: true
  • agent.env iterates with keys | sortAlpha for deterministic ConfigMap output (GitOps-safe)
  • kindIs "invalid" for null-checks on platform.allowAllUsers / platform.allowAllChannels (avoids nil-pointer panic from eq ... nil)
  • gateway.tag exposed in values.yaml with a warning comment — gateway versions independently from the agent
  • Cloudflare Tunnel token accepts either inline value or existingSecret

Why this approach?

Sidecar vs separate Deployment: The shared-filesystem requirement for media delivery makes sidecar the only viable option without introducing a ReadWriteMany PVC (which adds significant operational complexity). Verified experimentally — switching to separate Deployment causes gateway → line (reply API) to succeed but the agent replies "no content received" because tokio::fs::read(path) fails on a path that only exists on the gateway pod.

Cloudflare Tunnel as the exposure default: LINE requires a public HTTPS webhook with a valid TLS certificate. Cloudflare Tunnel satisfies this without requiring an Ingress controller or public LoadBalancer IP, matching the pattern used in charts/openab-telegram.

Known limitations:

  • add_reaction / remove_reaction commands are not supported by LINE Messaging API and are silently ignored by the gateway (logged at INFO).
  • Single-bot-per-chart design — one LINE channel per Helm release.

Alternatives Considered

  1. Extend charts/openab (base chart) — the base chart supports gateway.deploy: true with a separate Deployment. This works for text but breaks image delivery due to filesystem isolation. Rejected.

  2. Inline base64 in gateway WebSocket message — would remove the filesystem dependency. Rejected because it significantly increases WebSocket payload size for images and would require changes to the core gateway protocol, which is out of scope for a chart PR.

  3. ReadWriteMany PVC shared between gateway and agent Deployments — technically feasible but requires NFS/CephFS/EFS storage class, which is not available in most default Kubernetes clusters. Rejected.

Validation

  • helm lint charts/openab-line passes (0 chart(s) failed)
  • helm template renders correctly (verified deterministic output with sortAlpha)
  • Manual E2E — deployed to live Kubernetes cluster:
    • Text message: LINE → gateway → agent → reply ✅
    • Image message: LINE → gateway (download + store at /home/node/.openab/media/inbound/) → agent (read + base64 → LLM) → reply describing image content ✅
    • Cloudflare Tunnel: webhook reachable from LINE platform ✅
    • readOnlyRootFilesystem: true: containers start without crash ✅

chenhan1218 and others added 3 commits June 14, 2026 09:33
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- deployment: data volume unconditional (PVC or emptyDir) so readOnlyRootFilesystem
  containers always have a writable HOME; iterate agent.env with keys|sortAlpha
  for deterministic manifest output
- configmap: use kindIs "invalid" for null checks on allowAllUsers/allowAllChannels
- values: expose gateway.tag with compatibility warning; clarify
  cloudflare-tunnel-token is required (not optional) when tunnel.enabled=true;
  add allowAllUsers/allowAllChannels null defaults with resolve_allow_all semantics
- NOTES.txt: Deployment (not Pod), expose options cover Ingress/LB/NodePort
- README: expand values table to 36 rows covering all user-facing keys
- F1: Remove --token flag from cloudflared args; use TUNNEL_TOKEN env var
  only (avoids token exposure in procfs/kubectl describe)
- F2: Add liveness/readiness probes to gateway container (/healthz)
- F3: Add | default false guard for reactions fields in configmap
- F4: Add per-container resources blocks for gateway and cloudflared
- F5: Harden tunnelEnabled helper with explicit nil/type checks
@chaodu-agent

chaodu-agent commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

OUTDATED — see latest comment below.

Gateway v0.5.3 exposes GET /health, not /healthz. Aligns with
charts/openab gateway deployment template.
@chaodu-agent

Copy link
Copy Markdown
Collaborator

LGTM ✅ — All findings addressed. Chart is production-ready.

What This PR Does

Adds charts/openab-line, a batteries-included Helm chart deploying the OpenAB agent + gateway as a single pod with optional Cloudflare Tunnel sidecar, purpose-built for LINE Messaging API webhook integration.

How It Works

Single-pod architecture: agent ↔ gateway communicate via ws://localhost:8080/ws over shared filesystem ($HOME/.openab/media/inbound/). Optional cloudflared sidecar provides public HTTPS exposure without Ingress. Security-hardened by default (readOnlyRootFilesystem, drop ALL caps, runAsNonRoot, seccomp RuntimeDefault).

Findings

# Severity Finding Status
1 🟡 Tunnel token exposed in container args ✅ Fixed in 98a62c1
2 🟡 Gateway missing liveness/readiness probes ✅ Fixed in 98a62c1 + d9de33a
3 🟡 remove_after_reply missing default guard ✅ Fixed in 98a62c1
4 🟡 Gateway/cloudflared missing per-container resources ✅ Fixed in 98a62c1
5 🟡 tunnelEnabled helper fragile ✅ Fixed in 98a62c1
6 🔴 Probe path regression (/healthz → should be /health) ✅ Fixed in d9de33a

All findings resolved. CI green (helm-unittest + check pass on d9de33a).

Baseline Check
  • PR opened: 2026-06-14
  • Main already has: charts/openab, charts/openab-telegram, charts/openab-feishu
  • Net-new value: First LINE platform chart — no overlap with existing charts
What's Good (🟢)
  • Sidecar architecture correctly handles shared-filesystem media delivery constraint
  • Unconditional data volume prevents readOnlyRootFilesystem write failures
  • Recreate strategy avoids RWO PVC lockouts
  • helm.sh/resource-policy: keep protects auth sessions across upgrades
  • sortAlpha ensures deterministic ConfigMap output (GitOps-safe)
  • kindIs "invalid" for null-checks — better than hasKey approach
  • Comprehensive README with architecture diagram, credential options, and troubleshooting
  • All fail guards complete — catches missing credentials at install time
  • Gateway HMAC-SHA256 signature verification aligns with LINE webhook spec
  • Security baseline excellent: readOnlyRootFilesystem + drop ALL + runAsNonRoot + seccomp
  • Health probes aligned with gateway GET /health route and sibling chart pattern
Follow-up Suggestions (non-blocking)
  • platform.allowAllUsers defaults to null — when allowedUsers list is empty this means allow-all. Consider documenting this more prominently or defaulting to false for locked-down installs.
  • Consider adding helm unit tests for the new tunnel/probe/resources logic.

Review coverage: Architecture ✅ | Correctness ✅ | Security/CI ✅ | Spec Verification ✅ | Docs/UX pending (no blockers expected)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants