agentbbs/internal/qryptinvite/config.go
RissRIce 7cb2aba24e
Some checks failed
CI / build (push) Has been cancelled
deploy / deploy (push) Has been cancelled
test / test (push) Has been cancelled
fix(qryptinvite): validate quota and TTL env values (#93)
Co-authored-by: rissrice2105-agent <rissrice2105-agent@users.noreply.github.com>
2026-07-16 19:01:41 -07:00

74 lines
2.3 KiB
Go

package qryptinvite
import (
"crypto/ed25519"
"errors"
"os"
"strconv"
"time"
)
// Config is the resolved qrypt.chat invite-issuer configuration, read from the
// environment (see docs/qrypt-invites.md). It is shared by the SSH plugin and
// the ops CLI so they mint identical tokens.
type Config struct {
IssuerID string // AGENTBBS_QRYPT_ISSUER_ID (default "agentbbs")
Key string // AGENTBBS_QRYPT_ISSUER_KEY (base64 seed/priv)
TTL time.Duration // AGENTBBS_QRYPT_INVITE_TTL (default 168h)
RedeemURL string // AGENTBBS_QRYPT_REDEEM_URL (default https://qrypt.chat/anon?invite=)
Quota int // AGENTBBS_QRYPT_INVITE_QUOTA (default 5)
}
// DefaultIssuerID, DefaultRedeemURL, DefaultTTL and DefaultQuota are the
// fallbacks when the corresponding env var is unset.
const (
DefaultIssuerID = "agentbbs"
DefaultRedeemURL = "https://qrypt.chat/anon?invite="
DefaultTTL = 168 * time.Hour
DefaultQuota = 5
)
// ConfigFromEnv reads the AGENTBBS_QRYPT_* environment variables, applying
// defaults. The key is not validated here; call PrivateKey to parse it.
func ConfigFromEnv() Config {
c := Config{
IssuerID: DefaultIssuerID,
Key: os.Getenv("AGENTBBS_QRYPT_ISSUER_KEY"),
TTL: DefaultTTL,
RedeemURL: DefaultRedeemURL,
Quota: DefaultQuota,
}
if v := os.Getenv("AGENTBBS_QRYPT_ISSUER_ID"); v != "" {
c.IssuerID = v
}
if v := os.Getenv("AGENTBBS_QRYPT_REDEEM_URL"); v != "" {
c.RedeemURL = v
}
if v := os.Getenv("AGENTBBS_QRYPT_INVITE_TTL"); v != "" {
if d, err := time.ParseDuration(v); err == nil && d > 0 {
c.TTL = d
}
}
if v := os.Getenv("AGENTBBS_QRYPT_INVITE_QUOTA"); v != "" {
if n, err := strconv.Atoi(v); err == nil && n >= 0 {
c.Quota = n
}
}
return c
}
// ErrNoKey means AGENTBBS_QRYPT_ISSUER_KEY is unset, so no tokens can be minted.
var ErrNoKey = errors.New("qryptinvite: AGENTBBS_QRYPT_ISSUER_KEY is not set (run: agentbbs qrypt-issuer-keygen)")
// PrivateKey parses the configured issuer key, or returns ErrNoKey if unset.
func (c Config) PrivateKey() (ed25519.PrivateKey, error) {
if c.Key == "" {
return nil, ErrNoKey
}
return ParsePrivateKey(c.Key)
}
// RedeemLink returns the full URL a member opens to redeem token.
func (c Config) RedeemURLFor(token string) string {
return c.RedeemURL + token
}