Prove the vault's secret-ask test and the issuing provider's test with a stand-in provider (issue 675)
mesh/merge-gate head eb958f668ecd merged into trunk 1a2c917abfdb — pass: [release feat/57-config-and-provisioning, judged by its controller fcdecb436117] t…
mesh/repo-check head eb958f668ecd merged into trunk 1a2c917abfdb — fail: [release feat/57-config-and-provisioning, judged by its controller fcdecb436117] i…
mesh/delivery-group group pkg/57-v2-vault-verbs rejected: a member's own check failed
mesh/delivery stopped: the pull request closed unmerged

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 19:14:34 +00:00
co-authored by Claude Opus 5.5
parent 63b7aec065
commit eb958f668e
@@ -0,0 +1,495 @@
package main
// ADR 0349's tests for what the vault takes in (design 57's V2, issue 675): the vault's `secret-ask` test, the
// issuing provider's test with a stand-in provider (plex's own tool moves with the media catalogue, after
// release 57), an accepted value opened in the vault alone and never remade, and rotate.
//
// In each, "the controller" is a test function that holds no private key: it only carries what the desk or the
// provider sealed to the key the vault made for the call, as the controller does on the bus.
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/seal"
"golang.org/x/crypto/nacl/box"
)
// sealToCall seals value to a call's key as the desk's prompt and a provider's tool do: the NaCl anonymous box.
func sealToCall(t *testing.T, sealTo, value string) string {
t.Helper()
raw, err := base64.StdEncoding.DecodeString(sealTo)
if err != nil || len(raw) != 32 {
t.Fatalf("seal_to %q is not a key", sealTo)
}
var key [32]byte
copy(key[:], raw)
sealed, err := box.SealAnonymous(nil, []byte(value), &key, rand.Reader)
if err != nil {
t.Fatal(err)
}
return base64.StdEncoding.EncodeToString(sealed)
}
// handedTo opens what `get` hands an allowed asker: the value the vault holds.
func handedTo(t *testing.T, v testVault, asker, name string) string {
t.Helper()
key, _ := seal.GenerateKeyPair()
a, err := v.Get(asker, name, key.PublicKey)
if err != nil {
t.Fatal(err)
}
value, err := seal.Open(a.Sealed, key.PrivateKey)
if err != nil {
t.Fatal(err)
}
return value
}
// everything is all the vault said and wrote during a test, as text.
func everything(t *testing.T, v testVault, more ...any) string {
t.Helper()
var b strings.Builder
for _, said := range v.bus.raw {
b.WriteString(said)
}
for _, content := range filesUnder(t, v.dir) {
b.WriteString(content)
}
for _, m := range more {
raw, _ := json.Marshal(m)
b.Write(raw)
}
return b.String()
}
const typedAtTheDesk = "123456789:AAEhBP0av28P4XFQnIuR-o-7Xnz1kkUzW3g"
func telegramAsk(by string) AskOf {
return AskOf{Node: "anchor", Module: "telegram", Name: "telegram-token", Desk: "laptop", AskedBy: by}
}
// ---- the vault's secret-ask test (ADR 0349 "How it is checked") ---------------------------------------------
// An ask is recorded in the vault's store before the prompt opens; its prompt names the asker as the bus named
// it; the typed value opens only in the vault; the controller carries ciphertext only.
func TestAnAskIsRecordedBeforeThePromptAndTheTypedValueOpensOnlyInTheVault(t *testing.T) {
v := newVault(t, AllowList{"anchor.node-provisioner": {"own/anchor/telegram/telegram-token"}})
opened, err := v.AskOpen(ControllerCaller, telegramAsk("g14/claude-code, via the console"))
if err != nil {
t.Fatal(err)
}
// Recorded before any prompt opens: the record holds the ask now, with no end.
asks, _ := v.Store.Asks(10)
if len(asks) != 1 || asks[0].Event != AskOpened || asks[0].ID != opened.Call || asks[0].AskedBy != "g14/claude-code" ||
asks[0].Caller != ControllerCaller || asks[0].Desk != "laptop" || asks[0].Secret != "own/anchor/telegram/telegram-token" {
t.Fatalf("the record before the prompt: %+v", asks)
}
// The prompt names the asker as the bus named it, cut to a name: what the vault answers is what it recorded.
if opened.AskedBy != "g14/claude-code" {
t.Errorf("the prompt would name %q", opened.AskedBy)
}
// The desk seals what is typed to the call's key; the controller passes it on.
changed, err := v.AskEnd(ControllerCaller, opened.Call, sealToCall(t, opened.SealTo, typedAtTheDesk+"\n"), "")
if err != nil {
t.Fatal(err)
}
if changed.Generation != 1 || changed.By != "g14/claude-code" {
t.Errorf("kept as %+v", changed)
}
if got := handedTo(t, v, "anchor.node-provisioner", "own/anchor/telegram/telegram-token"); got != typedAtTheDesk {
t.Errorf("the vault holds %q, not what was typed with its line ending taken off", got)
}
rec, _, _ := v.Store.Read("own/anchor/telegram/telegram-token")
if rec.Origin != OriginTyped || rec.ChangedBy != "g14/claude-code" || rec.ChangedVia != ControllerCaller {
t.Errorf("the record says origin %q, by %q, via %q", rec.Origin, rec.ChangedBy, rec.ChangedVia)
}
asks, _ = v.Store.Asks(10)
if len(asks) != 2 || asks[0].Event != AskEnded || asks[0].Outcome != "given" || asks[0].ID != opened.Call {
t.Errorf("how it ended: %+v", asks)
}
if strings.Contains(everything(t, v, opened, changed, asks), typedAtTheDesk) {
t.Fatal("the typed value is in something the vault said, answered or wrote in the clear")
}
}
// One open ask per secret, and three an hour (ADR 0277 §24), read from the vault's own record.
func TestAnAskIsBoundedToOneOpenPerSecretAndThreeAnHour(t *testing.T) {
v := newVault(t, nil)
first, err := v.AskOpen(ControllerCaller, telegramAsk("g14.claude-code"))
if err != nil {
t.Fatal(err)
}
if _, err := v.AskOpen(ControllerCaller, telegramAsk("ace.claude-code")); err == nil ||
!strings.Contains(err.Error(), "still open (asked by g14.claude-code)") {
t.Fatalf("a second ask while one is open: %v", err)
}
// Another secret is not held back by it.
other := telegramAsk("ace.claude-code")
other.Name = "webhook-secret"
if _, err := v.AskOpen(ControllerCaller, other); err != nil {
t.Fatalf("another secret's ask was held back: %v", err)
}
if _, err := v.AskEnd(ControllerCaller, first.Call, "", "dismissed"); err != nil {
t.Fatal(err)
}
for i := 0; i < 2; i++ {
o, err := v.AskOpen(ControllerCaller, telegramAsk("g14.claude-code"))
if err != nil {
t.Fatalf("ask %d of the hour: %v", i+2, err)
}
if _, err := v.AskEnd(ControllerCaller, o.Call, "", "timed-out"); err != nil {
t.Fatal(err)
}
}
if _, err := v.AskOpen(ControllerCaller, telegramAsk("g14.claude-code")); err == nil ||
!strings.Contains(err.Error(), "3 times in the last hour") {
t.Fatalf("a fourth ask in the hour: %v", err)
}
asks, _ := v.Store.Asks(50)
outcomes := map[string]int{}
for _, a := range asks {
outcomes[a.Outcome]++
}
if outcomes["dismissed"] != 1 || outcomes["timed-out"] != 2 {
t.Errorf("the record's outcomes: %v", outcomes)
}
// An ask left open counts as open for SecretAskOpenFor only: a prompt that died does not hold the secret.
w := newVault(t, nil)
clock := time.Date(2026, 10, 14, 9, 0, 0, 0, time.UTC)
w.Now = func() time.Time { return clock }
if _, err := w.AskOpen(ControllerCaller, telegramAsk("g14.claude-code")); err != nil {
t.Fatal(err)
}
clock = clock.Add(SecretAskOpenFor + time.Second)
if _, err := w.AskOpen(ControllerCaller, telegramAsk("g14.claude-code")); err != nil {
t.Errorf("an ask left open past its time still held the secret: %v", err)
}
}
// Only the controller reaches a desk's prompt, so only it opens and ends an ask; a value not sealed to the
// call's key, or a call that waited too long, keeps nothing.
func TestAnAskIsTakenFromTheControllerAloneAndOnlySealedToItsCall(t *testing.T) {
v := newVault(t, nil)
for _, caller := range []string{"g14.node-tools", "unproven", "", "person.jochen"} {
if _, err := v.AskOpen(caller, telegramAsk("x")); err == nil {
t.Errorf("%q opened an ask", caller)
}
}
if asks, _ := v.Store.Asks(10); len(asks) != 0 {
t.Fatalf("a refused caller filled the record: %+v", asks)
}
opened, err := v.AskOpen(ControllerCaller, telegramAsk("g14.claude-code"))
if err != nil {
t.Fatal(err)
}
if _, err := v.AskEnd("g14.node-tools", opened.Call, sealToCall(t, opened.SealTo, "x"), ""); err == nil {
t.Error("another caller ended the ask")
}
other, _, _ := box.GenerateKey(rand.Reader)
if _, err := v.AskEnd(ControllerCaller, opened.Call, sealToCall(t, base64.StdEncoding.EncodeToString(other[:]), "x"), ""); err == nil ||
!strings.Contains(err.Error(), "not sealed to this call") {
t.Errorf("a value sealed to another key: %v", err)
}
if _, held, _ := v.Store.Read("own/anchor/telegram/telegram-token"); held {
t.Error("a value not sealed to the call was kept")
}
// The call's key is forgotten once used: the same call cannot be answered twice.
if _, err := v.AskEnd(ControllerCaller, opened.Call, sealToCall(t, opened.SealTo, "x"), ""); err == nil {
t.Error("a call was answered twice")
}
asks, _ := v.Store.Asks(1)
if asks[0].Outcome != "failed" {
t.Errorf("the ask that failed ended %+v", asks[0])
}
// A call that waited longer than its time is closed.
clock := time.Date(2026, 10, 14, 9, 0, 0, 0, time.UTC)
v.Now = func() time.Time { return clock }
late, err := v.IssuedOpen(ControllerCaller, "plex-api", "anchor", "plex")
if err != nil {
t.Fatal(err)
}
clock = clock.Add(CallOpenFor + time.Second)
if _, err := v.IssuedTake(ControllerCaller, late.Call, sealToCall(t, late.SealTo, "x")); err == nil {
t.Error("a call that waited too long was taken")
}
// An empty answer gives nothing and says so.
clock = clock.Add(time.Hour)
empty, _ := v.AskOpen(ControllerCaller, telegramAsk("g14.claude-code"))
if _, err := v.AskEnd(ControllerCaller, empty.Call, sealToCall(t, empty.SealTo, " \n"), ""); err == nil {
t.Error("an empty answer was kept")
}
if asks, _ := v.Store.Asks(1); asks[0].Outcome != "empty" {
t.Errorf("an empty answer ended %+v", asks[0])
}
}
// ---- the issuing provider's test, with a stand-in provider -------------------------------------------------
// standInProvider is a provider's tool for an `issued-by: software` offer, as plex's must answer (ADR 0305 §2
// with the vault in the controller's place): it refuses every caller but the controller — the one caller the
// bus lets ask it, carrying the vault's call — and answers only the software's value sealed to the call's key.
func standInProvider(software *string) func(caller string, args map[string]any) (map[string]any, error) {
return func(caller string, args map[string]any) (map[string]any, error) {
if caller != ControllerCaller {
return nil, errors.New("this tool answers the controller alone")
}
key, _ := args["seal_to"].(string)
raw, err := base64.StdEncoding.DecodeString(key)
if err != nil || len(raw) != 32 {
return nil, errors.New("seal_to is not a sealing key")
}
var to [32]byte
copy(to[:], raw)
sealed, err := box.SealAnonymous(nil, []byte(*software), &to, rand.Reader)
if err != nil {
return nil, err
}
return map[string]any{"sealed": base64.StdEncoding.EncodeToString(sealed)}, nil
}
}
// issue is the controller's `secret issued` for one provider, carried as the controller carries it: open the
// vault's call, ask the provider's tool with the call's key, pass on what it sealed.
func issue(t *testing.T, v testVault, provider func(string, map[string]any) (map[string]any, error)) (Changed, error) {
t.Helper()
opened, err := v.IssuedOpen(ControllerCaller, "plex-api", "anchor", "plex")
if err != nil {
return Changed{}, err
}
answer, err := provider(ControllerCaller, map[string]any{"provision": "plex-api", "seal_to": opened.SealTo})
if err != nil {
return Changed{}, err
}
return v.IssuedTake(ControllerCaller, opened.Call, answer["sealed"].(string))
}
func TestAnIssuingProviderAnswersOnlyTheControllersCallSealedToTheVault(t *testing.T) {
v := newVault(t, AllowList{"anchor.node-provisioner": {"provision/anchor/plex-api"}})
software := "plex-token-from-the-vendor"
provider := standInProvider(&software)
// The provider's tool refuses every caller but the controller, whatever it asks.
for _, caller := range []string{"anchor.node-tools", "g14.claude-code", "unproven", ""} {
if _, err := provider(caller, map[string]any{"provision": "plex-api", "seal_to": "x"}); err == nil {
t.Errorf("the provider's tool answered %q", caller)
}
}
// The vault takes the steps from the controller alone.
if _, err := v.IssuedOpen("anchor.node-tools", "plex-api", "anchor", "plex"); err == nil {
t.Error("the vault opened an issued call for another caller")
}
changed, err := issue(t, v, provider)
if err != nil {
t.Fatal(err)
}
if changed.Name != "provision/anchor/plex-api" || changed.Generation != 1 || changed.By != "plex on anchor" {
t.Errorf("kept as %+v", changed)
}
if got := handedTo(t, v, "anchor.node-provisioner", "provision/anchor/plex-api"); got != software {
t.Errorf("the vault holds %q", got)
}
rec, _, _ := v.Store.Read("provision/anchor/plex-api")
if rec.Origin != OriginIssued || rec.ChangedVia != ControllerCaller {
t.Errorf("the record: origin %q via %q", rec.Origin, rec.ChangedVia)
}
// Nothing rewritten for a value every holder has (ADR 0305 §5): no new generation, no write, nothing said.
path := filepath.Join(v.dir, "store", "secrets", "provision%2Fanchor%2Fplex-api.json")
before, _ := os.ReadFile(path)
said := len(v.bus.raw)
again, err := issue(t, v, provider)
if err != nil {
t.Fatal(err)
}
after, _ := os.ReadFile(path)
if !again.Unchanged || again.Generation != 1 || string(before) != string(after) || len(v.bus.raw) != said {
t.Errorf("the same value again rewrote something: %+v", again)
}
// The software changes its value: the next ask keeps it as a new generation, and says so without it.
software = "plex-token-renewed"
renewed, err := issue(t, v, provider)
if err != nil || renewed.Generation != 2 {
t.Fatalf("%+v %v", renewed, err)
}
if got := handedTo(t, v, "anchor.node-provisioner", "provision/anchor/plex-api"); got != software {
t.Errorf("after the change the vault holds %q", got)
}
// A value sealed to any other key is refused, and nothing changes.
opened, _ := v.IssuedOpen(ControllerCaller, "plex-api", "anchor", "plex")
stranger, _, _ := box.GenerateKey(rand.Reader)
if _, err := v.IssuedTake(ControllerCaller, opened.Call, sealToCall(t, base64.StdEncoding.EncodeToString(stranger[:]), "forged")); err == nil {
t.Error("a value not sealed to the call was taken")
}
// The software rotates it, not the mesh (ADR 0305 §9); and no other path puts it into the vault (§10).
if _, err := v.Rotate(ControllerCaller, "provision/anchor/plex-api", "person.jochen"); err == nil ||
!strings.Contains(err.Error(), "software") {
t.Errorf("rotate of an issued value: %v", err)
}
if _, err := v.AcceptOpen(ControllerCaller, "provision/anchor/plex-api", "person.jochen"); err == nil {
t.Error("an issued value was taken by accept")
}
if strings.Contains(everything(t, v), "plex-token") {
t.Fatal("the issued value is in something the vault said or wrote in the clear")
}
}
// ---- accept and rotate -------------------------------------------------------------------------------------
// A value given at the controller's terminal is sealed there to the vault's call and opened here alone; an
// accepted provision's credential is never remade; a module's own given value rotates as a made one (ADR 0228).
func TestAnAcceptedValueOpensInTheVaultAloneAndIsNotRemade(t *testing.T) {
v := newVault(t, AllowList{"g14.node-provisioner": {"provision/novox/postgres", "own/g14/dunst/token"}})
if _, err := v.AcceptOpen("g14.claude-code", "provision/novox/postgres", ""); err == nil {
t.Error("accept was opened for a caller that is not the controller")
}
for name, value := range map[string]string{"provision/novox/postgres": "pw-of-last-year", "own/g14/dunst/token": "tok"} {
opened, err := v.AcceptOpen(ControllerCaller, name, "jochen")
if err != nil {
t.Fatal(err)
}
changed, err := v.AcceptTake(ControllerCaller, opened.Call, sealToCall(t, opened.SealTo, value))
if err != nil {
t.Fatal(err)
}
if changed.By != "jochen" {
t.Errorf("%s accepted by %q", name, changed.By)
}
if got := handedTo(t, v, "g14.node-provisioner", name); got != value {
t.Errorf("%s holds %q", name, got)
}
rec, _, _ := v.Store.Read(name)
if rec.Origin != OriginAccepted || rec.ChangedVia != ControllerCaller {
t.Errorf("%s: origin %q via %q", name, rec.Origin, rec.ChangedVia)
}
}
if _, err := v.Rotate(ControllerCaller, "provision/novox/postgres", "jochen"); err == nil ||
!strings.Contains(err.Error(), "not remade") {
t.Errorf("rotate of an accepted credential: %v", err)
}
rotated, err := v.Rotate(ControllerCaller, "own/g14/dunst/token", "jochen")
if err != nil || rotated.Generation != 2 {
t.Fatalf("rotate of a module's own given value: %+v %v", rotated, err)
}
if got := handedTo(t, v, "g14.node-provisioner", "own/g14/dunst/token"); got == "tok" || len(got) != 40 {
t.Errorf("rotated to %q", got)
}
if strings.Contains(everything(t, v), "pw-of-last-year") {
t.Fatal("an accepted value is in something the vault said or wrote in the clear")
}
}
// Rotate: refused for a secret not held and for an unproven caller; who asked is the proven caller, or the
// person the controller names when it carries the act — both kept.
func TestRotateKeepsWhoAskedAndWhoCarriedIt(t *testing.T) {
v := newVault(t, nil)
if _, err := v.Rotate(ControllerCaller, "own/g14/dunst/token", "jochen"); err == nil {
t.Error("a secret not held was rotated")
}
if _, err := v.Mint("own/g14/dunst/token", "the test"); err != nil {
t.Fatal(err)
}
if _, err := v.Rotate("unproven", "own/g14/dunst/token", ""); err == nil {
t.Error("an unproven caller rotated")
}
if _, err := v.Rotate(ControllerCaller, "own/g14/dunst/token", "jochen"); err != nil {
t.Fatal(err)
}
rec, _, _ := v.Store.Read("own/g14/dunst/token")
if rec.ChangedBy != "jochen" || rec.ChangedVia != ControllerCaller || rec.Generation != 2 {
t.Errorf("by %q via %q generation %d", rec.ChangedBy, rec.ChangedVia, rec.Generation)
}
// A `by` from any caller but the controller names nobody: the proven caller is who asked.
if _, err := v.Rotate("g14.claude-code", "own/g14/dunst/token", "jochen"); err != nil {
t.Fatal(err)
}
rec, _, _ = v.Store.Read("own/g14/dunst/token")
if rec.ChangedBy != "g14.claude-code" || rec.ChangedVia != "" {
t.Errorf("by %q via %q", rec.ChangedBy, rec.ChangedVia)
}
}
// The names the vault keeps them under are names, never paths.
func TestTheNamesOfWhatIsTakenIn(t *testing.T) {
if n, err := OwnSecret("g14", "dunst", "token"); err != nil || n != "own/g14/dunst/token" {
t.Errorf("%q %v", n, err)
}
if n, err := ProvisionSecret("novox", "postgres"); err != nil || n != "provision/novox/postgres" {
t.Errorf("%q %v", n, err)
}
for _, bad := range [][3]string{{"..", "x", "y"}, {"g14", "a/b", "y"}, {"g14", "x", ""}, {"G14", "x", "y"}} {
if _, err := OwnSecret(bad[0], bad[1], bad[2]); err == nil {
t.Errorf("%v was taken", bad)
}
}
}
// ---- the verbs as the runtime calls them ------------------------------------------------------------------
// Each verb takes its caller from the runtime, never from its arguments, and names its secret one way only.
func TestTheVerbsTakeTheirCallerFromTheRuntime(t *testing.T) {
v := newVault(t, nil)
env := &Env{Vault: v.Vault}
defs := definitions(env)
ask := toolNamed(t, defs, Seat+".secret-ask")
args := map[string]any{"node": "anchor", "module": "telegram", "name": "telegram-token", "desk": "laptop",
"asked_by": "g14.claude-code", "caller": ControllerCaller}
if _, err := ask.Run(args); err == nil {
t.Error("an ask opened for a caller named in the arguments")
}
answer, err := ask.runAs(ControllerCaller, args)
if err != nil {
t.Fatal(err)
}
opened := answer.(Opened)
if _, err := ask.runAs(ControllerCaller, map[string]any{"call": opened.Call, "outcome": "dismissed"}); err != nil {
t.Fatal(err)
}
listed, err := toolNamed(t, defs, "secret_asks").Run(map[string]any{})
if err != nil || len(listed.([]AskRecord)) != 2 {
t.Fatalf("secret_asks: %v %v", listed, err)
}
accept := toolNamed(t, defs, Seat+".accept")
if _, err := accept.runAs(ControllerCaller, map[string]any{"node": "g14", "provision": "postgres"}); err == nil {
t.Error("accept took a name both ways")
}
answer, err = accept.runAs(ControllerCaller, map[string]any{"provider": "novox", "provision": "postgres", "by": "jochen"})
if err != nil {
t.Fatal(err)
}
o := answer.(Opened)
if _, err := accept.runAs(ControllerCaller, map[string]any{"call": o.Call, "sealed": sealToCall(t, o.SealTo, "pw")}); err != nil {
t.Fatal(err)
}
rotate := toolNamed(t, defs, Seat+".rotate")
if _, err := rotate.runAs("g14.claude-code", map[string]any{"node": "g14", "module": "dunst", "name": "token"}); err == nil {
t.Error("a secret not held was rotated through the verb")
}
issued := toolNamed(t, defs, Seat+".issued")
answer, err = issued.runAs(ControllerCaller, map[string]any{"provision": "plex-api", "provider": "anchor", "module": "plex"})
if err != nil {
t.Fatal(err)
}
o = answer.(Opened)
if _, err := issued.runAs(ControllerCaller, map[string]any{"call": o.Call, "sealed": sealToCall(t, o.SealTo, "tok")}); err != nil {
t.Fatal(err)
}
broken := &Env{Broken: errors.New("no store")}
if _, err := toolNamed(t, definitions(broken), Seat+".rotate").runAs(ControllerCaller, map[string]any{}); err == nil ||
err.Error() != "no store" {
t.Errorf("a broken vault said %v", err)
}
}