A login moves its node; an API key is added from any node, sealed (novox/hq ADR 0209)
The manager binds the node a login was adopted from to that login's licence, switching it if it was bound to another; serves public_key; adopt takes a key sealed to it. claude-code gains claude_code_add_api_key: read a file on this node, seal, hand to adopt, remove the file, optionally switch here.
This commit is contained in:
@@ -343,6 +343,54 @@ func Apply(p Paths, c Current, write WriteManaged) (map[string]any, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AddAPIKey adds an API key from a file on this node to the licence manager (ADR 0209): sealed to the
|
||||
// manager's public key, handed to the seat's `adopt` on request/reply, and the file removed once taken. With
|
||||
// useHere, this node is switched to the new licence. The key never crosses the bus in the clear and is
|
||||
// never an argument.
|
||||
func AddAPIKey(p Paths, name, file string, useHere bool, ask Ask) (map[string]any, error) {
|
||||
raw, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the key is read from a file on this node: %w", err)
|
||||
}
|
||||
key := strings.TrimSpace(string(raw))
|
||||
if key == "" {
|
||||
return nil, fmt.Errorf("%s is empty", file)
|
||||
}
|
||||
answer, err := ask(SeatVerb("public_key"), map[string]any{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pk struct {
|
||||
PublicKey string `json:"public_key"`
|
||||
}
|
||||
if err := json.Unmarshal(answer, &pk); err != nil || !strings.Contains(pk.PublicKey, "PUBLIC KEY") {
|
||||
return nil, errors.New("the licence manager did not say what key to seal to")
|
||||
}
|
||||
box, err := Seal(key, pk.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adopted, err := ask(SeatVerb("adopt"), map[string]any{"name": name, "sealed": box, "from": p.Node})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := map[string]any{"adopted": json.RawMessage(adopted)}
|
||||
// Taken: the key now lives encrypted in the manager's store alone.
|
||||
if err := os.Remove(file); err != nil {
|
||||
out["file"] = "could not be removed: " + err.Error()
|
||||
} else {
|
||||
out["file"] = "removed"
|
||||
}
|
||||
if useHere {
|
||||
switched, err := ask(SeatVerb("switch"), map[string]any{"consumer": p.Node, "licence": name})
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("adopted, and switching this node to it failed: %w", err)
|
||||
}
|
||||
out["switched"] = json.RawMessage(switched)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ---- MCP servers ----------------------------------------------------------------------------------
|
||||
|
||||
// Registration is a server registered (or, with no entry, unregistered) through this module.
|
||||
|
||||
Reference in New Issue
Block a user