Files
mesh-controller/internal/secrets/sealedbox_xcheck_test.go
T
jschoubben c3b88b9148 Rename mesh-control -> mesh-controller, substrate -> foundation
One name per thing, per the HQ glossary: the module/container/image/binary/repo
becomes mesh-controller, the seat the-controller, and the store+broker pair the
foundation (embedded base bundles, default template and example lock renamed with
their go:embed directives). No behaviour change — a pure vocabulary rename.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2026-09-16 18:40:40 +02:00

80 lines
3.2 KiB
Go

package secrets
import (
"encoding/base64"
"encoding/json"
"os"
"testing"
"golang.org/x/crypto/nacl/box"
)
// The manager module's TypeScript seal and this package's Go seal are the SAME anonymous sealed box,
// byte for byte — the property the refreshable-grant carve-out rests on (novox/hq ADR 0050).
//
// **Why it must hold.** The refresh token is sealed to the manager node — at adoption and after each
// rotation — by the manager MODULE, in TypeScript (mesh-catalog anthropic-manager/sealedbox.ts). The
// HOST then unseals it with Go's box.OpenAnonymous (mesh-host identity.SealingKey.Unseal) to mount the
// cleartext, and mesh-controller seals every other credential with box.SealAnonymous (secrets.Seal). If
// the TS seal and the Go box disagreed by a byte, the host would refuse the refresh token as a value
// it cannot open — silently, as a manager that never gets its credential. So this is load-bearing, and
// it is pinned here rather than trusted.
//
// The fixture is produced by the module's own compiled seal() over a fresh node key pair; this test
// opens it with box.OpenAnonymous — exactly what the host runs — and with secrets.Open, and recovers
// the plaintext. Regenerate it with the module's seal() if the construction ever changes; a drift
// shows up here as a fixture Go cannot open, which is the whole point.
func TestModuleSealedBoxOpensInGo(t *testing.T) {
raw, err := os.ReadFile("testdata/module-sealedbox-fixture.json")
if err != nil {
t.Fatal(err)
}
var f struct {
ManagerPublicKey string `json:"managerPublicKey"`
ManagerPrivateKey string `json:"managerPrivateKey"`
Plaintext string `json:"plaintext"`
Sealed string `json:"sealed"`
}
if err := json.Unmarshal(raw, &f); err != nil {
t.Fatal(err)
}
pub, err := base64.StdEncoding.DecodeString(f.ManagerPublicKey)
if err != nil || len(pub) != 32 {
t.Fatalf("the fixture public key is not a 32-byte X25519 key")
}
priv, err := base64.StdEncoding.DecodeString(f.ManagerPrivateKey)
if err != nil || len(priv) != 32 {
t.Fatalf("the fixture private key is not 32 bytes")
}
blob, err := base64.StdEncoding.DecodeString(f.Sealed)
if err != nil {
t.Fatalf("the sealed value is not base64: %v", err)
}
// The host's path: box.OpenAnonymous with the node's key pair.
var pubA, privA [32]byte
copy(pubA[:], pub)
copy(privA[:], priv)
out, ok := box.OpenAnonymous(nil, blob, &pubA, &privA)
if !ok {
t.Fatal("box.OpenAnonymous (the host's Unseal) FAILED to open the module's TS seal — " +
"the TypeScript crypto_box_seal has drifted from Go's box")
}
if string(out) != f.Plaintext {
t.Fatalf("opened to %q, expected %q", out, f.Plaintext)
}
// And it is exactly what secrets.Seal produces: a value this package can round-trip is one the TS
// module could equally have produced, so the two are interchangeable at the seam.
roundTrip, err := Seal(f.ManagerPublicKey, []byte(f.Plaintext))
if err != nil {
t.Fatal(err)
}
rtBlob, _ := base64.StdEncoding.DecodeString(roundTrip)
back, ok := box.OpenAnonymous(nil, rtBlob, &pubA, &privA)
if !ok || string(back) != f.Plaintext {
t.Fatal("secrets.Seal did not round-trip under box.OpenAnonymous")
}
}