sentinel

Adaptive security decisions — distributed rate limiting, lockout, risk scoring, and a tamper-evident audit chain verified in two languages.

GoPythonhash chainsliding window demo: deploy pending

What it is

Meridian’s adaptive-security decision service. POST /v1/check runs an attempt through three stages — rate limit (per-IP / per-user / per-client), brute-force lockout (per-account and per-IP), and a deterministic risk pipeline (impossible travel, new device, velocity, known-bad IPs) — and answers allow | challenge | deny with a score and named reasons. Every decision lands in an append-only, hash-chained audit log; Python tooling verifies the chain and renders compliance reports independently of the Go code.

Key design decisions

// hashRecord computes hex(SHA-256(prev hash hex || canonical JSON)).
func hashRecord(r Record) (string, error) {
    c, err := canonical(r)
    if err != nil {
        return "", err
    }
    h := sha256.New()
    h.Write([]byte(r.Prev))
    h.Write(c)
    return hex.EncodeToString(h.Sum(nil)), nil
}

Security highlights

From the threat model: success during an active lockout does not unlock — a stolen password doesn’t convert a lockout into a free pass; an unknown rate-limit class is a hard error, not “unlimited” (typo-safe); device fingerprints are only enrolled on successful auth, so failing logins with a chosen fingerprint can’t whitelist it; Check performs uniform work whether or not a key is locked, so lockout state can’t become a username oracle; risk never lowers a hard gate’s verdict. The honest limit of hash chains is documented too: whole-file rewrite is only detectable via external anchoring, and the periodic anchor records are the mount point for KMS/WORM/RFC 3161 notarization.

Verified by

Code worth reading