package catalogue import ( "fmt" "strings" ) // Seats a module declares of its own (novox/hq ADR 0118). // // The set of seats a mesh has is **derived**: the mesh's own, in seats.go, plus those declared by // every module it has registered. Still closed — a seat named nowhere is refused — but computed // from the catalogue rather than written in the controller, which is what ADR 0110 actually // needed and a hand-maintained table could not keep. Its own evidence: the enumeration done by // hand while that record was written reported eleven claims where there were thirteen. // // **What can be checked from one manifest and what cannot.** A declaration's shape, its scope, // and the reserved prefix are facts about the manifest in front of you. Whether a seat anybody // names actually exists, whether two modules declared the same one, and whether a holder // satisfies the protocol are facts about the *catalogue* — so they are checked at registration, // by CatalogueProblems, which is the last moment the mesh can still say no. // A SeatDeclaration is a role a module offers on the bus: what may be sent to it, what it says, // and what it answers. A caller declares that it uses the *seat*, never the module, so the // implementation can be replaced under it. type SeatDeclaration struct { Name string `json:"name"` Scope string `json:"scope,omitempty"` // Accepts are the verbs others may submit work on. Each becomes a work-queue subject, and // the holder is the only consumer — so exactly one worker does the job, by construction // rather than by how carefully somebody wrote a subscribe call. Accepts []string `json:"accepts,omitempty"` // Emits are the verbs the holder publishes: 1:many, nobody obliged to act. Emits []string `json:"emits,omitempty"` // Serves are the verbs the holder answers: request and reply, awaited. A bare name, or the // verb in full with its schema (novox/hq ADR 0132). Serves []Verb `json:"serves,omitempty"` // RetainSeconds is how long the inbound backlog survives with no holder, zero for the // mesh's default. Retention belongs to whoever owns the namespace (design 29 §3) — a seat // owns its own, which is why a seat is also the answer for a module that needs retention // its events cannot have. RetainSeconds int `json:"retain-seconds,omitempty"` // Kinded makes the seat a kinded bench (novox/hq ADR 0234 §2, ADR 0259 §3): its holders are different // modules, each claiming one kind, and each verb's subject carries the kind. Only the benches in // KindedBenches may be kinded; making another is a decision, recorded. Kinded bool `json:"kinded,omitempty"` // ByCaller are accepts and emits whose subject's last token names the calling module (ADR 0259 §3): a // user submits such an accept, and hears such an event, under its own name and no other. ByCaller []string `json:"by-caller,omitempty"` // Proofs are verbs carried as core request and reply, never on a stream: what travels on them (a code // the operator typed) is never kept (ADR 0259 §3). On a kinded bench a holder asks with its own kind and // the modules watching the seat answer. Proofs []string `json:"proofs,omitempty"` // Records are state buckets of the declaring module that each user reads under its own name — the // keys `.…` and no other (ADR 0259 §3). Records []string `json:"records,omitempty"` // Paths are the paths on its machine the seat's holder owns (novox/hq ADR 0287), under the rules the // mesh's own seats name theirs by: a node seat only, each absolute and literal, and none another seat // names. A module's seat never steps back. Paths []string `json:"paths,omitempty"` // Needs is every node seat this seat's holder needs held on its own node (novox/hq ADR 0220, ADR // 0378 §8): a role whose holder is only right while another role is filled beside it. A module // claiming this seat depends on each, exactly as a module declaring a service depends on the // service manager (ADR 0207) — derived from the claim, never written again in the claimant's own // manifest. Only a node seat may carry needs, and a compiled seat carries none. Needs []string `json:"needs,omitempty"` } // KindedBenches are the seats that may be kinded (novox/hq ADR 0234 §2): `channel` sends to the operator, // `intake` takes what the operator answers. Another is a decision, recorded, as ADR 0223 asks of a bench. var KindedBenches = map[string]bool{"channel": true, "intake": true} // NamedByCaller says whether one of the seat's verbs is named by its caller. func (s SeatDeclaration) NamedByCaller(verb string) bool { for _, v := range s.ByCaller { if v == verb { return true } } return false } // At is this declaration's scope, with the default applied. Mesh by default, because a seat // declared by a module is nearly always "there is one of these in the mesh" — a per-node worker // is the deliberate case, and says so. func (s SeatDeclaration) At() string { if s.Scope == "" { return ScopeMesh } return s.Scope } // A Shelf is every manifest the mesh has registered, by module name. type Shelf map[string]Manifest // CatalogueProblems are the rules no single manifest can be judged against. // // Run at registration, which is the last moment the mesh can still refuse: after it, a caller is // bound to a seat and a refusal is an outage rather than a conversation. // UndeclaredSeat ends the problem of a `uses` or a claim naming a seat no manifest given declares: over the whole // catalogue (registration, the catalogue's own check) a refusal, and over some manifests alone a seat whose // declaring module was not given (novox/hq issue 364). const UndeclaredSeat = "which no module declares and the mesh does not define" // IsUndeclaredSeat says whether a problem CatalogueProblems gave is a seat no manifest given declares. func IsUndeclaredSeat(problem string) bool { return strings.HasSuffix(problem, UndeclaredSeat) || strings.HasSuffix(problem, UnheldSeat) } // ChannelCapabilities is the fixed vocabulary `channel-capabilities/1` (novox/hq ADR 0234 §2): a word // outside it is refused. `max-length:` takes a number. var ChannelCapabilities = map[string]bool{ "deliver": true, "reaches-away": true, "loud": true, "silent": true, "edit": true, "reaches-when-mesh-down": true, "private": true, "choice": true, "reply": true, "threads": true, "operator-first": true, "verified-sender": true, "exact-render": true, "code-factor": true, "key-factor": true, } // TrustedHolding is why a module must run as its own account (novox/hq ADR 0259 §8), or "": it holds a seat // whose events it says to one caller each (a warrant), or speaks for a kind of a kinded bench that proves // its sender, or is private (a link's code is shown there). None may be carried by the machine's runtime, which // runs as the operator's account. func TrustedHolding(m Manifest, declared map[string]SeatDeclaration) string { for _, c := range m.Claims { s, ok := declared[c.Name] if !ok { continue } for _, e := range s.Emits { if s.NamedByCaller(e) { return fmt.Sprintf("it holds %s, whose %s it says to one caller each", c.Name, e) } } if s.Kinded { for _, capability := range c.Capabilities { switch capability { case "verified-sender": return fmt.Sprintf("it holds %s of kind %s, which proves its sender", c.Name, c.Kind) case "private": // A private kind is shown a link's code, which makes an account the operator's. return fmt.Sprintf("it holds %s of kind %s, which is private: a link's code is shown there", c.Name, c.Kind) } } } } return "" }