Documentation
¶
Index ¶
- Constants
- func CleanupExpired(db *gorm.DB) (int64, error)
- func CreateHISSample(db *gorm.DB, s *HISSample) error
- func DeleteAPIKey(db *gorm.DB, id int64) error
- func GenerateHMACSecret() (string, error)
- func GenerateKeyID() (string, error)
- func GetAllKeysStatsSummary(db *gorm.DB) (map[int64]KeyStatsSummary, error)
- func GetLoginCaptchaEnabled(db *gorm.DB) (bool, error)
- func GetSetting(db *gorm.DB, key string) (string, error)
- func IncrementChallengesIssued(db *gorm.DB, apiKeyID int64) error
- func IncrementCountryVerification(db *gorm.DB, apiKeyID int64, country string, ok bool) error
- func IncrementHISObservation(db *gorm.DB, apiKeyID int64, botSuspected bool) error
- func IncrementVerificationsFail(db *gorm.DB, apiKeyID int64) error
- func IncrementVerificationsOK(db *gorm.DB, apiKeyID int64) error
- func IsConsumed(db *gorm.DB, challenge string) (bool, error)
- func MarkConsumed(db *gorm.DB, challenge string, apiKeyID int64, expiresAt time.Time) error
- func PruneHISSamples(db *gorm.DB, cutoff time.Time) (int64, error)
- func RotateHMACSecret(db *gorm.DB, id int64) (string, error)
- func SetSetting(db *gorm.DB, key, value string) error
- func UpdateAPIKey(db *gorm.DB, id int64, params UpdateAPIKeyParams) error
- type APIKey
- func CreateAPIKey(db *gorm.DB, name, domain string, maxNumber int64, expireSeconds int, ...) (*APIKey, error)
- func EnsureLoginCaptchaAPIKey(db *gorm.DB) (*APIKey, error)
- func GetAPIKeyByID(db *gorm.DB, id int64) (*APIKey, error)
- func GetAPIKeyByKeyID(db *gorm.DB, keyID string) (*APIKey, error)
- func ListAPIKeys(db *gorm.DB) ([]APIKey, error)
- type AdminUser
- type ConsumedChallenge
- type CountryStat
- type DailyCountryStat
- type DailyStat
- type HISCalibration
- type HISSample
- type HISScoreBucket
- type KeyStatsSummary
- type Setting
- type StatsOverview
- type UpdateAPIKeyParams
Constants ¶
const ( SettingLoginCaptchaEnabled = "login_captcha_enabled" SettingLoginCaptchaAPIKeyID = "login_captcha_api_key_id" )
Variables ¶
This section is empty.
Functions ¶
func CreateHISSample ¶
CreateHISSample persists one raw HIS observation.
func GenerateHMACSecret ¶
func GenerateKeyID ¶
func GetAllKeysStatsSummary ¶
func GetAllKeysStatsSummary(db *gorm.DB) (map[int64]KeyStatsSummary, error)
GetAllKeysStatsSummary returns all-time totals grouped by API key ID.
func GetLoginCaptchaEnabled ¶
GetLoginCaptchaEnabled returns whether the login CAPTCHA is enabled.
func GetSetting ¶
GetSetting retrieves a single setting value by key. Returns ("", nil) if the key does not exist.
func IncrementCountryVerification ¶
IncrementCountryVerification records one verification outcome for the key/day/country, atomically bumping the matching counter via UPSERT. country may be empty (unknown source); it is stored as-is so totals reconcile.
func IncrementHISObservation ¶
IncrementHISObservation records one HIS Monitor sample for the key/day: it always bumps the observation count and additionally bumps the bot-suspected count when the sample was flagged. It never affects verification outcomes.
func MarkConsumed ¶
func PruneHISSamples ¶
PruneHISSamples deletes samples recorded before cutoff, returning the count removed. Called periodically by the cleanup worker to bound storage.
func SetSetting ¶
SetSetting upserts a setting value.
func UpdateAPIKey ¶
func UpdateAPIKey(db *gorm.DB, id int64, params UpdateAPIKeyParams) error
Types ¶
type APIKey ¶
type APIKey struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`
KeyID string `gorm:"not null;uniqueIndex;size:32" json:"key_id"`
HMACSecret string `gorm:"not null" json:"hmac_secret,omitempty"`
Name string `gorm:"not null;default:''" json:"name"`
Domain string `gorm:"not null;default:''" json:"domain"`
MaxNumber int64 `gorm:"not null;default:100000" json:"max_number"`
ExpireSeconds int `gorm:"not null;default:300" json:"expire_seconds"`
Algorithm string `gorm:"not null;default:'SHA-256'" json:"algorithm"`
// RateLimitPerMin caps how many /api/v1 requests this key accepts per minute,
// aggregated across all clients. 0 means unlimited (only the global per-IP
// limiter applies).
RateLimitPerMin int `gorm:"not null;default:0" json:"rate_limit_per_min"`
// AdaptiveDifficulty, when set, raises the proof-of-work MaxNumber above this
// key's configured base for clients (by IP) that request challenges at an
// abusive rate, capped server-side. MaxNumber stays the floor.
AdaptiveDifficulty bool `gorm:"not null;default:false" json:"adaptive_difficulty"`
// HISSampling, when set, persists each scored HIS observation for this key
// (raw aggregates + score) so enforcement thresholds can be calibrated on
// real traffic. Samples are pruned after the configured retention window.
HISSampling bool `gorm:"not null;default:false" json:"his_sampling"`
Enabled bool `gorm:"not null;default:true" json:"enabled"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
APIKey represents a site-specific API key used to generate and verify ALTCHA challenges.
func CreateAPIKey ¶
func EnsureLoginCaptchaAPIKey ¶
EnsureLoginCaptchaAPIKey returns the existing login CAPTCHA API key, or creates a dedicated one if none exists yet.
type AdminUser ¶
type AdminUser struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`
Username string `gorm:"not null;uniqueIndex;size:64;default:admin" json:"username"`
PasswordHash string `gorm:"not null" json:"-"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
AdminUser represents a dashboard admin account.
type ConsumedChallenge ¶
type ConsumedChallenge struct {
ID int64 `gorm:"primaryKey;autoIncrement"`
Challenge string `gorm:"not null;uniqueIndex;size:256"`
APIKeyID int64 `gorm:"not null;index"`
ExpiresAt time.Time `gorm:"not null"`
ConsumedAt time.Time `gorm:"not null;autoCreateTime"`
}
ConsumedChallenge tracks used challenge tokens to prevent replay attacks.
type CountryStat ¶
type CountryStat struct {
Country string `json:"country"`
VerificationsOK int `json:"verifications_ok"`
VerificationsFail int `json:"verifications_fail"`
Total int `json:"total"`
}
CountryStat is an aggregated per-country row for the dashboard.
func GetCountryStats ¶
GetCountryStats returns per-country verification totals over the last `days`, ordered by total descending and capped at `limit`. When apiKeyID is nil the totals span every key; otherwise they are scoped to that one key.
type DailyCountryStat ¶
type DailyCountryStat struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"-"`
APIKeyID int64 `gorm:"not null;uniqueIndex:idx_key_date_country" json:"api_key_id"`
Date string `gorm:"not null;uniqueIndex:idx_key_date_country;size:10" json:"date"`
Country string `gorm:"uniqueIndex:idx_key_date_country;size:2" json:"country"`
VerificationsOK int `gorm:"not null;default:0" json:"verifications_ok"`
VerificationsFail int `gorm:"not null;default:0" json:"verifications_fail"`
}
DailyCountryStat holds per-key, per-day, per-country verification counters. Country is an ISO 3166-1 alpha-2 code resolved from the client IP at request time; the raw IP is never stored (privacy-first). An empty Country means the source could not be geolocated (private/loopback/unknown) and is bucketed as "unknown" so per-country totals reconcile with the overall verification ones.
type DailyStat ¶
type DailyStat struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"-"`
APIKeyID int64 `gorm:"not null;uniqueIndex:idx_key_date" json:"api_key_id"`
Date string `gorm:"not null;uniqueIndex:idx_key_date;size:10" json:"date"`
ChallengesIssued int `gorm:"not null;default:0" json:"challenges_issued"`
VerificationsOK int `gorm:"not null;default:0" json:"verifications_ok"`
VerificationsFail int `gorm:"not null;default:0" json:"verifications_fail"`
// HIS (Human Interaction Signature) Monitor counters: every scored sample
// increments HISObservations; samples at/above the suspect threshold also
// increment HISBotSuspected. Monitor mode only records — it never blocks.
HISObservations int `gorm:"not null;default:0" json:"his_observations"`
HISBotSuspected int `gorm:"not null;default:0" json:"his_bot_suspected"`
}
DailyStat holds per-key per-day counters.
type HISCalibration ¶
type HISCalibration struct {
Samples int `json:"samples"`
Suspected int `json:"suspected"`
Threshold float64 `json:"threshold"`
ScoreHistogram []HISScoreBucket `json:"score_histogram"`
AvgDurationMs float64 `json:"avg_duration_ms"`
AvgPointer float64 `json:"avg_pointer_events"`
NoMotionPct float64 `json:"no_motion_pct"`
}
HISCalibration summarizes stored samples to help tune the scoring heuristic and pick an enforcement threshold. The histogram shows how scores distribute relative to the current suspect threshold; the signal averages explain what is driving them.
func GetHISCalibration ¶
func GetHISCalibration(db *gorm.DB, apiKeyID *int64, days int, threshold float64) (*HISCalibration, error)
GetHISCalibration aggregates stored samples over the last `days`. When apiKeyID is nil it spans every key; otherwise it is scoped to that key.
type HISSample ¶
type HISSample struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`
APIKeyID int64 `gorm:"not null;index:idx_his_sample_key_created" json:"api_key_id"`
CreatedAt time.Time `gorm:"index:idx_his_sample_key_created" json:"created_at"`
Score float64 `gorm:"not null" json:"score"`
BotSuspected bool `gorm:"not null" json:"bot_suspected"`
DurationMs int `json:"duration_ms"`
TimeToFirstMs int `json:"time_to_first_ms"`
PointerEvents int `json:"pointer_events"`
PointerDistance float64 `json:"pointer_distance"`
Scrolls int `json:"scrolls"`
Touches int `json:"touches"`
Keydowns int `json:"keydowns"`
KeyIntervalStdevMs float64 `json:"key_interval_stdev_ms"`
}
HISSample is a single stored Human Interaction Signature observation: the raw privacy-preserving aggregates produced by the client collector together with the score the heuristic assigned. Samples are only written for keys that opt in (APIKey.HISSampling) and are pruned after a configurable retention window. They exist so HIS enforcement thresholds can be calibrated against real traffic before any blocking is enabled.
Like the rest of HIS, this stores no coordinates, timestamps, key contents or IP — only counts, distances, durations and timing variance.
type HISScoreBucket ¶
type HISScoreBucket struct {
Lo float64 `json:"lo"`
Hi float64 `json:"hi"`
Count int `json:"count"`
}
HISScoreBucket is one column of the score histogram: scores in [lo, hi).
type KeyStatsSummary ¶
type KeyStatsSummary struct {
APIKeyID int64 `json:"api_key_id"`
ChallengesIssued int `json:"challenges_issued"`
VerificationsOK int `json:"verifications_ok"`
VerificationsFail int `json:"verifications_fail"`
HISObservations int `json:"his_observations"`
HISBotSuspected int `json:"his_bot_suspected"`
LastUsedAt string `json:"last_used_at"`
}
KeyStatsSummary holds all-time totals for a single API key.
type Setting ¶
type Setting struct {
Key string `gorm:"primaryKey;size:64" json:"key"`
Value string `gorm:"not null;default:''" json:"value"`
UpdatedAt time.Time `json:"updated_at"`
}
Setting holds a key-value configuration entry.
type StatsOverview ¶
type StatsOverview struct {
TotalChallenges int `json:"total_challenges"`
TotalVerificationsOK int `json:"total_verifications_ok"`
TotalVerificationsFail int `json:"total_verifications_fail"`
TotalHISObservations int `json:"total_his_observations"`
TotalHISBotSuspected int `json:"total_his_bot_suspected"`
ActiveKeys int `json:"active_keys"`
Daily []DailyStat `json:"daily"`
Countries []CountryStat `json:"countries"`
}
StatsOverview holds aggregated statistics for the dashboard.
func GetStatsOverview ¶
func GetStatsOverview(db *gorm.DB, days int) (*StatsOverview, error)