Skip to content

feat: Tier 3 cross-pollination - PaperclipClusterDefaults + PaperclipSelfConfig CRDs + Tailscale sidecar - #68

Merged
stubbi merged 3 commits into
mainfrom
feat/tier3-crds-tailscale
Jun 3, 2026
Merged

feat: Tier 3 cross-pollination - PaperclipClusterDefaults + PaperclipSelfConfig CRDs + Tailscale sidecar#68
stubbi merged 3 commits into
mainfrom
feat/tier3-crds-tailscale

Conversation

@stubbi

@stubbi stubbi commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Tier 3 cross-pollination from openclaw-operator and hermes-operator. Three independent features, one commit each, built on top of the Tier 1+2 parity work.

P1 - PaperclipClusterDefaults CRD (cluster-scoped singleton defaults)

  • New cluster-scoped CRD PaperclipClusterDefaults (name must be cluster, shortName pccd).
  • Supplies org-wide defaults (image, storage class, database mode, observability, networking Service type, shared env vars) merged in-memory into every Instance at reconcile time. The stored spec in etcd is never overwritten; per-instance fields always win. Env is merged by Name (instance entries override defaults).
  • resources.ApplyClusterDefaults (pure function + unit tests), dedicated controller validating the singleton name (Ready / InvalidName condition), and an Instance watch that re-reconciles all Instances when the singleton changes.

P2 - PaperclipSelfConfig CRD (agent-driven, audited, Server-Side Apply)

  • New namespaced CRD PaperclipSelfConfig (shortName pcsc) letting the app request changes to its own parent Instance: addPlugins/removePlugins, patchConfig (deep-merge), addEnvVars/removeEnvVars.
  • Gated by Instance.spec.selfConfigure (enabled + allowedActions allowlist) plus protected config keys (auth/secrets/database/gateway) and protected env vars (DATABASE_URL, BETTER_AUTH_SECRET, ...).
  • Applied via SSA with a dedicated field manager paperclip-selfconfig so GitOps does not flap. Terminal Pending/Applied/Failed/Denied phase, audit events, owner reference for GC, and 1h TTL reaping. Pure apply helpers have unit tests.

P3 - Tailscale sidecar

  • New spec.tailscale (enabled, mode=serve|funnel, image, authKey.secretRef, hostname).
  • Injects an ephemeral (--ephemeral) userspace Tailscale sidecar that Serves the Paperclip app (port 3100) over the tailnet via TS_SERVE_CONFIG (rendered into a managed ConfigMap, funnel mode sets AllowFunnel). Read-only root fs, all caps dropped.
  • NetworkPolicy gains STUN (3478/udp) + WireGuard (41641/udp) egress when enabled (443/tcp for DERP/control already allowed). TailscaleReady condition. Builder unit tests included.

Registration / generation

  • Registered both new CRDs + controllers in cmd/main.go and PROJECT; updated config kustomizations, RBAC (kubebuilder markers + viewer/editor/admin roles + Helm chart RBAC), samples.
  • Ran make generate && make manifests && make sync-chart-crds && make api-docs; committed regenerated CRDs, deepcopy, chart CRDs, and api-reference.

Validation

  • go build ./..., go vet ./...: clean
  • make lint (golangci-lint v2.11.4): 0 issues
  • go test ./internal/resources/... ./api/... and make test (envtest, k8s 1.33): pass
  • hack/sync-chart-crds.sh --check: in sync; hack/check-helm-rbac-sync.sh: in sync; helm lint: 0 failed
  • ASCII-only dash scan on changed files: clean

Notes

  • Do NOT merge / do NOT enable auto-merge.
  • The OLM bundle (bundle/) was not regenerated: there is no sync-bundle-crds target and the CSV was not touched. The bundle CSV will need the two new CRDs added in a follow-up before an OLM release.

🤖 Generated with Claude Code

stubbi and others added 3 commits June 3, 2026 22:17
Add a cluster-scoped singleton (name must be "cluster") that supplies
org-wide defaults merged into every Instance at reconcile time. The merge
happens in-memory only; the user's stored spec in etcd is never overwritten
and per-instance fields always win.

Defaults cover image, storage class, database mode, observability, networking
Service type, and shared env vars (merged by Name, instance entries win). The
Instance reconciler fetches the singleton, applies ApplyClusterDefaults before
rendering owned resources, and watches the singleton to re-reconcile Instances.

A dedicated PaperclipClusterDefaults controller validates the singleton name
and surfaces a Ready/InvalidName condition. Also wires the cross-cutting
generated artifacts (manifests, deepcopy, chart CRDs, RBAC, samples, docs) and
adds the spec fields and controller registration shared by the Tier 3 work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…a SSA

Add a namespaced PaperclipSelfConfig CRD that lets the Paperclip app request
changes to its own parent Instance (addPlugins/removePlugins, patchConfig,
addEnvVars/removeEnvVars). Requests are gated by the parent Instance's
spec.selfConfigure allowlist (enabled + allowedActions) and a set of protected
config keys and protected env vars so an agent cannot touch auth, secrets,
database, or operator-managed env.

Approved changes are applied to the Instance via Server-Side Apply with a
dedicated field manager ("paperclip-selfconfig") so GitOps controllers do not
flap over the agent-owned fields. The controller records audit events, sets a
terminal Pending/Applied/Failed/Denied phase, owns the request via an owner
reference, and TTL-reaps completed requests after one hour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add spec.tailscale (enabled, mode=serve|funnel, image, authKey.secretRef,
hostname) and an ephemeral userspace Tailscale sidecar that Serves the
Paperclip app (port 3100) over the tailnet via TS_SERVE_CONFIG. The node runs
with --ephemeral so it is removed from the tailnet when the pod is deleted, and
the sidecar runs with a read-only root filesystem and all capabilities dropped.

The serve config is rendered into a managed ConfigMap and mounted into the
sidecar; funnel mode additionally sets AllowFunnel. The Instance reconciler
provisions the ConfigMap (surfacing a TailscaleReady condition) and the
NetworkPolicy gains STUN (3478/udp) and WireGuard (41641/udp) egress when
Tailscale is enabled (443/tcp for DERP/control is already allowed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@stubbi
stubbi force-pushed the feat/tier3-crds-tailscale branch from 025be4c to 9a42765 Compare June 3, 2026 20:18
@stubbi
stubbi merged commit 5bb11f0 into main Jun 3, 2026
10 of 14 checks passed
@stubbi
stubbi deleted the feat/tier3-crds-tailscale branch June 3, 2026 20:22
@stubbi stubbi mentioned this pull request Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant