mathiasandClaude Opus 5 7db7e0779b
release / tag (push) Successful in 1s
feat(mcp): serve brain and infra over Tailscale Serve with real TLS
Replaces the raw NodePort URLs with Tailscale Serve endpoints on koala:

  brain  https://koala.tail30472e.ts.net:8443/mcp  -> 127.0.0.1:30330
  infra  https://koala.tail30472e.ts.net:8444/mcp  -> 127.0.0.1:30342

Why this beats both alternatives that were tried:

The public d-ma.be path exits to Cloudflare and NPM to reach a k3s service on
the machine the session is running on, and Cloudflare rate-limits and
size-caps tool results — the original complaint. The raw NodePort path fixed
that but only worked from koala: every namespace carries default-deny-ingress
and an allow policy naming the traefik namespace, so kube-proxy's
KUBE-EXTERNAL-SERVICES chain RSTs external callers. Admitting tailnet clients
there would need externalTrafficPolicy: Local on each service plus an ipBlock
per policy, because the default Cluster policy SNATs the source and an ipBlock
can never match.

Serve sidesteps all of it. tailscaled terminates TLS with a genuine cert and
proxies over loopback, so the request reaches the NodePort from koala itself —
the case the NetworkPolicy already permits. No policy change, no
externalTrafficPolicy change, no firewall rule, no cert-manager, no DNS record.

Port-per-service rather than path-per-service: the mapping is 1:1 with no
prefix rewriting, so what the client requests and what the server sees cannot
desync.

Verified: 200 authenticated in 7.6ms from koala, and `claude mcp list` reports
Connected against the Serve URL. An earlier apparent OAuth resource-metadata
mismatch turned out to be an artifact of testing without an Authorization
header — with a valid bearer the client never enters OAuth discovery.

Client requirement: `tailscale set --accept-dns=true`. Confirmed on iguana that
100.100.100.100 resolves the MagicDNS name correctly while the system resolver
does not, so the name fails to resolve until Tailscale is accepted as a DNS
provider. Tracked in #24 as the Mac prerequisite. Note Serve matches on SNI, so
substituting the raw tailnet IP is not a workaround.

Bump-Type: minor

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-24 21:11:49 +02:00

skills

Engineering skills library — markdown SKILL.md files that plug into any AI-coding harness.

For Claude Code this repo is a plugin, installed from the marketplace it also hosts; that path carries the hooks and MCP servers too. For Crush, Pi and OpenCode it is a plain library wired by install.sh as one directory symlink per harness. See SKILLS_INDEX.md.

What is a skill?

A skill is a single directory containing one or more markdown files. The canonical entrypoint is <name>/SKILL.md. Additional reference material lives alongside in the same directory (e.g. clean-code/references/code-smells.md). Harnesses load skills on demand — keep SKILL.md short and triggerable; push depth into references.

See SKILLS_INDEX.md for the full catalogue with descriptions and "use when" triggers.

Install

Claude Code — the plugin

/plugin marketplace add https://git.d-ma.be/mathias/skills.git
/plugin install skills@mathias

The plugin ships skills, the secret-guard + pre-task-ritual hooks, and the brain/gitea/infra MCP servers as one versioned unit. Skills are namespaced (skills:tdd). Updates land when .claude-plugin/plugin.json's version moves — CI bumps it on every release.

Crush, Pi, OpenCode — install.sh

curl -fsSL https://git.d-ma.be/mathias/skills/raw/branch/main/install.sh | bash

Idempotent. It clones or fast-forwards the checkout, then creates three symlinks — one directory link per harness, never one per skill:

Link Target
~/.agents/skills $SKILLS_CHECKOUT_DIR/skills (Pi reads this path directly)
~/.config/crush/skills ~/.agents/skills
~/.config/opencode/skills ~/.agents/skills

Adding a skill wires it everywhere with no re-run.

Where the checkout must live

SKILLS_CHECKOUT_DIR defaults to ~/.local/share/skills, and that default is load-bearing: it is host-local, outside any synced or shared directory.

On this fleet ~/dev is an iCloud folder shared by flamingo and iguana, and it spans two path roots — /Users/mathias on the Macs, /home/mathias on koala. A checkout there is a git working tree with two concurrent writers, and any absolute symlink stored there is broken on one side by construction. That is what produced 243 dangling links across 12 repos (#23). Do not "fix" the default to point into ~/dev.

Per-host env-var overrides

Env var Default
SKILLS_REPO_URL https://git.d-ma.be/mathias/skills.git
SKILLS_REF main (set to e.g. v0.4.0 to pin a release)
SKILLS_CHECKOUT_DIR $HOME/.local/share/skills
SKILLS_SKIP_FETCH unset (1 wires an existing checkout without touching HEAD)
AGENTS_SKILLS_DIR $HOME/.agents/skills
CRUSH_SKILLS_DIR $HOME/.config/crush/skills
OPENCODE_SKILLS_DIR $HOME/.config/opencode/skills

Antigravity and Mistral Vibe targets were removed — unused.

Checking a host

task doctor      # or bin/doctor

Reports dangling links, links resolving into a foreign checkout, skill-count mismatches, stray entries, and leftover per-skill Claude symlinks. Non-zero exit on drift. It also runs at session start via the plugin, so a drifting host says so rather than waiting to be discovered.

Versioning

Every commit to main is auto-tagged as a semver release by the .gitea/workflows/release.yml action. Bump direction follows the commit footer:

  • Bump-Type: major → X.0.0
  • Bump-Type: minor → 0.Y.0
  • anything else → 0.0.Z (patch, the default)

Consumer projects can pin a version by setting SKILLS_REF=v0.1.0 before task skills:install; default is main for always-current.

Contributing a skill

  1. Add a directory <short-kebab-name>/.
  2. Create <short-kebab-name>/SKILL.md — start with one-paragraph purpose, then triggers ("use when …"), then mechanics.
  3. Add a row to SKILLS_INDEX.md (description + use-when).
  4. Commit directly to main. Conventional-commit prefix (feat: / fix: / docs:). Add Bump-Type: minor footer when adding a new skill or expanding scope; default patch covers edits.

This repo is markdown-only and public — never include secrets, client names, or anything under NDA in a skill.

S
Description
Engineering skills library — markdown SKILL.md files installed into any project or harness via task skills:install
Readme
853 KiB
Languages
Shell 84%
JavaScript 9.5%
HTML 3.9%
TypeScript 2.6%