package inventory import ( "context" "encoding/json" "fmt" "time" ) // CoreBox is a core module's own credential as the vault sealed it to its machine (novox/hq ADR 0349 ยง16, issue 755): // the vault's generation, the machine's sealing key it was sealed to, and the box. Never a value: the controller // cannot open it. type CoreBox struct { Module string Name string Generation int SealedTo string Box json.RawMessage GotAt time.Time } // KeptCoreBoxes is every box kept for a machine, by module and name. func (i *Inventory) KeptCoreBoxes(ctx context.Context, node string) (map[string]map[string]CoreBox, error) { rows, err := i.store.Pool().Query(ctx, `select b.module, b.name, b.generation, b.sealed_to, b.box, b.got_at from core_box b join node n on n.id = b.node where n.name = $1`, node) if err != nil { return nil, err } defer rows.Close() out := map[string]map[string]CoreBox{} for rows.Next() { var b CoreBox var box string if err := rows.Scan(&b.Module, &b.Name, &b.Generation, &b.SealedTo, &box, &b.GotAt); err != nil { return nil, err } b.Box = json.RawMessage(box) if out[b.Module] == nil { out[b.Module] = map[string]CoreBox{} } out[b.Module][b.Name] = b } return out, rows.Err() } // KeepCoreBox keeps the box the vault answered for one of a machine's core credentials, over the one kept before. func (i *Inventory) KeepCoreBox(ctx context.Context, node string, b CoreBox) error { if b.Generation <= 0 || b.SealedTo == "" || len(b.Box) == 0 || !json.Valid(b.Box) { return fmt.Errorf("the vault's box for %s/%s on %s is not whole: nothing kept", b.Module, b.Name, node) } tag, err := i.store.Pool().Exec(ctx, `insert into core_box (node, module, name, generation, sealed_to, box) select n.id, $2, $3, $4, $5, $6 from node n where n.name = $1 on conflict (node, module, name) do update set generation = excluded.generation, sealed_to = excluded.sealed_to, box = excluded.box, got_at = now()`, node, b.Module, b.Name, b.Generation, b.SealedTo, string(b.Box)) if err != nil { return err } if tag.RowsAffected() == 0 { return fmt.Errorf("%w: %s", ErrNoSuchNode, node) } return nil }