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.
65 lines
1.9 KiB
Go
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
|
|
}
|