Files
mesh-catalog/modules/claude-licence-manager/cmd/claude-licence-manager/crypt.go
T
jochen 15b2e6b86e The licence manager, in Go, and claude-code's half of ADR 0206
claude-licence-manager holds the anthropic-licence-manager seat: it reads
every node's holdings state, adopts a login it does not hold by refreshing
it (newest first, once per account), keeps each grant alive under a lease,
publishes what each consumer should hold as its bindings state with a
generation, and answers current sealed to the consumer's key. Postgres
store prepared by a run-once step; grants encrypted with the vault's key.

claude-code reports what its node holds (fingerprints and account, never a
token), hands its grant over only when the manager asks, watches its
binding and fetches the token on a newer generation, and writes
access-token-only. Its ask now reads the runtime's answer as a value and
addresses seats as seats.
2026-10-04 12:18:51 +02:00

65 lines
1.9 KiB
Go

package main
// The grants at rest (novox/hq ADR 0183): encrypted with a key the vault made for this module, so the
// store holds ciphertext and only this module, reading its own secret, can open a row. AES-256-GCM, the
// key derived from the vault's secret by SHA-256 so a secret of any length serves.
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"errors"
"os"
"strings"
)
// Crypt seals and opens what the store keeps.
type Crypt struct{ key []byte }
// NewCrypt is the store's cipher from the vault's secret.
func NewCrypt(secret string) (*Crypt, error) {
secret = strings.TrimSpace(secret)
if secret == "" {
return nil, errors.New("the key the grants are encrypted with is empty: the vault has not delivered it yet")
}
sum := sha256.Sum256([]byte(secret))
return &Crypt{key: sum[:]}, nil
}
// CryptFromFile reads the vault's secret from the file the mesh delivered it to.
func CryptFromFile(path string) (*Crypt, error) {
raw, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return NewCrypt(string(raw))
}
// Seal encrypts a plaintext as `v1.<iv>.<ciphertext and tag>`.
func (c *Crypt) Seal(plaintext string) string {
gcm, _ := newGCM(c.key)
iv := make([]byte, 12)
_, _ = rand.Read(iv)
b64 := base64.StdEncoding.EncodeToString
return "v1." + b64(iv) + "." + b64(gcm.Seal(nil, iv, []byte(plaintext), nil))
}
// Open decrypts what Seal made with the same key.
func (c *Crypt) Open(sealed string) (string, error) {
parts := strings.Split(sealed, ".")
if len(parts) != 3 || parts[0] != "v1" {
return "", errors.New("not a grant this module sealed")
}
iv, err1 := base64.StdEncoding.DecodeString(parts[1])
ct, err2 := base64.StdEncoding.DecodeString(parts[2])
if err := errors.Join(err1, err2); err != nil {
return "", err
}
gcm, _ := newGCM(c.key)
plain, err := gcm.Open(nil, iv, ct, nil)
if err != nil {
return "", errors.New("the grant does not open with this module's key")
}
return string(plain), nil
}