package broker import ( "regexp" "strings" ) // A tool call names its caller (novox/hq issue 365, by ADR 0259 §3's precedent for asks). // // mesh.mod..call.[.]. // mesh.seat..call.[.]. // // The last token is the bus user that published the call, and each user is granted these subjects with its own // name there and no other — the caller is a fact the server enforces, and the tool runtime hands it to the // module from the subject a call arrived on (mesh-tools node-tools internal/bus caller.go holds the same shape). // // **A kind of its own, `call`, not the `tool` subject with a token appended.** Every grant to call a tool is a // wildcard over the `tool` kind — `.tool..*` for the instance on any machine, `mesh.mod.*.tool.>` for every // tool — and either would match a `tool` subject with any caller appended, so a caller could name another. // Under `call` nothing is granted but the caller-named subjects. No stream's filter covers it: a tool call is // never persisted (design 25 §3). // // **Derived from the `tool` grants, in one place** (callerNamed): wherever a principal may call or answer a // tool, it may call it in its own name, or answer it naming any caller — so no kind of principal is left // unable to call in its own name, and a new grant to call is one in both shapes by construction. // // The `tool` grants stay beside these for one release, so every caller and every runtime moves without a gap: // their retirement is hq issue 464. const CallKind = "call" var userName = regexp.MustCompile(`^[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)*$`) // CallerToken is a bus user's name as the last token of a call it publishes: each dot written `~`, which no part // of a user's name may hold (safeSubject), so the token names that user and no other. "" for a name that is not // a user's. func CallerToken(user string) string { if !userName.MatchString(user) { return "" } return strings.ReplaceAll(user, ".", "~") } // toolGrant splits a grant on the `tool` kind — `mesh.mod..tool.` or `mesh.seat..tool.`, any // part possibly a wildcard — at the kind; ok is false for any other subject. func toolGrant(subject string) (head, rest string, ok bool) { parts := strings.SplitN(subject, ".", 5) if len(parts) != 5 || parts[0] != "mesh" || (parts[1] != "mod" && parts[1] != "seat") || parts[3] != "tool" || parts[2] == "" || parts[4] == "" { return "", "", false } return parts[0] + "." + parts[1] + "." + parts[2], parts[4], true } // CalledSubject is where a call to a tool subject goes naming its caller; "" when the subject is not a tool's // or the user is not a bus user. func CalledSubject(subject, user string) string { head, rest, ok := toolGrant(subject) token := CallerToken(user) if !ok || token == "" || strings.ContainsAny(rest, "*>") { return "" } return head + "." + CallKind + "." + rest + "." + token } // CalledPattern is what a holder answering a tool subject also subscribes, to hear the calls that name their // caller: the same address under the `call` kind, any caller last. "" for a subject that is not a tool's. func CalledPattern(subject string) string { head, rest, ok := toolGrant(subject) if !ok || strings.ContainsAny(rest, "*>") { return "" } return head + "." + CallKind + "." + rest + ".*" } // calledPublish is a grant to call on the `tool` kind, as the same grant in the caller's own name: its last // token the caller's, and nothing that reaches past it. A `>` is every tail a call carries — the tool alone or // the tool and the machine — so it becomes both, each ending in the caller. func calledPublish(grant, token string) []string { head, rest, ok := toolGrant(grant) if !ok || token == "" { return nil } base := head + "." + CallKind + "." switch { case rest == ">": return []string{base + "*." + token, base + "*.*." + token} case strings.HasSuffix(rest, ".>"): return []string{base + strings.TrimSuffix(rest, ">") + "*." + token} } return []string{base + rest + "." + token} } // calledSubscribe is a grant to answer on the `tool` kind, as the same grant for the calls naming any caller. func calledSubscribe(grant string) []string { head, rest, ok := toolGrant(grant) if !ok { return nil } base := head + "." + CallKind + "." if strings.HasSuffix(rest, ">") { return []string{base + rest} } return []string{base + rest + ".*"} } // callerNamed adds, beside a principal's grants on the `tool` kind, the same grants under `call`: to publish in // its own name, to subscribe naming anybody. func callerNamed(user string, pub, sub []string) ([]string, []string) { token := CallerToken(user) for _, g := range pub { pub = append(pub, calledPublish(g, token)...) } for _, g := range sub { sub = append(sub, calledSubscribe(g)...) } return unique(pub), unique(sub) }