The controller ended (ADR 0590): the grants issued are the grants part's alone, so its grant value is Grant and GrantSource goes; a node trusts the vault's key alone, follows the grants part's user list alone and composes its own or none; the controller's composed and builds bucket names go. nox calls seat verbs (ADR 0590 clause 7), so the cli package that spoke to the node-engine's socket goes, and mesh-hand-compose, which wrote the controller's compositions. A have an engine from before wrote, composes controller, still reads. Issue 1126 B4a, B6. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
96 lines
3.5 KiB
Go
96 lines
3.5 KiB
Go
package delivery
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// **The grants issued** (novox/hq ADR 0528 clause 2, ADR 0429 §6 rule 6, ADR 0547 §4, §5, ADR 0590): when the grants
|
|
// part has issued a module's bus credentials for a fingerprint, it writes `<module>.<fingerprint hex>` in its state
|
|
// `grants` (PartGrantsBucket). The assigner reads it: a node's want moves to a fingerprint only once its grant exists.
|
|
// Only the grants part writes the bucket.
|
|
const (
|
|
// PartGrantsState is the grants part's state of issued credentials by fingerprint, as its manifest names it.
|
|
PartGrantsState = "grants"
|
|
// PartGrantsBucket is that state's bucket, `<owner module>_<name>`.
|
|
PartGrantsBucket = "mesh-grants_" + PartGrantsState
|
|
// PartGrantsRead is that state as a reader names it in `reads`.
|
|
PartGrantsRead = "mesh-grants." + PartGrantsState
|
|
// PartGrantsHistory is the revisions the bucket keeps per key.
|
|
PartGrantsHistory = 5
|
|
)
|
|
|
|
// PartGrantKey is the key, in PartGrantsBucket, of a module's credential issued for a fingerprint: `<module>.<hex>`,
|
|
// the hex being the fingerprint's key part (FingerprintKeyPart).
|
|
func PartGrantKey(module, fingerprint string) (string, error) {
|
|
return BuilderBuildKey(module, fingerprint)
|
|
}
|
|
|
|
// ParsePartGrantKey is the inverse of PartGrantKey; it refuses a want or held key, naming it.
|
|
func ParsePartGrantKey(key string) (module, fingerprint string, err error) {
|
|
if module, fingerprint, err = ParseBuilderBuildKey(key); err != nil {
|
|
return "", "", fmt.Errorf("delivery: %q is not a grant key, <module>.<fingerprint hex>: %w", key, err)
|
|
}
|
|
return module, fingerprint, nil
|
|
}
|
|
|
|
// Grant is the value of a grant key: the bus accounts (their public keys) the module's processes were issued
|
|
// for that fingerprint, none for a module whose grants the tool runner's account carries, so a reissue for another
|
|
// account is a new revision. The entry's own time says when it was issued.
|
|
type Grant struct {
|
|
V int `json:"v"`
|
|
Accounts []string `json:"accounts,omitempty"`
|
|
}
|
|
|
|
func (g Grant) check() error {
|
|
for _, a := range g.Accounts {
|
|
if strings.TrimSpace(a) == "" || strings.ContainsAny(a, " \t\n") {
|
|
return fmt.Errorf("delivery: a grant names an empty or spaced account %q", a)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EncodeGrant is g as JSON at Version; it refuses an empty or spaced account.
|
|
func EncodeGrant(g Grant) ([]byte, error) {
|
|
if err := g.check(); err != nil {
|
|
return nil, err
|
|
}
|
|
g.V = Version
|
|
return json.Marshal(g)
|
|
}
|
|
|
|
// DecodeGrant reads a grant; it refuses an unknown version (ErrUnknownVersion) and an empty or spaced account.
|
|
func DecodeGrant(data []byte) (Grant, error) {
|
|
var g Grant
|
|
if err := decode(data, &g); err != nil {
|
|
return Grant{}, err
|
|
}
|
|
if err := g.check(); err != nil {
|
|
return Grant{}, err
|
|
}
|
|
return g, nil
|
|
}
|
|
|
|
// SameGrant says whether two grants are the same value.
|
|
func SameGrant(a, b Grant) bool { return reflect.DeepEqual(a, b) }
|
|
|
|
// NamesNode says whether the grant names module's accounts on node: its own `<node>.<module>` or a process's
|
|
// `<node>.<module>.<process>`. A grant that names no account (a module that does not speak on the bus) names every
|
|
// node. The assigner wants a node only once the grant names its account (issue 1945), and a put waits for it (ADR
|
|
// 0546 clause 4).
|
|
func (g Grant) NamesNode(node, module string) bool {
|
|
if len(g.Accounts) == 0 {
|
|
return true
|
|
}
|
|
own := node + "." + module
|
|
for _, a := range g.Accounts {
|
|
if a == own || strings.HasPrefix(a, own+".") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|