Documentation
¶
Overview ¶
Package commserver exposes the inter-session communication subsystem (internal/comm) as an MCP endpoint, separate from the knowledge base's.
Why a separate endpoint and not more tools on /mcp ¶
A client registers Ken twice. That separation is a security property, not packaging taste: a knowledge-base token cannot send messages and a comm token cannot write knowledge, each surface gets independent rate accounting so a poll loop cannot starve `kb_*` calls, revocation is per-surface, and an operator can firewall or disable one without the other. It also lets this endpoint refuse the permissive CORS the knowledge-base endpoint needs for a browser-based connector, since nothing here has a browser client.
What this package does not do ¶
It does not decide whether a receiving session should act on a message. The instructions tell a model to treat message content as data and to confirm with its human before acting on instructions found inside one — and that is advice, not a control: Ken cannot verify a client surfaced it, cannot scope it per tool, and gets no signal that a human confirmed anything. The enforced boundary is upstream, in who may open a channel at all: a human mints the pairing code. docs/COMM.md §8 states this rather than implying a guarantee this code cannot make.
Index ¶
Constants ¶
const ( ScopeComm = "comm" ScopeCommFile = "comm-file" )
ScopeComm is the capability required by every tool on this endpoint.
ScopeCommFile is RESERVED and required by nothing yet: file exchange is deferred to a later MINOR (docs/COMM.md §11). It is declared now because splitting a shipped `comm` scope into two later would be a MAJOR under COMPATIBILITY.md, while merging two into one is free.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Deps ¶
type Deps struct {
// Comm is the message store. Required.
Comm *comm.Store
// Store is the knowledge base, used ONLY to authenticate bearer tokens.
// Nothing in this package reads or writes knowledge.
Store *store.Store
// TokenLimiter is comm's own rate bucket, separate from the knowledge base's.
// Optional; nil disables per-token limiting on this endpoint.
TokenLimiter ratelimit.Limiter
// Metrics is optional.
Metrics *metrics.Registry
// MaxPollWaitSeconds bounds a long poll. Clamped server-side regardless of
// what an operator configures, because a wait that ties or exceeds the client's
// tool timeout turns a successful empty poll into a tool ERROR, which models
// handle badly — and reverse proxies commonly read-timeout at 60s.
MaxPollWaitSeconds int
}
Deps are the collaborators for the comm MCP endpoint.
type FileHandler ¶
type FileHandler struct {
// contains filtered or unexported fields
}
The byte-relay HTTP surface: PUT and GET /comm/files/{grant}.
This exists because payload bytes must never travel as tool-call tokens (C8): tool arguments are generated token by token by a model, so the model mints a one-time grant over MCP and then drives the actual bytes with a shell tool — curl does resumable HTTP correctly, and the tokens spent are one command line.
Two credentials are required on every request, deliberately: the bearer token (which must carry `comm-file` and must OWN the endpoint the grant was minted for) and the grant itself (single-use, minutes-lived, bound to one attachment and one direction). A leaked URL is useless without the token; a leaked token cannot touch bytes it was never granted. This is what "no HTTP path serves an attachment without authentication" means in practice.
func NewFileHandler ¶
func NewFileHandler(d Deps, h *Handler) *FileHandler
NewFileHandler builds the relay surface. h supplies the long-poll wakeup.
func (*FileHandler) ServeHTTP ¶
func (f *FileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
type Handler ¶
Handler is the comm MCP endpoint.
func NewHTTPHandler ¶
NewHTTPHandler builds the comm endpoint: a streamable-HTTP MCP server wrapped in comm-only bearer auth.
func (*Handler) Drain ¶
func (h *Handler) Drain()
Drain wakes every parked long poll and refuses new ones. Call before HTTP shutdown so a restart is invisible to connected agents rather than producing a burst of severed connections.
func (*Handler) ParkedWaiters ¶
ParkedWaiters reports how many long polls are currently parked (metrics/tests).
func (*Handler) SetMaxPollWait ¶
SetMaxPollWait updates the long-poll ceiling live. The value is clamped to hardMaxPollWait no matter what an operator configures: a wait that ties or exceeds the client's own tool timeout converts a successful empty poll into a tool ERROR, and reverse proxies commonly read-timeout at 60s.