Yabanin Security Posture
This page is the entry point to Yabanin's security model: who can authenticate through which surface, how secrets are stored, where the policy pipeline fits as a control layer, and how the supply chain is guarded. Detailed findings and the threat-model history live in security-audit.md; the OWASP Top-10 for LLM Applications mapping lives in security-owasp-llm.md.
Authentication surfaces
| Surface | Credential | Mechanism | Code |
|---|---|---|---|
| Admin console (local) | email + password | bcrypt (cost 12) hashes; a login attempt always burns bcrypt work (timing-equalized); HMAC-signed session cookie with expiry + issued-at | pkg/adminauth/auth.go, pkg/adminauth/hash.go |
| Admin SSO — OIDC | upstream IdP | Authorization Code + PKCE (S256), state CSRF token, nonce, fail-closed user mapping | pkg/adminauth/oidc.go, sso-oidc.md |
| Admin SSO — SAML 2.0 | upstream IdP | ACS endpoint, signed AuthnRequests, assertion replay cache (each assertion ID accepted once) | pkg/adminauth/saml.go, sso-saml.md |
| SCIM 2.0 provisioning | org-scoped bearer token | mounted on the public router under its own bearer guard (never the admin session); token lookup by SHA-256 (unique index, no secret comparison in SQL) + constant-time compare | services/admin/handlers/scim.go, pkg/adminstore/scim.go |
| Gateway JWT auth | external JWT | keys selected by kid from the org-configured JWKS only; iss/aud/exp/nbf enforced; the org claim becomes the request's org context and the key-label claim becomes locked key labels; symmetric keys planted in the JWKS are rejected; with an unreachable JWKS the gateway fails closed (503, bounded staleness) | pkg/gateway/auth_jwt.go |
Gateway API keys (yab_*) | bearer key | stored SHA-256-hashed; resolved through a Redis cache keyed by sha256(plaintext) with HMAC-signed entries (CacheSigner) — a Redis attacker cannot forge a resolve result | pkg/auth/resolver.go, pkg/auth/sessionkeys.go |
Agent keys (yag_*) | bearer key | same hashing; agent-principal semantics (tool calls, budgets, task tags) | pkg/auth/agentkey.go, agent-governance.md |
Revocation propagation is bounded and pinned by tests: every revocation path
purges the shared resolve cache, so a revoked key is denied immediately with
Redis, or within YABANIN_ORG_RESOLVER_TTL (default 300 s) without it —
security-audit.md, "Key Revocation Propagation".
Brute force and abuse: admin login is limited to 5 attempts / 15 min, the
admin API to 1000 req/min, with a Redis-backed limiter
(pkg/adminauth/ratelimit_redis.go); the gateway applies per-IP token-bucket
limits. Admin mutations land in the hash-chained audit journal
(pkg/auditlog: row_hash = sha256(prev_hash || payload)), and privileged
routes require root-only middleware.
Secrets at rest
enc:envelopes. Provider keys (BYOK) and MCP upstream credentials are stored as AES-256-GCM ciphertext with anenc:version prefix. Writes are fail-closed: without a configured encryptor the write is refused — a missing key can never silently downgrade to plaintext. Weak keys (short, low-entropy hex) are rejected at construction (pkg/encryption/encryption.go).- KMS master key.
local(env/file master key,YABANIN_KMS_LOCAL_KEY) andfileproviders are wired; theawsprovider fails closed unless configured (pkg/kms/factory.go). - Hashed, never recoverable. API/agent keys are SHA-256 hashes; SCIM bearer tokens resolve by hash lookup with a constant-time confirm; admin passwords are bcrypt. No plaintext path exists for any of these.
- Masked reads. Every API that returns a secret returns the masked row
(
secret_last4,has_secret); plaintext exists only as a call argument into store/resolve methods and is never logged or serialized (mcp-gateway.md §6). - Org isolation. Secret-bearing tables sit under the org-isolation RLS
contract (
ENABLE+FORCE,app.current_orgGUC pinned per transaction); a foreign org's rows are invisible and unwritable. - Deployment secrets (
session_key,cache_hmac,pg_uri, KMS keys) are provisioned as Docker secrets (/run/secrets/), not environment variables; rotation is a documented runbook (ops/secrets-rotation.md).
The policy pipeline as a control layer
Policy Pipeline (YPP) is the enforcement point for content-level controls: PII/secret/injection detectors, redaction and pseudonymization mutators, external scanners, LLM judges, output-schema and streaming checks. Properties that make it a control rather than a feature:
- Off by default, admin-owned. With
YABANIN_POLICY_MODEunset the runtime is never built; there are no per-request (SDK-supplied) policy configs, so a client cannot bypass an admin's policy. - Fail-closed default. A step error denies by default (
on_error: fail_closed);fail_openis an explicit per-step choice. Unknown actions rank as deny. - Shadow → enforce. Policies roll out in shadow mode (record-only, never mutates), with the decision log (90-day retention, content hashed) and per-policy metrics as the review surface. A process-wide kill switch downgrades everything to shadow in one move.
- Bounded egress. Callout steps require an operator allow-list
(
YABANIN_POLICY_CALLOUT_ALLOW_HOSTS); every dial is SSRF-guarded with pinned DNS (no rebinding window), no redirects, no retries, per-URL circuit breaker.
The OWASP LLM Top-10 mapping (security-owasp-llm.md) spells out per-risk what this layer covers and what it does not.
Supply chain
- Dependency CVE scanning in the gates.
make analyze-securityrunsgo mod verify+ symbol-levelgovulncheck; the same check is a quality gate (scripts/quality/dep_audit.py), so a reachable CVE fails the build, not just a report. Analyzer versions are pinned (make install-analysis-tools); findings triage usesscripts/security/govulncheck-classify.sh. Current scan results: security-audit.md. - Dependency policy. New Go dependencies ride the same review + quality gates as code changes; the scanner classifies findings by reachability, and "not called" findings are recorded with that justification rather than silenced.
- Runtime supply chain. Containers run read-only with
no-new-privileges on an internal bridge network; services bind to internal
interfaces, and egress to user-supplied hosts always passes the SSRF
allow-list guard (
pkg/ssrf). - Policy/prompt/model provenance. Policies reconcile from git and record the git SHA of the declaring revision; judge prompts and prompt-studio resources are versioned; model eligibility can be constrained per org with the model-whitelist step. The npm tree of the dashboard/site and Docker base images are outside Go scanning — a known, documented boundary (security-owasp-llm.md, LLM03).
Incident response
Runbooks: incident response, severity classification, secret rotation, budget-reservation alerts, and the production runbook.