Compare commits

..
4 Commits
Author SHA1 Message Date
mathias 38f905e705 chore(release): plugin.json version 0.6.0
Skip-Release: true
2026-08-24 19:16:00 +00:00
mathiasandClaude Opus 5 7db7e0779b feat(mcp): serve brain and infra over Tailscale Serve with real TLS
release / tag (push) Successful in 1s
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
mathias 0be3506799 chore(release): plugin.json version 0.5.0
Skip-Release: true
2026-08-24 16:01:14 +00:00
mathiasandClaude Opus 5 6151b973bc perf(mcp): reach brain and infra over tailnet NodePorts, not the public edge
release / tag (push) Successful in 1s
Every MCP call from Claude Code was going out through claude.ai's connector
infrastructure, across the public internet, through Cloudflare and NPM on
piguard, back into koala's k3s ingress — to reach a service on the same
machine the session is running on. For brain that means a two-cloud round trip
to read /var/lib/supervisor/brain, a file on koala's local disk.

Cloudflare rate-limits and size-caps tool results on that path, which is the
actual user-facing symptom. Measured from koala:

  brain  koala:30330    200 in  6.8ms
  infra  koala:30342    200 in  3.6ms
  brain  public edge    200 in 27.7ms

infra starts working again as a side effect: its only fault was an SNI/cert
gap at the public edge (TLS "unrecognized name"), which the NodePort path does
not traverse. No NPM change needed for local use.

Note this is not a relocation of existing config. The mcpServers block that
lived in ~/.claude/settings.json was inert — Claude Code reads server
definitions from ~/.claude.json, project .mcp.json, plugins and connectors,
not from settings.json. Verified: a uniquely-named server placed there is
invisible to both `claude mcp list` and `claude mcp get`. So before the plugin
existed, koala had no direct path at all and every call went via Cloudflare by
construction. That also explains how a stale GITEA_MCP_TOKEN went unnoticed
for five days: nothing consumed it.

Safe to land now because koala is the only host with the plugin installed
(#24). The koala firewall must allow 30330/30342 on the Tailscale interface —
NOT on the LAN broadly, so tailnet membership stays the perimeter — before the
Macs get the plugin. Tracked as a prerequisite in #24. Measured today: those
ports are refused from iguana over both LAN and tailnet while :22 answers, so
the rule does not exist yet.

Bump-Type: minor

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-24 18:00:17 +02:00
2 changed files with 5 additions and 4 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "skills", "name": "skills",
"description": "Mathias's engineering skills library, plus the hooks and MCP servers that make it enforceable: TDD, clean code, debugging, specs, discovery, CI, and secret hygiene.", "description": "Mathias's engineering skills library, plus the hooks and MCP servers that make it enforceable: TDD, clean code, debugging, specs, discovery, CI, and secret hygiene.",
"version": "0.4.2", "version": "0.6.0",
"author": { "author": {
"name": "Mathias", "name": "Mathias",
"url": "https://git.d-ma.be/mathias" "url": "https://git.d-ma.be/mathias"
+4 -3
View File
@@ -1,15 +1,16 @@
{ {
"_comment": "Tailscale Serve on koala, not the public d-ma.be hostnames. The public path exits to Cloudflare and NPM to reach a k3s service on the same machine most sessions run on, which imposes rate limits and message-size caps on tool results. Serve terminates TLS with a real Tailscale cert and proxies to the NodePort over loopback, so traffic reaches the service from koala itself and satisfies the default-deny-ingress NetworkPolicy with no cluster change. Clients need 'tailscale set --accept-dns=true' for the MagicDNS name to resolve; see mathias/skills#24.",
"mcpServers": { "mcpServers": {
"brain": { "brain": {
"type": "http", "type": "http",
"url": "https://brain-mcp.d-ma.be/mcp", "url": "https://koala.tail30472e.ts.net:8443/mcp",
"headers": { "headers": {
"Authorization": "Bearer ${BRAIN_MCP_TOKEN}" "Authorization": "Bearer ${BRAIN_MCP_TOKEN}"
} }
}, },
"infra": { "infra": {
"type": "http", "type": "http",
"url": "https://infra-mcp.d-ma.be/mcp" "url": "https://koala.tail30472e.ts.net:8444/mcp"
} }
} }
} }