// Package hub implements the project side of the Hub contract // (central_ai_llm_master/docs/ask-contract.md) for the Internet Use Control // Control Service. // // The contract is one-directional by design: the Hub asks a project about its // own data and the project answers. A project never learns the Hub's data model // and the Hub never learns the project's. // // GET /capabilities -> the topics this project can answer (Hub routing) // POST /ask -> a short answer about this project's data // POST /notify -> accept a notification and deliver it to Sam // // /ask and /capabilities are read-only. /ask answers from SQLite and never // writes, so the "read only" promise (AGENTS.md §10 rule 9) is a testable // property, not a comment (TestAskWritesNothing). package hub // ProjectName is the value the Hub matches in routing.md. const ProjectName = "internet_use_control" // Capabilities is the GET /capabilities reply. The Hub reads it to route a // question to this project. Keep the topic list short and plain; it holds no // private data. type Capabilities struct { Project string `json:"project"` Topics []string `json:"topics"` People []string `json:"people"` } // DefaultCapabilities returns the topics this service can answer. They match // the screen-time domain in AGENTS.md §1 and §4. func DefaultCapabilities() Capabilities { return Capabilities{ Project: ProjectName, Topics: []string{"internet", "screen time", "screens", "usage", "devices", "blocking", "kids"}, People: []string{"harry", "finn"}, } } // AskRequest is the POST /ask body (ask-contract.md). type AskRequest struct { Question string `json:"question"` Asker string `json:"asker"` Person string `json:"person,omitempty"` Source string `json:"source,omitempty"` } // AskResponse is the POST /ask reply. A nil Answer means the project cannot // answer and Reason explains why. The shape is frozen by the contract. type AskResponse struct { Answer *string `json:"answer"` Data map[string]any `json:"data,omitempty"` Expires string `json:"expires,omitempty"` Reason string `json:"reason,omitempty"` } // NotifyRequest is the POST /notify body (ask-contract.md, "Notify"). The // contract defines it as project-to-Hub; this service accepts the same shape so // a caller (Hub, admin app, or a future phone tamper report) can have Sam // notified without inventing a second shape. type NotifyRequest struct { From string `json:"from"` Level string `json:"level"` Message string `json:"message"` Person string `json:"person,omitempty"` } // answerText is a small helper so pointer handling stays in one place. func answerText(s string) *string { return &s } // cannotAnswer builds the contract's "cannot answer" reply. func cannotAnswer(reason string) AskResponse { return AskResponse{Answer: nil, Reason: reason} }