package main import ( "bufio" "context" "encoding/json" "errors" "flag" "fmt" "os" "strings" ) // licenceCommand is everything about model access the mesh holds. // // **A licence is a named thing and the name is the operator's** (novox/hq ADR 0024). *The personal // account*, *the organisation's account* — those are names a person uses, and the mesh has to use // them too, because the whole point is saying which one a given consumer uses. func licenceCommand(ctx context.Context, args []string) error { if len(args) == 0 { return errors.New("licence add|list|use|release|key|forget") } switch args[0] { case "add": return licenceAdd(ctx, args[1:]) case "list": return licenceList(ctx) case "use": return licenceUse(ctx, args[1:], true) case "release": return licenceUse(ctx, args[1:], false) case "key": return licenceKey(ctx, args[1:]) case "forget": return licenceForget(ctx, args[1:]) } return fmt.Errorf("licence %q; it is add, list, use, release, key or forget", args[0]) } func licenceAdd(ctx context.Context, args []string) error { set := flag.NewFlagSet("licence add", flag.ContinueOnError) // What a consumer must know that is not secret — a base URL, a model name. Never the key. serves := set.String("serves", "", "JSON a consumer must know that is not secret, such as a base URL or a model") positionals, err := parseAround(set, args) if err != nil { return err } if len(positionals) != 2 { return errors.New(`licence add [--serves '{"model":"..."}']`) } vendor, name := positionals[0], positionals[1] values := map[string]any{} if strings.TrimSpace(*serves) != "" { if err := json.Unmarshal([]byte(*serves), &values); err != nil { return fmt.Errorf("--serves is not JSON: %w", err) } } held, err := openLicences(ctx) if err != nil { return err } defer held.Close() if err := held.Add(ctx, name, vendor, values); err != nil { return err } fmt.Printf("%s (%s) recorded. Nothing uses it yet, and it has no key:\n"+ " licence use %s \n licence key %s\n", name, vendor, name, name) return nil } func licenceList(ctx context.Context) error { held, err := openLicences(ctx) if err != nil { return err } defer held.Close() all, err := held.All(ctx) if err != nil { return err } if len(all) == 0 { // Said, not printed as nothing: an empty list and a failed read must never look the same. fmt.Println("this mesh holds no licences") return nil } for _, one := range all { holders, err := held.HoldersOf(ctx, one.Name) if err != nil { return err } fmt.Printf("%s (%s)\n", one.Name, one.Vendor) if len(holders) == 0 { fmt.Printf(" nobody uses it\n") } for _, h := range holders { // Whether it has a key is the question somebody is actually asking, so it is said // per holder rather than per licence: the key was sealed to the holders that existed // when it was supplied, and one recorded afterwards has none. state := "has no key — supply it again with `licence key " + one.Name + "`" if h.Sealed != "" { state = "has a key" } fmt.Printf(" %s on %s: %s\n", h.Module, h.Node, state) } } return nil } func licenceUse(ctx context.Context, args []string, using bool) error { verb := "use" if !using { verb = "release" } if len(args) != 3 { return fmt.Errorf("licence %s ", verb) } name, node, module := args[0], args[1], args[2] held, err := openLicences(ctx) if err != nil { return err } defer held.Close() if !using { if err := held.StopUsing(ctx, name, node, module); err != nil { return err } fmt.Printf("%s on %s no longer uses %s. Its copy of the key goes on the next push\n", module, node, name) return nil } if err := held.Use(ctx, name, node, module); err != nil { return err } fmt.Printf("%s on %s uses %s.\n", module, node, name) // The consequence, said now rather than discovered as a machine that resolves and receives // nothing: the mesh discarded the plaintext, so a holder added after the key was supplied has // no key and the mesh cannot make one. sealed, err := held.KeyFor(ctx, name, node, module) if err != nil { return err } if sealed == "" { fmt.Printf(" It has no key yet — the mesh discarded the plaintext when it was supplied "+ "and cannot seal another. Supply it again:\n licence key %s\n", name) } return nil } // licenceKey is the *accept* verb novox/hq ADR 0024 names as missing. // // Take a value, seal it to each holder, and discard the plaintext. Every other credential the // mesh handles it generated itself; an API key arrives from a person, and a mesh that kept // operator-supplied keys readably is the arrangement this project measured and rejected. func licenceKey(ctx context.Context, args []string) error { set := flag.NewFlagSet("licence key", flag.ContinueOnError) // A file rather than an argument, by default. A key on a command line is a key in shell // history and in every process listing taken while it ran. from := set.String("file", "", "read the key from a file instead of standard input") positionals, err := parseAround(set, args) if err != nil { return err } if len(positionals) != 1 { return errors.New("licence key [--file ]") } name := positionals[0] var value string if *from != "" { raw, err := os.ReadFile(*from) if err != nil { return err } value = strings.TrimSpace(string(raw)) } else { fmt.Fprintln(os.Stderr, "reading the key from standard input; it is not echoed anywhere") reader := bufio.NewReader(os.Stdin) line, err := reader.ReadString('\n') if err != nil && line == "" { return fmt.Errorf("nothing was given on standard input: %w", err) } value = strings.TrimSpace(line) } held, err := openLicences(ctx) if err != nil { return err } defer held.Close() open, err := openStores(ctx) if err != nil { return err } defer open.Close() inv := open.inventory sealed, err := held.Accept(ctx, name, value, func(node string) (string, error) { return inv.SealingKeyOf(ctx, node) }) if err != nil { if sealed > 0 { // Some holders got it and some did not, and the person holding the key is the only // one who can finish the job. Saying how far it got is the difference between running // this again knowing what it will do and running it hoping. return fmt.Errorf( "%w\n\n%d holder(s) were sealed before this. Running `licence key %s` again "+ "with the same key seals the rest and changes nothing for those already done", err, sealed, name) } return err } // Not echoed back, ever. What is stored is unreadable by whoever holds it, the mesh included, // and printing the value here would put the one copy that matters on a terminal. fmt.Printf("sealed to %d holder(s). The mesh has discarded the key and cannot read it back\n", sealed) fmt.Printf(" run `push` to deliver it\n") return nil } func licenceForget(ctx context.Context, args []string) error { if len(args) != 1 { return errors.New("licence forget ") } held, err := openLicences(ctx) if err != nil { return err } defer held.Close() if err := held.Forget(ctx, args[0]); err != nil { return err } // Said plainly, because the mesh cannot do it and pretending otherwise is worse than useless: // a licence outliving its holder is a live credential nobody is watching. fmt.Printf("%s is forgotten, and every record of who held it with it.\n"+ " The key itself is not the mesh's to revoke — do that where the licence was bought\n", args[0]) return nil }