package runnerfacade import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "errors" ) // Capabilities are deterministic per assignment so controller restarts do not // require a per-task token database. type Capabilities struct{ key []byte } func NewCapabilities(key []byte) (Capabilities, error) { if len(key) < 32 { return Capabilities{}, errors.New("runner facade capability key must be at least 32 bytes") } return Capabilities{key: append([]byte(nil), key...)}, nil } func (c Capabilities) Issue(assignmentID string) string { if len(c.key) < 32 || assignmentID == "" { return "" } mac := hmac.New(sha256.New, c.key) _, _ = mac.Write([]byte("gitea-runner-assignment\x00" + assignmentID)) return base64.RawURLEncoding.EncodeToString(mac.Sum(nil)) } func (c Capabilities) Verify(assignmentID, token string) bool { want := c.Issue(assignmentID) if want == "" || token == "" { return false } return hmac.Equal([]byte(want), []byte(token)) }