From 8 Skills to 16: Building an AI Agent Toolbox on Kubernetes
OpenClaw ships with 52 skills but most show missing. Here is how I installed 9 tools on a PVC and doubled the ready count.
OpenClaw ships with 52 bundled skills. Sounds impressive until you open the skills dashboard and see most of them sitting at "missing." The reason: they're wrappers around CLI tools that aren't in the container image. No gh, no GitHub skill. No jq, no structured data parsing. No rg, no code search. Out of the box, I had 8 working skills. Everything else was a dead link.
Here's how I installed 9 tools on a PVC and went from 8 to 16 ready skills, plus the safety assessment that blocked one.
The Constraint
Talos Linux is immutable. There's no SSH, no package manager, no apt-get install on the host. The OpenClaw container image is upstream. I'm not forking it just to add binaries. And even if I did, any tool that needs runtime configuration or auth tokens would still need persistent storage.
So the container is read-only from an image perspective, and the host is untouchable. That's the box I'm working in.
The Solution: PVC as Toolbox
OpenClaw mounts a Longhorn PVC at /home/node/.openclaw/. That directory persists across pod restarts, Flux reconciliations, and node reboots. The trick is treating it as a local package prefix.
Install binaries to /home/node/.openclaw/bin/, then add that to PATH via an environment variable in the Deployment manifest:
env:
- name: PATH
value: "/home/node/.openclaw/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"That's it. Any binary dropped in that directory is available to OpenClaw's agent runtime immediately. No image rebuild, no pod restart needed for existing sessions.
Three Install Patterns
Not every tool installs the same way. I ended up with three distinct patterns.
1. Static Binaries
The simplest path. Download a release tarball, extract, chmod, done. These tools ship as self-contained Linux binaries with zero runtime dependencies:
- gh v2.88.1: GitHub CLI
- gog v0.12.0: Google Workspace CLI
- goplaces v0.3.0: Google Places API
- blogwatcher v0.0.2: blog monitoring
- jq v1.8.1: JSON processor
- rg v15.1.0: ripgrep, fast code search
- codexbar: barcode generation
- uv: Python package manager
The pattern for each is nearly identical:
cd /tmp
wget https://github.com/<org>/<repo>/releases/download/v<version>/<artifact>-linux-amd64.tar.gz
tar xzf <artifact>-linux-amd64.tar.gz
mv <binary> /home/node/.openclaw/bin/
chmod +x /home/node/.openclaw/bin/<binary>Pin the version. Always pin the version. I'm not pulling latest into an agent's toolchain.
2. npm Packages
Some tools are Node.js packages that expect a global install. Since I can't write to the system node_modules, I install to a PVC-based prefix:
npm install -g --prefix /home/node/.openclaw <package>
ln -sf /home/node/.openclaw/lib/node_modules/<package>/bin/<binary> /home/node/.openclaw/bin/<binary>This worked for:
- mcporter: MCP tool integration
- xurl: URL expansion and metadata
- gemini-cli: Google Gemini from the terminal
- Claude Code: Anthropic's CLI agent
The symlink into bin/ keeps the PATH clean. Without it, you'd need to add every package's nested bin/ directory individually.
3. Auth-Required Tools
The hardest category. These tools work fine as binaries but need credentials that must also persist on the PVC:
- gog needs Google OAuth tokens
- gh needs a GitHub Personal Access Token
- gemini-cli needs Google OAuth
- Claude Code needs subscription credentials
Each one has its own auth story, which I'll cover below.
The XDG_CONFIG_HOME Trick
Multiple tools store configuration in ~/.config//. In an ephemeral container, that's gone on every restart. I could create symlinks for each tool, but there's a cleaner fix: one environment variable.
env:
- name: XDG_CONFIG_HOME
value: "/home/node/.openclaw/config"Any XDG-compliant tool (and most modern CLI tools are) will now write its config to /home/node/.openclaw/config// instead of ~/.config//. That directory lives on the PVC. Config persists. Done.
This single env var replaced what would have been a dozen individual symlinks or mount paths.
The Init Container Symlink Trap
Before I discovered the XDG_CONFIG_HOME approach, I tried the obvious thing: an init container that creates symlinks from ~/.config/gogcli to the PVC path.
It didn't work. Init containers run in their own filesystem layer. The symlinks they create in the container's writable overlay don't carry over to the main container. The main container gets a fresh overlay. The init container's work is just gone.
This is one of those Kubernetes gotchas that seems obvious in hindsight but costs you 30 minutes of "why is this empty" debugging. XDG_CONFIG_HOME is the clean fix because it's an env var, not a filesystem mutation.
Safety Assessment
Before installing anything, I checked every tool. Who maintains it? Is the source available? What data does it send, and where?
Most passed without issue. Static binaries from well-known GitHub repos with tagged releases, verifiable checksums, clear scope. But one didn't make the cut.
clawhub: OpenClaw's built-in skill marketplace. It lets agents install arbitrary skills from a registry with no human approval workflow. An agent can discover a skill, install it, and start using it in the same session. No review, no diff, no confirmation.
That's a non-starter. I don't care how convenient it is. I'm not giving an AI agent the ability to install arbitrary code into its own runtime without my explicit approval. Manual installs only. Every tool goes through the same review: source audit, scope check, data flow analysis. Then I install it by hand in an interactive shell.
Tool-by-Tool Auth Setup
Google Workspace (gog)
gog uses OAuth 2.0. In a headless container with no browser, you need the --manual flag, which prints a URL to visit on your local machine. Paste the auth code back into the terminal.
gog auth --manual --scopes calendar.readonlyI started with Calendar read-only and expanded incrementally: Gmail full access, Drive full access, then Docs and Sheets. Each scope expansion requires a new OAuth consent flow. The tokens are stored in gog's config directory, which, thanks to XDG_CONFIG_HOME, lives on the PVC.
For the keyring (where gog stores refresh tokens), I use file-based storage with a password stored in a SOPS-encrypted secret:
env:
- name: GOG_KEYRING_PASSWORD
valueFrom:
secretKeyRef:
name: openclaw-secrets
key: gog-keyring-passwordGitHub CLI (gh)
gh respects the GH_TOKEN environment variable. I created a fine-grained Personal Access Token scoped to specific repositories, not an org-wide classic token. The token is stored in SOPS and injected as an env var:
env:
- name: GH_TOKEN
valueFrom:
secretKeyRef:
name: openclaw-secrets
key: gh-tokenNo interactive auth flow needed. gh sees the token and works immediately.
Gemini CLI
Google OAuth again, but gemini-cli has its own auth flow. The GOOGLE_GENAI_USE_GCA env var tells it to use Google Cloud Application credentials:
env:
- name: GOOGLE_GENAI_USE_GCA
value: "true"Initial auth requires an interactive terminal session (kubectl exec -it) to complete the OAuth browser flow via the manual copy-paste method. After that, tokens persist in the XDG config directory on the PVC.
Claude Code
This one evolved. Initially I installed Claude Code via npm on the PVC, then manually copied OAuth credentials from my macOS Keychain into the PVC-mounted config directory:
env:
- name: CLAUDE_CONFIG_DIR
value: "/home/node/.openclaw/config/claude"That worked but was fragile: credentials would expire and need manual refresh. Later, Anthropic added setup-token support, which lets you provision credentials non-interactively. That superseded the manual Keychain copy approach entirely.
What's Still Blocked
Not every skill made it to "ready":
- summarize: no Linux binary available, macOS only
- model-usage: macOS only
- goplaces: binary installed, but needs a Google Places API key I haven't provisioned yet
- nano-banana-pro: needs API keys
These stay at "missing" until their dependencies are resolved. I'm not rushing to enable things I don't actively need.
The Result: 16 Ready Skills
After all the installs, auth flows, and config persistence work, OpenClaw went from 8 to 16 functional skills:
- coding-agent: code generation and editing
- gog: Google Workspace (Calendar, Gmail, Drive, Docs, Sheets)
- github: GitHub repository operations
- gh-issues: GitHub issue management
- session-logs: conversation history and context
- blogwatcher: blog and feed monitoring
- gemini: Google Gemini integration
- mcporter: MCP tool bridge
- xurl: URL expansion and metadata extraction
- healthcheck: service health monitoring
- node-connect: inter-agent communication
- openai-image-gen: image generation via OpenAI
- openai-whisper-api: speech-to-text transcription
- skill-creator: skill scaffolding and development
- slack: Slack messaging
- weather: weather data retrieval
Each one backed by a pinned binary or npm package on the PVC, with credentials in SOPS secrets, and config persisted via XDG_CONFIG_HOME.
Custom Claude Code Skills
Beyond the built-in OpenClaw skills, I also wrote two custom skills for the Claude Code instance running inside the container:
openclaw-ops: over 400 lines of infrastructure operations context. This skill gives Claude Code deep knowledge of my specific OpenClaw deployment: the Talos constraints, the PVC toolbox layout, the network policies, the auth patterns. When I ask it to troubleshoot or modify the setup, it doesn't start from zero.
openclaw-chat: an agent communication assistant. OpenClaw's multi-agent architecture means agents need to coordinate. This skill provides patterns for inter-agent messaging, context sharing, and task handoff.
Both skills live on the PVC and are loaded via Claude Code's skill configuration. They're the layer that turns a generic AI agent into one that understands *this* infrastructure.
Takeaways
The PVC-as-toolbox pattern works well for immutable OS environments where you can't modify the container image. The key ingredients:
- A persistent volume mounted at a known path
- PATH extended to include a bin directory on that volume
- XDG_CONFIG_HOME pointed at the volume for config persistence
- SOPS secrets for credentials, injected as env vars
- Manual installs only: no agent self-service for tooling
The approach is reproducible. If I lose the PVC, I can rebuild the toolbox from the install notes in about 20 minutes. The credentials would need re-provisioning through their respective OAuth flows, but the binaries are just downloads.
Would I do this differently if I could modify the image? Probably. I'd bake the static binaries into a custom Dockerfile and only use the PVC for auth state. But the whole point of running upstream images is staying on the upgrade path without merge conflicts. The PVC toolbox is the pragmatic middle ground.
Eight skills to sixteen. No image fork. No host modification. Just a PVC and some env vars.