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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ directory containing `Chart.yaml` is an independently versioned chart.
| --- | --- |
| `agent-pipe-uploader/` | Internal presigned-S3 artifact uploader Service and Deployment |
| `opencode-server/` | OpenCode Deployment and non-secret configuration |
| `opencode-sms-bridge/` | Signed Twilio SMS/MMS bridge Deployment for fixed OpenCode mobile agents |

## Guides

Expand Down
2 changes: 1 addition & 1 deletion opencode-server/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
apiVersion: v2
name: opencode-server
description: OpenCode server Deployment and non-secret configuration
version: 0.1.57
version: 0.1.58
appVersion: "1.18.23"
type: application
14 changes: 14 additions & 0 deletions opencode-server/files/agents/grillmaster-sms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: SMS-only charcoal cooking adviser with no operational tools
mode: primary
model: kimi-for-coding/k3
variant: high
permission:
"*": deny
---

# Grillmaster SMS Agent

You provide short, practical charcoal-cooking advice through carrier SMS/MMS. Ask only for the ingredients, equipment state, temperature, and time needed to make the next safe decision. Separate observations from estimates, flag food-safety uncertainty, and recommend a thermometer rather than guessing doneness.

You have no tools and must not claim to inspect private knowledge, inventories, repositories, live systems, recipes, or external sources. Never disclose credentials, addresses, private inventory, or operational details. Keep replies SMS-sized and offer one clear next action.
16 changes: 16 additions & 0 deletions opencode-server/files/agents/homerepair-sms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: SMS-only household repair triage assistant with no operational tools
mode: primary
model: kimi-for-coding/k3
variant: high
permission:
"*": deny
---

# Home Repair SMS Agent

You provide concise household-repair triage through carrier SMS/MMS. Describe visible evidence before conclusions and request a wide view, close-up, and relevant product label when an image is needed. Prefer the least-invasive diagnostic step and state appropriate PPE, isolation, and stop conditions.

Immediately direct the owner to emergency services, the utility, or a qualified professional for fire, gas odor or leak, active arcing, flooded electrical areas, major active leaks, threatened collapse, or other immediate danger. Do not advise work on energized electrical circuits, gas systems, fuel-burning appliances, refrigerant, structural elements, concealed plumbing, hazardous materials, or dangerous heights.

You have no tools and must not claim to inspect private knowledge, repositories, live systems, manuals, or external sources. Never ask for or disclose credentials, addresses, inventory, security details, or other sensitive information.
14 changes: 14 additions & 0 deletions opencode-server/files/agents/homesteader-sms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: SMS-only generic outdoor and household observation assistant with no private-knowledge or operational tools
mode: primary
model: kimi-for-coding/k3
variant: high
permission:
"*": deny
---

# Homesteader SMS Agent

You provide generic, observation-led outdoor, garden, animal-care, and household-maintenance guidance through carrier SMS/MMS. Ask for safe observations, measurements, and clear photos; state uncertainty plainly and prefer reversible, low-risk checks.

This is not a channel for confidential homestead knowledge. You have no tools and must not read, request, infer, or disclose property location, security, access, livestock, equipment, inventory, projects, credentials, or other private records. Do not claim to inspect repositories, private knowledge, live systems, manuals, or external sources. Escalate urgent animal, fire, gas, electrical, medical, or safety concerns to an appropriate local professional or emergency service.
14 changes: 14 additions & 0 deletions opencode-server/files/agents/lawnmowerman-sms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: SMS-only lawn and small-engine troubleshooting assistant with no operational tools
mode: primary
model: kimi-for-coding/k3
variant: high
permission:
"*": deny
---

# Lawnmowerman SMS Agent

You provide concise, safety-first small-engine and lawn-equipment troubleshooting through carrier SMS/MMS. Describe visible evidence and request a clearer image when needed. Start hands-on guidance with the relevant precautions: disconnect the spark plug before working near blades or rotating parts, let hot equipment cool, and keep fuel away from ignition sources.

You have no tools and must not claim to inspect repositories, private knowledge, live systems, manuals, or external sources. Never ask for or disclose credentials, access details, inventory records, addresses, or other sensitive information. For exact specifications, part numbers, or an unsafe condition, say what observation or official manual is needed and recommend qualified help when appropriate.
6 changes: 6 additions & 0 deletions opencode-sms-bridge/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v2
name: opencode-sms-bridge
description: Signed Twilio SMS and MMS bridge for fixed OpenCode agents
type: application
version: 0.1.0
appVersion: "1"
30 changes: 30 additions & 0 deletions opencode-sms-bridge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# OpenCode SMS bridge chart

This chart deploys the portable, single-replica `opencode-sms-bridge` Pod: a public signed-webhook ingress container and a private queue worker container. It is not a Twilio-account or cluster-integration chart.

## Ownership

The chart owns a Deployment and non-secret ConfigMap. `makeitworkcloud/kustomize-cluster` owns the existing `opencode` namespace, RWO state PVC, stable ClusterIP Service, public workload `TunnelBinding`, and all SOPS-encrypted Secret values. Do not define those resources here.

The Pod uses `Recreate` because the SQLite queue/session store is intentionally single-writer on an RWO PVC. It is annotated with the installed Reloader's named-Secret annotation so rotation of any referenced bridge or OpenCode Basic-auth Secret replaces both containers.

## Prerequisites

The GitOps consumer must provide these Secrets in the `opencode` namespace:

- `opencode-sms-bridge-routing` with `routing.json`, containing the Twilio account identifier, initial approved sender set, and exactly four fixed destination-to-`*-sms` agent mappings;
- `opencode-sms-bridge-shared` with the webhook-validation token, Fernet state-encryption key, and independent sender-HMAC key;
- `opencode-sms-bridge-worker` with a least-privilege Twilio API Key SID/secret for outbound replies; and
- the existing `opencode-server-auth` Secret for the worker's private OpenCode HTTP request.

Use an immutable published image SHA in `image.tag`; `latest` is only the source-chart default and must never be selected by the GitOps consumer.

## Image and audio gates

Image parts are disabled by default. Enable `config.imagePartsEnabled` only after the selected OpenCode model and deployed file-part API are functionally verified. Keep `config.whisperUrl` empty until a local Whisper-compatible service, capacity, and retention boundary are selected; audio MMS then receives a bounded non-processing reply rather than unvalidated forwarding.

## Rendered resource

- Deployment, sync wave `2`, with two hardened containers and a `Recreate` strategy.

The corresponding cluster overlay must place state, Secrets, and Service in wave `1`; the `TunnelBinding` belongs in wave `3`. These waves order only resources within the existing `opencode` child Application.
13 changes: 13 additions & 0 deletions opencode-sms-bridge/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{- define "opencode-sms-bridge.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{- define "opencode-sms-bridge.fullname" -}}
{{- default (include "opencode-sms-bridge.name" .) .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{- define "opencode-sms-bridge.labels" -}}
app: {{ include "opencode-sms-bridge.fullname" . }}
app.kubernetes.io/name: {{ include "opencode-sms-bridge.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
17 changes: 17 additions & 0 deletions opencode-sms-bridge/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "opencode-sms-bridge.fullname" . }}-config
labels:
{{- include "opencode-sms-bridge.labels" . | nindent 4 }}
data:
CANONICAL_WEBHOOK_URL: {{ .Values.config.canonicalWebhookUrl | quote }}
STATE_PATH: {{ .Values.config.statePath | quote }}
TWILIO_MEDIA_ALLOWED_HOSTS: {{ join "," .Values.config.mediaAllowedHosts | quote }}
MAX_MEDIA_BYTES: {{ .Values.config.maxMediaBytes | quote }}
MAX_AUDIO_SECONDS: {{ .Values.config.maxAudioSeconds | quote }}
OPENCODE_IMAGE_PARTS_ENABLED: {{ .Values.config.imagePartsEnabled | quote }}
WHISPER_URL: {{ .Values.config.whisperUrl | quote }}
WHISPER_MODEL: {{ .Values.config.whisperModel | quote }}
OPENCODE_API_BASE_URL: {{ .Values.config.opencodeApiBaseUrl | quote }}
OPENCODE_SERVER_USERNAME: {{ .Values.config.opencodeUsername | quote }}
168 changes: 168 additions & 0 deletions opencode-sms-bridge/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "opencode-sms-bridge.fullname" . }}
annotations:
argocd.argoproj.io/sync-wave: "2"
ignore-check.kube-linter.io/unset-cpu-requirements: "single-node policy"
ignore-check.kube-linter.io/unset-memory-requirements: "single-node policy"
secret.reloader.stakater.com/reload: {{ join "," (list .Values.secrets.routing .Values.secrets.shared .Values.secrets.worker .Values.secrets.opencodeServerAuth) | quote }}
labels:
{{- include "opencode-sms-bridge.labels" . | nindent 4 }}
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: {{ include "opencode-sms-bridge.fullname" . }}
template:
metadata:
annotations:
checksum/opencode-sms-bridge-config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
labels:
{{- include "opencode-sms-bridge.labels" . | nindent 8 }}
spec:
automountServiceAccountToken: false
securityContext:
fsGroup: 1000
runAsGroup: 1000
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: ingress
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
env:
- name: BRIDGE_MODE
value: ingress
- name: ROUTING_CONFIG_PATH
value: /etc/opencode-sms-bridge/routing.json
- name: STATE_ENCRYPTION_KEY
valueFrom:
secretKeyRef: {name: {{ .Values.secrets.shared }}, key: state-encryption-key}
- name: SENDER_HASH_KEY
valueFrom:
secretKeyRef: {name: {{ .Values.secrets.shared }}, key: sender-hash-key}
- name: TWILIO_AUTH_TOKEN
valueFrom:
secretKeyRef: {name: {{ .Values.secrets.shared }}, key: twilio-auth-token}
- name: CANONICAL_WEBHOOK_URL
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: CANONICAL_WEBHOOK_URL}
- name: STATE_PATH
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: STATE_PATH}
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet: {path: /healthz, port: http}
livenessProbe:
httpGet: {path: /healthz, port: http}
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
readOnlyRootFilesystem: true
volumeMounts:
- name: state
mountPath: /state
- name: routing
mountPath: /etc/opencode-sms-bridge
readOnly: true
- name: tmp
mountPath: /tmp
- name: worker
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
env:
- name: BRIDGE_MODE
value: worker
- name: ROUTING_CONFIG_PATH
value: /etc/opencode-sms-bridge/routing.json
- name: STATE_ENCRYPTION_KEY
valueFrom:
secretKeyRef: {name: {{ .Values.secrets.shared }}, key: state-encryption-key}
- name: SENDER_HASH_KEY
valueFrom:
secretKeyRef: {name: {{ .Values.secrets.shared }}, key: sender-hash-key}
- name: TWILIO_AUTH_TOKEN
valueFrom:
secretKeyRef: {name: {{ .Values.secrets.shared }}, key: twilio-auth-token}
- name: TWILIO_API_KEY_SID
valueFrom:
secretKeyRef: {name: {{ .Values.secrets.worker }}, key: twilio-api-key-sid}
- name: TWILIO_API_KEY_SECRET
valueFrom:
secretKeyRef: {name: {{ .Values.secrets.worker }}, key: twilio-api-key-secret}
- name: OPENCODE_SERVER_PASSWORD
valueFrom:
secretKeyRef: {name: {{ .Values.secrets.opencodeServerAuth }}, key: password}
- name: CANONICAL_WEBHOOK_URL
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: CANONICAL_WEBHOOK_URL}
- name: STATE_PATH
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: STATE_PATH}
- name: TWILIO_MEDIA_ALLOWED_HOSTS
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: TWILIO_MEDIA_ALLOWED_HOSTS}
- name: MAX_MEDIA_BYTES
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: MAX_MEDIA_BYTES}
- name: MAX_AUDIO_SECONDS
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: MAX_AUDIO_SECONDS}
- name: OPENCODE_IMAGE_PARTS_ENABLED
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: OPENCODE_IMAGE_PARTS_ENABLED}
- name: WHISPER_URL
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: WHISPER_URL}
- name: WHISPER_MODEL
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: WHISPER_MODEL}
- name: OPENCODE_API_BASE_URL
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: OPENCODE_API_BASE_URL}
- name: OPENCODE_SERVER_USERNAME
valueFrom:
configMapKeyRef: {name: {{ include "opencode-sms-bridge.fullname" . }}-config, key: OPENCODE_SERVER_USERNAME}
livenessProbe:
exec:
command:
- python3
- -c
- import urllib.request; urllib.request.urlopen('http://127.0.0.1:8081/healthz', timeout=2)
readinessProbe:
exec:
command:
- python3
- -c
- import urllib.request; urllib.request.urlopen('http://127.0.0.1:8081/healthz', timeout=2)
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
readOnlyRootFilesystem: true
volumeMounts:
- name: state
mountPath: /state
- name: routing
mountPath: /etc/opencode-sms-bridge
readOnly: true
- name: tmp
mountPath: /tmp
volumes:
- name: state
persistentVolumeClaim:
claimName: {{ .Values.persistence.existingClaim }}
- name: routing
secret:
secretName: {{ .Values.secrets.routing }}
items:
- key: routing.json
path: routing.json
- name: tmp
emptyDir: {}
29 changes: 29 additions & 0 deletions opencode-sms-bridge/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
fullnameOverride: opencode-sms-bridge

image:
repository: ghcr.io/makeitworkcloud/opencode-sms-bridge
# GitOps must replace this with the immutable image commit SHA after publication.
tag: latest

persistence:
existingClaim: opencode-sms-bridge-state

secrets:
routing: opencode-sms-bridge-routing
shared: opencode-sms-bridge-shared
worker: opencode-sms-bridge-worker
opencodeServerAuth: opencode-server-auth

config:
canonicalWebhookUrl: https://sms-opencode.makeitwork.cloud/twilio/inbound
statePath: /state/opencode-sms-bridge.db
mediaAllowedHosts:
- api.twilio.com
maxMediaBytes: 5242880
maxAudioSeconds: 120
imagePartsEnabled: false
# Set only after a local Whisper-compatible service is selected and validated.
whisperUrl: ""
whisperModel: base
opencodeApiBaseUrl: http://opencode.opencode.svc:4096
opencodeUsername: opencode