package main import ( "bufio" "context" "encoding/json" "errors" "flag" "fmt" "io" "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|manager|set-grant|refresh|submit-refresh|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 "manager": return licenceManager(ctx, args[1:]) case "set-grant": return licenceSetGrant(ctx, args[1:]) case "refresh": return licenceRefresh(ctx, args[1:]) case "submit-refresh": return licenceSubmitRefresh(ctx, args[1:]) case "forget": return licenceForget(ctx, args[1:]) } return fmt.Errorf( "licence %q; it is add, list, use, release, key, manager, set-grant, refresh, "+ "submit-refresh 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 } // licenceManager names the one node that holds a refreshable-grant licence's refresh token readably // and refreshes it centrally (novox/hq ADR 0050). // // **Only a refreshable-grant licence has one.** A static-key licence has no refresh token to hold, so // naming a manager for it is refused where the mistake is made rather than kept as a field that means // nothing — the absent manager is part of what keeps a static key from ever holding a value readably // at rest. func licenceManager(ctx context.Context, args []string) error { if len(args) != 3 { return errors.New("licence manager ") } name, node, module := args[0], args[1], args[2] held, err := openLicences(ctx) if err != nil { return err } defer held.Close() if err := held.SetManager(ctx, name, node, module); err != nil { return err } fmt.Printf("%s on %s holds and refreshes %s.\n"+ " Its refresh token is sealed to %s's key — readable by that node alone, not by any other "+ "node and not by this database. Put %s on the licence too so it is delivered the token:\n"+ " licence use %s %s %s\n", module, node, name, node, module, name, node, module) return nil } // sealedGrantJSON is the wire shape of a sealed refresh token on this command surface: an anonymous // sealed box and the public key it was sealed to, and nothing else. // // **Every field of it is ciphertext or a public key.** `sealed` is the refresh token as a // `crypto_box_seal` to the manager node's public key; `manager_key` is that public key. Neither is // the refresh token in the clear — which is why this surface may read one in (`set-grant`, // `submit-refresh`) without the control plane ever holding a refresh token it could read. The manager // module, on the manager node, seals it; the HOST, on that node, unseals it to deliver cleartext. This // database, and this surface, only ever forward the box (novox/hq ADR 0050). type sealedGrantJSON struct { Sealed string `json:"sealed"` ManagerKey string `json:"manager_key"` } // readSealedGrant reads a sealed refresh token from a file or standard input as JSON. func readSealedGrant(from string) (sealedGrantJSON, error) { var raw []byte var err error if from != "" { raw, err = os.ReadFile(from) } else { raw, err = readAllStdin() } if err != nil { return sealedGrantJSON{}, err } var g sealedGrantJSON if err := json.Unmarshal(raw, &g); err != nil { return sealedGrantJSON{}, fmt.Errorf("the sealed refresh token is not JSON: %w", err) } if g.Sealed == "" || g.ManagerKey == "" { return sealedGrantJSON{}, errors.New( "a sealed refresh token is {sealed, manager_key}, and one part is missing") } return g, nil } func readAllStdin() ([]byte, error) { reader := bufio.NewReader(os.Stdin) return io.ReadAll(reader) } // licenceSetGrant stores a sealed refresh token the manager module produced — adoption, and the // re-seal after a rotation done outside this process (novox/hq ADR 0050). // // **It takes a sealed box, never a refresh token.** The manager module, on the manager node, reads // the operator's refresh token, seals it to that node's own public key, and hands the box here. So the // one moment a refresh token is in the clear is on the manager node, never in the control plane — the // same bound the whole carve-out keeps. This surface refuses anything that is not a complete sealed // grant rather than storing half of one. func licenceSetGrant(ctx context.Context, args []string) error { set := flag.NewFlagSet("licence set-grant", flag.ContinueOnError) from := set.String("file", "", "read the sealed refresh token 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 set-grant [--file ]") } name := positionals[0] grant, err := readSealedGrant(*from) if err != nil { return err } held, err := openLicences(ctx) if err != nil { return err } defer held.Close() if err := held.SetRefreshGrant(ctx, name, grant.Sealed, grant.ManagerKey); err != nil { return err } fmt.Printf("%s now holds a refresh token for %s, sealed to that node's key and readable by it "+ "alone.\n the control plane stored the box without opening it; run `push` to deliver it\n", "the manager", name) return nil } // licenceSubmitRefresh publishes a refresh a MANAGER NODE already performed: the new access token is // sealed to every holder, and a rotated refresh token replaces the stored envelope (novox/hq ADR // 0050, Phase C). // // **This is the boundary the invariant rests on.** The manager runtime, on the manager node, opened // the at-rest envelope with that node's key, called the vendor's OAuth endpoint, and produced this: // the new access token in the clear, and — only if the vendor rotated it — the refresh token already // re-sealed at rest. This reads exactly those two things and no refresh token in the clear ever // reaches it, because it is never given one. The access token is sealed per holder and discarded, // as any accepted key is; the rotated envelope is stored opaque. func licenceSubmitRefresh(ctx context.Context, args []string) error { set := flag.NewFlagSet("licence submit-refresh", flag.ContinueOnError) accessFrom := set.String("access-file", "", "read the new access token from a file instead of standard input") grantFrom := set.String("grant-file", "", "the rotated sealed refresh token, if the vendor rotated it; omit if it did not") positionals, err := parseAround(set, args) if err != nil { return err } if len(positionals) != 1 { return errors.New( "licence submit-refresh [--access-file ] [--grant-file ]") } name := positionals[0] var accessToken string if *accessFrom != "" { raw, err := os.ReadFile(*accessFrom) if err != nil { return err } accessToken = strings.TrimSpace(string(raw)) } else { raw, err := readAllStdin() if err != nil { return err } accessToken = strings.TrimSpace(string(raw)) } if accessToken == "" { return errors.New("no access token was given, so there is nothing to seal") } // The rotated sealed token is optional: absent, the stored refresh token is left exactly as it was. var newSealed, newManagerKey string if *grantFrom != "" { grant, err := readSealedGrant(*grantFrom) if err != nil { return err } newSealed, newManagerKey = grant.Sealed, grant.ManagerKey } open, err := openStores(ctx) if err != nil { return err } defer open.Close() held, err := open.Licences(ctx) if err != nil { return err } inv := open.inventory sealed, err := held.SubmitRefresh(ctx, name, accessToken, newSealed, newManagerKey, func(node string) (string, error) { return inv.SealingKeyOf(ctx, node) }) if err != nil { return err } rotatedNote := "the refresh token was left with its manager unchanged" if newSealed != "" { rotatedNote = "the rotated refresh token replaced the stored box, still readable by the " + "manager node alone" } fmt.Printf("submitted a refresh for %s: a new access token sealed to %d holder(s), and %s.\n"+ " run `push` to deliver it\n", name, sealed, rotatedNote) return nil } // licenceRefresh mints a new access token for a refreshable-grant licence and seals it to every // holder (novox/hq ADR 0050). The refresh token stays with the manager and is never delivered. // // The vendor's actual refresh is a plug-in this build does not ship (Phase C), so here this reports // that plainly rather than pretending to have refreshed. func licenceRefresh(ctx context.Context, args []string) error { if len(args) != 1 { return errors.New("licence refresh ") } name := args[0] open, err := openStores(ctx) if err != nil { return err } defer open.Close() held, err := open.Licences(ctx) if err != nil { return err } inv := open.inventory sealed, err := held.Refresh(ctx, name, func(node string) (string, error) { return inv.SealingKeyOf(ctx, node) }) if err != nil { return err } fmt.Printf("refreshed %s: a new access token sealed to %d holder(s), and the refresh token left "+ "with its manager.\n run `push` to deliver it\n", name, sealed) 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 }