9029d48584
- TERDUT_OPERATOR_KEY creates or re-keys the instance-scoped service account
"terdut-operator" at every start, so terdut-operator needs no bootstrap
handshake. An instance-scoped account now acts as owner of every team's
configuration, but is not a member of any team.
- POST /api/teams takes an external_id (instance service accounts only) and
is idempotent on it, so automation finds its own team again after a crash
instead of adopting by display name. GET /api/teams?name= is removed.
- Integration and dead man's switch names are unique per team (409). The
escalation PUT accepts usernames and resolves them itself.
- The 18 migrations are squashed into 001_schema.sql, with no Default team.
TERDUT_DEADMAN_* and the env seeding of switches are removed: teams carry
their own. Existing development databases must be recreated.
Security and robustness:
- GET /api/users no longer returns other people's email or ntfy topic to
non-admins.
- The access log records the route pattern, so integration keys and ack
tokens in the path are not written to the log. Server errors are logged.
- Rate limits take the client address TERDUT_TRUSTED_PROXIES hops from the
right of X-Forwarded-For instead of trusting the first, forgeable entry.
- /api/bootstrap runs in a transaction under an advisory lock, so two
concurrent calls cannot both create an administrator.
- API key last_used_at is written at most every five minutes.
Cleanup: remove GET /api/incidents/{id}/alerts, unused exports, SQLite
remnants in comments and config.
Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
32 lines
904 B
Go
32 lines
904 B
Go
package api
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// DeadmanConfig is a test fixture only: a set of matchers with one timeout and
|
|
// severity, turned into switches over the API by the test helpers. Production
|
|
// has no server-wide default any more -- switches belong to teams.
|
|
type DeadmanConfig struct {
|
|
Matchers []DeadmanMatcher
|
|
Timeout time.Duration
|
|
Severity string
|
|
}
|
|
|
|
// ParseDeadmanConfig reads a ";"-separated matcher list the way the removed
|
|
// environment variable did, dropping malformed entries.
|
|
func ParseDeadmanConfig(matchers string, timeout time.Duration, severity string) DeadmanConfig {
|
|
cfg := DeadmanConfig{Timeout: timeout, Severity: severity}
|
|
for _, entry := range strings.Split(matchers, ";") {
|
|
entry = strings.TrimSpace(entry)
|
|
if entry == "" {
|
|
continue
|
|
}
|
|
if m, err := parseDeadmanMatcher(entry); err == nil {
|
|
cfg.Matchers = append(cfg.Matchers, m)
|
|
}
|
|
}
|
|
return cfg
|
|
}
|