feat: Tier 3 cross-pollination - PaperclipClusterDefaults + PaperclipSelfConfig CRDs + Tailscale sidecar - #68
Merged
Merged
Conversation
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
force-pushed
the
feat/tier3-crds-tailscale
branch
from
June 3, 2026 20:18
025be4c to
9a42765
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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)
PaperclipClusterDefaults(name must becluster, shortNamepccd).Instanceat 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)
PaperclipSelfConfig(shortNamepcsc) letting the app request changes to its own parentInstance:addPlugins/removePlugins,patchConfig(deep-merge),addEnvVars/removeEnvVars.Instance.spec.selfConfigure(enabled+allowedActionsallowlist) plus protected config keys (auth/secrets/database/gateway) and protected env vars (DATABASE_URL, BETTER_AUTH_SECRET, ...).paperclip-selfconfigso 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
spec.tailscale(enabled,mode=serve|funnel,image,authKey.secretRef,hostname).--ephemeral) userspace Tailscale sidecar that Serves the Paperclip app (port 3100) over the tailnet viaTS_SERVE_CONFIG(rendered into a managed ConfigMap, funnel mode setsAllowFunnel). Read-only root fs, all caps dropped.TailscaleReadycondition. Builder unit tests included.Registration / generation
cmd/main.goandPROJECT; updated config kustomizations, RBAC (kubebuilder markers + viewer/editor/admin roles + Helm chart RBAC), samples.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 ./...: cleanmake lint(golangci-lint v2.11.4): 0 issuesgo test ./internal/resources/... ./api/...andmake test(envtest, k8s 1.33): passhack/sync-chart-crds.sh --check: in sync;hack/check-helm-rbac-sync.sh: in sync;helm lint: 0 failedNotes
bundle/) was not regenerated: there is nosync-bundle-crdstarget 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