Switch Docker runtime from scratch to alpine:3.19 so kubectl exec works for admin CLI operations. Add `synapbus channels create` and `synapbus channels join` CLI commands with corresponding admin socket handlers. Change default socket path to /data/synapbus.sock (absolute). Also add Helm envFrom support and NodePort configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.4 KiB
Research: Admin CLI & Docker Fixes
Feature: 006-admin-cli-docker-fixes Date: 2026-03-15
R1: Alpine vs Scratch Docker Base Image
Decision: Use alpine:3.19 as the runtime base image.
Rationale: The scratch image has no shell, no /bin/sh, no filesystem utilities. This means kubectl exec cannot spawn any process other than the entrypoint binary itself. Since admin CLI commands need to connect to the Unix socket created by the running server process, exec'd processes need a working environment. Alpine adds ~7MB but provides /bin/sh, basic filesystem operations, and a working process environment.
Alternatives considered:
distroless/static(Google): No shell, same problem as scratch.busybox: Works but no package manager. Alpine is the standard minimal base.debian-slim: ~80MB, unnecessarily large.
R2: Admin Socket Protocol for Channel Operations
Decision: Add channels.create and channels.join commands to the existing admin socket dispatch table, following the exact pattern of existing commands (e.g., agent.create, webhook.register).
Rationale: The admin socket already has a well-established request/response pattern: JSON-RPC style {command, args} → {ok, data, error}. The channel service already exposes CreateChannel and JoinChannel methods. The admin server already holds a reference to the channel service via Services.Channels. No new wiring needed.
Alternatives considered:
- HTTP admin API endpoint: Would require API key protection (user explicitly rejected this approach).
- Direct database manipulation via CLI: Bypasses service layer validation, unsafe.
R3: Default Socket Path
Decision: Change default from ./data/synapbus.sock to /data/synapbus.sock (absolute).
Rationale: In containers, the working directory is / and the data volume is mounted at /data. The relative path ./data/synapbus.sock resolves to /data/synapbus.sock from /, but this is confusing and fragile. An absolute default matches the Dockerfile's --data /data argument and the Helm chart's volumeMount at /data.
The SYNAPBUS_SOCKET environment variable and --socket flag still allow overriding for development (e.g., --socket ./data/synapbus.sock for local dev).
Alternatives considered:
- Keep relative path: Works in containers but confusing for users.
- Use
$SYNAPBUS_DATA_DIR/synapbus.sockas default: Over-engineered; the socket path flag already exists.
R4: channels.create Admin Handler Design
Decision: The channels.create handler accepts {name, description} args, calls channelService.CreateChannel with created_by: "system", and returns the created channel as JSON.
Rationale: Admin socket commands are implicitly trusted (localhost-only, process-level access). Using "system" as the creator matches the pattern used for the default #general channel. The description field is optional (defaults to empty string).
R5: channels.join Admin Handler Design
Decision: The channels.join handler accepts {channel, agent} args, looks up the channel by name via GetChannelByName, then calls JoinChannel(channelID, agentName). Returns success message.
Rationale: The CLI uses channel names (not IDs) because operators work with names. The service's JoinChannel already handles idempotency (re-joining is a no-op) and private channel invite checks.