status --json, so a board has something to read
A board reads through interfaces and holds nothing. Everything it needs is already answered — as text, for people, which is not something a page can read. `--json` rather than a serving API, because nothing needs one yet: whatever serves a board runs the command, and the constraint holds either way — the board never touches a context's store. An API is the larger thing and should wait until something asks for it. Both forms are gathered from the same reads before either says anything, so they answer the same questions rather than being two implementations that can drift. That was not true of the first version: the JSON printed after the text, because the branch was too late. Four properties, each asserted and each confirmed to fail when removed: - refused and failed stay distinct all the way out. They are fixed in different places, so one word for both sends half a page's readers to the wrong one — and how much DID apply is carried, since "three of eight" and "none of eight" are different machines - a machine that never spoke carries no time at all, rather than a zero one that any page would format as a date in 1970 - nothing is null. A page distinguishing "no machines are wrong" from "this field is missing" has to handle both, and null is the one that gets forgotten - no field is named like a secret. Everything here comes from records that hold no readable one, but a shape a page is built against is exactly where one would eventually be added for convenience
This commit is contained in:
@@ -180,3 +180,40 @@ func (i *Inventory) SecretForModule(ctx context.Context, node, module, name stri
|
||||
}
|
||||
return made.ForConsumer, nil
|
||||
}
|
||||
|
||||
// AcceptSecretForModule keeps a value somebody supplied as a module's own secret.
|
||||
//
|
||||
// The counterpart to SecretForModule, which generates one. Some of what a module needs the mesh
|
||||
// cannot invent: a broker account exists because the broker was told about it, and the password is
|
||||
// whatever was agreed with the broker at that moment. The mesh's job is to carry it to the machine
|
||||
// that will use it without being able to read it afterwards.
|
||||
//
|
||||
// Sealed on the way in and the plaintext discarded, exactly as a generated one is — so the only
|
||||
// difference between the two is where the value came from.
|
||||
func (i *Inventory) AcceptSecretForModule(ctx context.Context, node, module, name, value string) error {
|
||||
key, err := i.SealingKeyOf(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if key == "" {
|
||||
return fmt.Errorf(
|
||||
"%s has no sealing key, so nothing can be sealed to it — it joins again to get one",
|
||||
node)
|
||||
}
|
||||
record, err := i.NodeByName(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sealed, err := secrets.Accept(value, key, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = i.store.Pool().Exec(ctx,
|
||||
`insert into module_secret (node, module, name, sealed, node_key)
|
||||
values ($1, $2, $3, $4, $5)
|
||||
on conflict (node, module, name) do update set
|
||||
sealed = excluded.sealed, node_key = excluded.node_key, made_at = now()`,
|
||||
record.ID, module, name, sealed.ForConsumer, key)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user