runner: publish gRPC port + grpcs:// for TLS via Caddy Verified

The runner gRPC port (9443) wasn't being published on the host —
only the cave-net podman network could reach it. That made a
laptop runner impossible. Three changes:

cavectl:
- New ports.grpc (default 9443) and ports.grpc_bind (default
  "127.0.0.1") in cave.yaml; cavectl init picks a free port near
  9443.
- createCave publishes the gRPC port via ports.grpc_bind.
  Set ports.grpc_bind: 0.0.0.0 on a public VPS to let remote
  runners connect.

Runner client TLS:
- parse-grpc-url now recognizes grpcs:// alongside grpc://, returns
  a third value (tls-p), defaults the grpcs port to 443.
- handle-runner threads :tls tls-p into the two ag-grpc:make-channel
  calls (initial register + the watch-loop reconnect).
- This lets you front cave's plaintext h2c gRPC with Caddy:
      runner.cave.example.com {
          reverse_proxy h2c://127.0.0.1:9443
      }
  Runners then connect with --url grpcs://runner.cave.example.com,
  Caddy terminates Let's Encrypt TLS, talks h2c to cave's :9443
  on loopback.

Runner registration UI:
- Was hardcoded to grpc://localhost:9443. New runner-grpc-url
  helper derives the right URL from :runner-public-url (override)
  or :base-url's host on :grpc-port, so the copy-paste command on
  the Admin / Settings runner pages reflects the actual deploy.

Caveat documented in cavectl config: cave's gRPC has no native TLS
yet; over the public internet without Caddy in front, the runner
auth token is sent in clear. Use grpcs:// (Caddy in front) or
restrict to a trusted network.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Anthony Green2026-05-25 07:54:57 -0400 (�application/grpc grpc-encodinggzip����HN,K�OI-(�w�,�(M�uL.�+�M�)�y9�)�E%�i@Q]=C=c.]��t݂�|#9'�I/*H��L/J�Œ�^�Z\T�UZ�Z� J#� grpc-status0++4c9b1960602111a0aa1c0a1556792a26ca8259bf
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>