API Security Evidence

Evidence of authentication, rate limiting, input validation, CORS policies, and security headers across API services

Public

Status: pre-launch. This evidence reflects implemented code and deployed infrastructure. Provii is not yet serving end-user production traffic, so production operational metrics and audit history are not yet available.

API Security Evidence Collection

Control Coverage: UC-044 through UC-051 Date Generated: 2026-02-14 Status: Complete Repositories Analysed: 4 backend services (provii-verifier, provii-issuer, provii-management, provii-credit-management)


Executive Summary

This document provides evidence for API security controls across the Maelstrom AI backend infrastructure. All services demonstrate defence-in-depth strategies including TLS enforcement, HMAC authentication, RBAC authorisation, security headers, rate limiting, and input validation.

Key Findings:

  • TLS enforcement via Cloudflare across all services (UC-044)
  • Multi-layered authentication: HMAC-SHA256, JWT sessions, API keys (UC-045)
  • RBAC and BOLA protection (UC-046)
  • Security headers on all services (13 headers on provii-verifier)
  • Rate limiting via shared-rate-limit library and per-endpoint middleware
  • Input validation using Zod schemas (TypeScript) and strict newtype wrappers (Rust)
  • Audit logging to KV with 90-day retention; critical security event logs are retained for up to 365 days

Table of Contents

  1. UC-044: Encryption in Transit (TLS)
  2. UC-045: Authentication Mechanisms
  3. UC-046: Authorisation Controls
  4. UC-047: Incident Response
  5. UC-048: Vulnerability Management
  6. UC-049: Security Monitoring
  7. UC-050: Penetration Testing
  8. UC-051: Security Awareness Training
  9. API Security Best Practices
  10. Evidence Artifacts

UC-044: Encryption in Transit (TLS)

Implementation

Cloudflare Universal SSL/TLS: All Maelstrom AI backend services are deployed on Cloudflare Workers, which provides:

  • TLS 1.2+ enforcement (TLS 1.3 preferred)
  • Automatic certificate management and renewal
  • HTTP Strict Transport Security (HSTS)
  • Automatic HTTP to HTTPS redirects

Evidence

1. HSTS Headers (All Services)

provii-verifier (src/security/headers.rs):

headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")?;

provii-management (src/middleware/security-headers.ts:48):

// Always enabled (Cloudflare Workers run on HTTPS by default)
c.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');

provii-credit-management (src/utils/security-headers.ts:27):

'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',

2. TLS Configuration Evidence

Deployment Platform:

  • All services: wrangler.toml -> Cloudflare Workers deployment
  • Cloudflare enforces TLS 1.2+ by default
  • No plaintext HTTP endpoints exposed to public internet

Local Development:

  • Development environments use http://localhost only
  • Production/staging enforce HTTPS exclusively

3. Certificate Management

Cloudflare Automatic Certificate Management:

  • Universal SSL certificates auto-issued for all *.provii.app and *.zerokp.id domains
  • Automatic renewal (60-90 days before expiration)
  • Certificate transparency logging enabled
  • OCSP stapling for revocation checking

Evidence Location:

  • Cloudflare Dashboard -> SSL/TLS -> Edge Certificates
  • HSTS preload list submission: hstspreload.org (pending)

UC-045: Authentication Mechanisms

Overview

Maelstrom AI backend services implement multiple authentication mechanisms:

  1. HMAC-SHA256 per-request signatures (provii-verifier, provii-issuer, provii-management)
  2. JWT session tokens (provii-management via Logto)
  3. API key authentication (provii-issuer, provii-credit-management)

1. HMAC-SHA256 Authentication

provii-verifier

Implementation: provii-verifier/src/security/auth.rs

Canonical Message Format:

// From src/routes/challenge.rs lines 245-273
fn create_canonical_message_for_challenge(
    method: &str,
    path: &str,
    timestamp: u64,
    body: &CreateChallengeRequest,
) -> String {
    let payload = json!({
        "code_challenge": body.code_challenge.to_string(),
        "method": body.method,
        "requested_cutoff_days": body.requested_cutoff_days.map(|c| c.get()),
        "verifying_key_id": body.verifying_key_id.map(|v| v.get()),
        "expires_in": body.expires_in.get(),
        "proof_direction": body.proof_direction,
    });

    format!("{}:{}:{}:{}", timestamp, method, path, payload.to_string())
}

Security Properties:

  • 256-bit HMAC secret (CSPRNG-generated)
  • Replay protection via timestamp validation (300-second window)
  • Nonce-based duplicate request detection
  • Constant-time signature verification (ct_eq in auth.rs)
  • Request integrity protection (method + path + body)

Documentation: provii-verifier/docs/security/AUTHENTICATION_PATHWAYS.md

provii-issuer

Implementation: provii-issuer/src/security.rs

Request Signature:

// Lines 43-56
pub struct RequestSignature {
    pub method: String,
    pub path: String,
    pub query_params: Vec<(String, String)>,
    pub content_type: String,
    pub body_hash: String,
    pub timestamp: u64,
}

impl RequestSignature {
    pub fn to_canonical_string(&self) -> String {
        let mut sorted_params = self.query_params.clone();
        sorted_params.sort_by(|a, b| a.0.cmp(&b.0));

        let query_string = sorted_params
            .iter()
            .map(|(k, v)| format!("{}={}", k, v))
            .collect::<Vec<_>>()
            .join("&");

        format!(
            "{}\n{}\n{}\n{}\n{}\n{}",
            self.method, self.path, query_string, self.content_type, self.body_hash, self.timestamp
        )
    }
}

Verification:

// Lines 89-98
pub fn verify(&self, secret: &[u8], signature: &[u8]) -> Result<bool> {
    type HmacSha256 = Hmac<Sha256>;
    let mut mac = HmacSha256::new_from_slice(secret)
        .map_err(|e| ApiError::CryptoError(format!("Invalid HMAC key: {}", e)))?;

    mac.update(self.to_canonical_string().as_bytes());

    // Constant-time verification
    Ok(mac.verify_slice(signature).is_ok())
}

provii-management

Implementation: provii-management/src/middleware/auth.ts

Canonical Message:

// Lines 131-133 of auth.ts
const method = c.req.method;
const path = c.req.path + (c.req.url.includes('?') ? '?' + new URL(c.req.url).search.slice(1) : '');
const canonicalMessage = createCanonicalMessage(timestamp, method, path, bodySha256);

// from utils/hmac.ts:
export function createCanonicalMessage(timestamp: number, method: string, path: string, bodyHash: string): string {
  return `${timestamp}:${method}:${path}:${bodyHash}`;
}

HMAC Verification:

// auth.ts line 136-162
const secret = await c.env.MANAGEMENT_HMAC_KEY.get(); // Secrets Store binding (async)

const isValidSignature = await verifyHMAC(secret, signature, canonicalMessage);

if (!isValidSignature) {
  console.warn('Authentication failed: Invalid HMAC signature', {
    sessionId,
    path,
    method,
    timestamp,
    canonicalMessage,
    receivedSignature: signature.substring(0, 30) + '...',
  });
  throw new HTTPException(401, { message: 'Invalid request signature' });
}

2. JWT Session Tokens

provii-management (Durable Object SessionManager)

Session Retrieval and Validation:

// Lines 168-177 from auth.ts
const session = await getAdminSessionFromDO(c.env.ADMIN_PORTAL_SESSION_MANAGER, sessionId);

if (!session) {
  throw new HTTPException(401, {
    message: 'Invalid or expired session',
    cause: AuthErrorType.SESSION_NOT_FOUND,
  });
}

// Validate session expiration
if (session.expiresAt < now) {
  throw new HTTPException(401, { message: 'Session has expired' });
}

Session Binding Validation:

// Lines 210-214
// CRITICAL-04 FIX: Validate session binding
await validateSessionBinding(c);

// HIGH-05 FIX: Validate session expiration
await validateSessionExpiration(c);

3. API Key Authentication

provii-issuer

Implementation: provii-issuer/src/security.rs:119-260

pub async fn verify_api_key(env: &Env, api_key: &str) -> Result<ClientRegistration> {
    let kv = env
        .kv("ISSUER_CLIENTS")
        .map_err(|e| ApiError::StorageError(format!("Failed to get KV namespace: {}", e)))?;

    let list_result = kv
        .list()
        .execute()
        .await
        .map_err(|e| ApiError::StorageError(format!("Failed to list clients: {}", e)))?;

    // Try each client (with Argon2id hash verification)
    for (idx, key_info) in list_result.keys.iter().enumerate() {
        if let Ok(Some(data)) = kv.get(&key_info.name).text().await {
            match serde_json::from_str::<ClientRegistration>(&data) {
                Ok(client) => {
                    if !client.active {
                        continue;
                    }
                    // Verify Argon2id hash (constant-time via Argon2 library)
                }
            }
        }
    }

    Err(ApiError::Unauthorized("Invalid API key".to_string()))
}

Security Properties:

  • API keys hashed with Argon2id
  • Constant-time comparison (built into Argon2id verification)
  • Active/inactive status checking
  • KV-based storage with encryption at rest (hashes encrypted before storage)

UC-046: Authorisation Controls

RBAC Implementation

provii-management

Role Definitions:

// From types/index.ts line 211
export type AdminRole = 'super_admin' | 'admin' | 'viewer';

// From types/index.ts lines 365-404
export interface AdminSession {
  sessionId: string;
  userId: string;
  email: string;
  role: AdminRole;
  createdAt: number;
  expiresAt: number;
  last_activity: number;
  ip_hash: string;
  user_agent_hash: string;
  allowed_environments?: string[];
  organization_id?: string;
}

Role Checking:

// From middleware/auth.ts lines 328-344
function isValidSession(data: unknown): data is AdminSession {
  if (!data || typeof data !== 'object') {
    return false;
  }

  const session = data as Partial<AdminSession>;

  return (
    typeof session.sessionId === 'string' &&
    typeof session.userId === 'string' &&
    typeof session.email === 'string' &&
    typeof session.role === 'string' &&
    ['viewer', 'admin', 'super_admin'].includes(session.role) &&
    typeof session.createdAt === 'number' &&
    typeof session.expiresAt === 'number'
  );
}

BOLA Protection (Broken Object Level Authorisation)

provii-verifier

Implementation: provii-verifier/src/routes/redeem.rs:168-257

// SECURITY: Mandatory ownership verification (OWASP API1:2023, CWE-639)
match &cached.client_id {
    Some(owner_id) if owner_id == &client_id => {
        console_log!(
            "[SECURITY] redeem_challenge: Ownership verified for challenge {} by client {}",
            redact_challenge_id(&sid.to_string()),
            client_id
        );
    }
    Some(owner_id) => {
        console_log!(
            "[SECURITY] redeem_challenge: Ownership verification failed - challenge {} owned by '{}', accessed by '{}'",
            redact_challenge_id(&sid.to_string()),
            owner_id,
            client_id
        );
        return ApiError::Forbidden("Access denied: you do not own this challenge").to_response();
    }
    None => {
        console_log!(
            "[SECURITY] redeem_challenge: Ownership verification failed - challenge {} has no owner",
            redact_challenge_id(&sid.to_string())
        );
        return ApiError::Forbidden("Access denied: challenge ownership not established").to_response();
    }
}

Documentation: provii-verifier/docs/security/AUTHENTICATION_PATHWAYS.md (Lines 449-558)

CVSS Score: 7.5 (High) - Mitigated

Migration Status:

  • Phase 1 (Complete): All new challenges include client_id
  • Phase 2 (In Progress): Monitor legacy challenge usage
  • Phase 3 (Month 4-5): 60-day grace period with enforcement date
  • Phase 4 (Month 10): Strict enforcement, reject all legacy challenges

Principle of Least Privilege

provii-verifier - Asymmetric Security Design

Public Endpoint (GET /v1/challenge/{id}):

  • No authentication required (UUID serves as capability credential)
  • Returns minimal data: challenge_id, short_code, rp_challenge, cutoff_days, verifying_key_id, submit_secret, expires_at, proof_direction, status_url, verify_url
  • submit_secret is a single-use 32-byte bearer token
  • Sensitive data (origin, client_id, PKCE) requires authentication via separate poll endpoint

Authenticated Endpoints:

  • POST /v1/redeem. Requires PKCE code_verifier + submit_secret
  • POST /v1/challenge. Requires HMAC authentication + Client ID extraction
  • GET /v1/challenge/{id}/poll. Requires HMAC authentication + ownership verification

Rationale: Users need to retrieve challenges by short code (accessibility), but sensitive operations require strong authentication and authorisation.


UC-047: Incident Response

Audit Logging

provii-management

Audit Middleware: provii-management/src/middleware/audit.ts

Log Format:

// From types/index.ts lines 219-265
export interface AuditEvent {
  eventId: string;
  timestamp: number;
  action: string;
  category: string;
  adminUser: {
    userId: string;
    email: string;
    role: string;
  };
  target?: {
    type: string;
    id: string;
  };
  request: {
    ip: string;
    userAgent: string;
    requestId: string;
    sessionId?: string;
    correlationId?: string;
  };
  success: boolean;
  error?: string;
  metadata?: Record<string, any>;
  statusCode?: number;
  durationMs?: number;
}

All API Calls Logged:

// Applied globally (src/index.ts:149)
app.use('*', auditMiddleware);

provii-verifier

Security Logging Examples:

BOLA Attempt Detection:

// redeem.rs lines 228-233
console_log!(
    "[SECURITY] redeem_challenge: Ownership verification failed - challenge {} owned by '{}', accessed by '{}'",
    redact_challenge_id(&sid.to_string()),
    owner_id,
    client_id
);

Authentication Failures:

// auth.rs - HMAC mismatch logging with CWE-778 reference
console_log!("[Auth] HMAC mismatch for client_id={}", client.client_id);
// Structured logging with failure_type: "hmac_signature_mismatch"

Incident Response Procedures

Documentation: src/content/trust/security/incident-response.mdx

Incident Classification:

  • Critical (P0): Authentication bypass, data breach, service outage
  • High (P1): BOLA attempts, rate limit abuse, suspicious traffic patterns
  • Medium (P2): Failed authentication attempts, input validation errors
  • Low (P3): Configuration warnings, certificate expiry notices

Incident Tracking:

  • All audit events logged to MANAGEMENT_AUDIT_LOGS KV namespace (90-day retention; critical security event logs are retained for up to 365 days)
  • Slack webhook integration exists for admin-portal alerts
  • Post-incident review procedures defined in incident-response.mdx

UC-048: Vulnerability Management

Dependency Scanning

Automated Scanning:

  • npm audit for TypeScript services (provii-management, provii-credit-management)
  • cargo audit for Rust services (provii-verifier, provii-issuer)
  • Dependabot enabled on all GitHub repositories
  • Semgrep for SAST analysis
  • Trivy for vulnerability scanning
  • Gitleaks for secret detection

Evidence:

# Security workflows vary by repo type:
# Rust repos: .github/workflows/security-audit.yml
# TypeScript repos: .github/workflows/security.yml
# All use SHA-pinned actions (e.g., actions/checkout@v6.0.2)

Penetration Testing

Planned Testing:

  • Internal Testing: Quarterly security assessments
  • External Pentest: Annual third-party penetration test
  • Responsible Disclosure: security@maelstrom.au

Vulnerability Disclosure

Contact: security@maelstrom.au

Disclosure Policy:

  • Report submitted -> 48-hour acknowledgment
  • Initial assessment -> 5 business days
  • Fix timeline -> 30 days for critical, 90 days for high/medium
  • Public disclosure -> Coordinated with reporter

UC-049: Security Monitoring

Rate Limiting

All rate limiting uses a simple KV counter pattern (non-atomic increment with TTL, fail-open on KV errors). Tier-based per-customer limits are loaded from RATE_LIMIT_CONFIG KV and cached per-isolate for 60 seconds. The shared-rate-limit library and its Durable Object have been retired and archived.

Architecture:

┌─────────────────────────────────────────────────────────────┐
│ Layer 1: Cloudflare Edge (WAF rate limiting, 5,000/10min/IP)│
├─────────────────────────────────────────────────────────────┤
│ Layer 2: Per-service KV counters (hourly, tier-based)       │
│   - Per-customer quota (authenticated, from tier config)    │
│   - Per-IP limit (short code enumeration, provii-verifier)     │
│   - Per-issuer limit (blind issuance, provii-issuer)           │
│   - In-memory auth limiter (admin-portal only)              │
└─────────────────────────────────────────────────────────────┘

Per-Service Limits:

ServiceLimit TypeDefaultIdentifierConfigurable Via
provii-verifierPer-customer hourly quotaDEFAULT_QUOTA_PER_HOUR env varAuthenticated client IDKV tier config
provii-verifierShort code enumerationSHORT_CODE_LIMIT_PER_HOUR (60/hr)CF-Connecting-IPwrangler.toml env var
provii-issuerPer-customer hourly quotaDEFAULT_QUOTA_PER_HOUR env varAuthenticated client IDKV tier config
provii-issuerBlind issuance per-issuerBLIND_ISSUANCE_LIMIT_PER_HOUR (1,000/hr)Issuer IDwrangler.toml env var
provii-verifier (hosted mode)Per-customer hourly quotaDEFAULT_QUOTA_PER_HOUR env varPublic key (pk_)KV tier config
admin-portalAuth brute-force5/min per IPCF-Connecting-IPIn-memory Map
provii-managementNone (internal, behind admin-portal auth)...
provii-credit-managementNone (internal, service-to-service only)...

HTTP 429 Response Format:

  • Retry-After header (seconds remaining in current hour)
  • JSON body: { "error": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded", "retry_after": <seconds> }

Nonce Replay Prevention: NonceDO (src/durable_objects/nonce_do.rs) handles nonce replay detection separately from rate limiting. Nonces expire after 5 minutes (300 seconds).

Alerting

Implemented:

  • Slack webhook integration for admin-portal events
  • Audit log storage in MANAGEMENT_AUDIT_LOGS KV (90-day retention; critical security event logs are retained for up to 365 days)
  • Cloudflare Workers Logs (shipped to Grafana Loki) for real time metrics

Planned (not yet implemented):

  • PagerDuty integration for critical alerts
  • Email daily digest
  • Bespoke Grafana dashboards for rate limit monitoring
  • ML-based anomaly detection

UC-050: Penetration Testing

Testing Schedule

Frequency:

  • Internal security testing: Quarterly
  • External penetration testing: Annual
  • Responsible disclosure: Continuous (security@maelstrom.au)

Test Scope

In-Scope:

  • All API endpoints (provii-verifier, provii-issuer, provii-management, provii-credit-management)
  • Authentication mechanisms (HMAC, JWT, API keys)
  • Authorisation controls (RBAC, BOLA)
  • Rate limiting effectiveness
  • Input validation
  • Session management
  • Cryptographic implementations

Out-of-Scope:

  • Physical security
  • Social engineering
  • Denial of service attacks (without pre-approval)
  • Third-party services (Cloudflare, Logto, Stripe)

Methodology

Standards:

  • OWASP API Security Top 10 (2023)
  • OWASP ASVS 5.0.0 (Level 3)
  • NIST SP 800-115 (Technical Guide to Information Security Testing)

Phases:

  1. Reconnaissance: Identify attack surface, enumerate endpoints
  2. Vulnerability Discovery: Automated scanning + manual testing
  3. Exploitation: Attempt to exploit discovered vulnerabilities
  4. Privilege Escalation: Test lateral movement and privilege escalation
  5. Reporting: Document findings with CVSS scores and remediation guidance

Previous Testing Results

provii-verifier ASVS 5.0.0 Hardening (2025-11-07):

Critical fixes implemented as part of ASVS alignment:

  1. V3.5.8. Sec-Fetch header validation (fail-closed enforcement)
  2. V14.2.5. Cache headers (cache deception prevention)
  3. V13.3.4. Secret expiry enforcement (fail-fast logic)
  4. V3.4.7. CSP endpoint rate limiting (DoS prevention)
  5. V11.7.2. Debug redaction (secret leakage prevention)

UC-051: Security Awareness Training

Training Program

Target Audience:

  • All ISMS roles (sole operator. handles development, DevOps, security, and product)
  • Contractors with access to backend services (when engaged)

Training Modules:

  1. Secure Coding Practices:
  • Input validation and sanitisation
  • Output encoding
  • XSS prevention
  • CSRF protection
  • Authentication and session management
  1. API Security:
  • OWASP API Security Top 10
  • HMAC signature generation and validation
  • Rate limiting implementation
  • BOLA/IDOR prevention
  • Secure error handling
  1. Cryptography:
  • Key management best practices
  • Encryption at rest and in transit
  • Hash function selection (Argon2id for passwords)
  • Random number generation (CSPRNG)
  1. Incident Response:
  • Security incident classification
  • Reporting procedures
  • Escalation paths
  • Post-incident review process

Training Delivery

Format:

  • Quarterly training sessions (1-2 hours)
  • Annual security awareness day
  • On-demand video modules

Documentation:

  • src/content/trust/security/security-awareness.md
  • src/content/trust/security/roles-responsibilities.md

Tracking:

  • Training completion tracked in HR system
  • Annual refresher required for all team members
  • Security champions program for cross-functional teams

API Security Best Practices

Security Headers

All backend services implement security headers:

provii-verifier

Implementation: provii-verifier/src/security/headers.rs

Default Headers Applied (13 total):

  1. Content-Security-Policy:

    default-src 'none'; script-src 'none'; style-src 'none'; img-src 'none';
    font-src 'none'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none';
    object-src 'none'; form-action 'none'; report-uri /v1/csp-report; report-to csp-endpoint
  2. Report-To: CSP reporting endpoint configuration

  3. Cross origin-Opener-Policy: same-origin

  4. Cross origin-Embedder-Policy: require-corp

  5. Cross origin-Resource-Policy: same-origin

  6. X-Frame-Options: DENY

  7. X-Content-Type-Options: nosniff

  8. X-XSS-Protection: 1; mode=block

  9. Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

  10. Referrer-Policy: no-referrer

  11. Permissions-Policy: accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()

  12. Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0

  13. X-Permitted-Cross-Domain-Policies: none

Additional headers (Pragma: no-cache, Expires: 0) are applied to API-specific endpoints via api_security_headers().

provii-management

Implementation: provii-management/src/middleware/security-headers.ts (lines 35-89)

Headers Applied:

c.header('X-Content-Type-Options', 'nosniff');
c.header('X-Frame-Options', 'DENY');
c.header('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'");
c.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
c.header('Referrer-Policy', 'no-referrer');
c.header('Permissions-Policy',
  'accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), ' +
  'cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), ' +
  // ... 27 directives total (all denied)
);

provii-credit-management

Implementation: provii-credit-management/src/utils/security-headers.ts (lines 23-119)

Dynamic Headers Based on Path Sensitivity:

export function isSensitivePath(path: string): boolean {
  const sensitivePaths = [
    '/v1/credits/balance',
    '/v1/credits/consume',
    '/v1/admin/entities',
    '/v1/admin/transactions',
  ];
  return sensitivePaths.some(sensitive => path.startsWith(sensitive));
}

// If sensitive:
headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, private';
headers['Pragma'] = 'no-cache';
headers['Expires'] = '0';

Input Validation

provii-management

Implementation: provii-management/src/utils/validation.ts

Zod-Based Validation:

export function validateRequest<T>(schema: z.ZodSchema<T>) {
  return async (body: unknown): Promise<
    { success: true; data: T } |
    { success: false; error: string; details: any[] }
  > => {
    try {
      const data = await schema.parseAsync(body);
      return { success: true, data };
    } catch (error) {
      if (error instanceof z.ZodError) {
        const details = error.issues.map((e: z.ZodIssue) => ({
          path: e.path.join('.'),
          message: e.message,
        }));
        return { success: false, error: details[0]?.message ?? 'Validation failed', details };
      }
      return { success: false, error: 'Invalid request body', details: [] };
    }
  };
}

Common Validators:

export const ShortString = z.string().min(1).max(256);
export const MediumString = z.string().min(1).max(1000);
export const LongString = z.string().min(1).max(10000);
export const Email = z.string().email().max(256);
export const PositiveInteger = z.number().int().positive();
export const NonNegativeInteger = z.number().int().min(0);
export const UUID = z.string().uuid();
export const SecureURL = z.string()
  .url()
  .max(500)
  .refine(
    (url) => url.startsWith('https://') || url.startsWith('http://localhost'),
    { message: 'URL must use HTTPS (or HTTP for localhost)' }
  );

provii-verifier

Implementation: provii-verifier/src/types/strict.rs

Strict Newtype Wrappers for Input Validation:

  • B64Url32: Validates base64url-encoded 32-byte values
  • B64Url192: Validates base64url-encoded 192-byte values
  • ShortCode: Validates 12-digit numeric short codes
  • CutoffDays: Validates age in days (-25,000 to 54,750 range, i32)
  • ExpiresIn: Validates time-to-live (30-300 seconds, u64)
  • PkceMethod: Enum accepting only “S256”

CreateChallengeRequest:

#[derive(Debug, Clone, Deserialize)]
pub struct CreateChallengeRequest {
    pub code_challenge: B64Url32,
    pub method: PkceMethod,          // Enum, not free-form String
    pub requested_cutoff_days: Option<CutoffDays>,
    pub verifying_key_id: Option<VkId>,
    pub expires_in: ExpiresIn,
    pub proof_direction: String,
    pub authorizer: Authorizer,
}

CSRF Protection

provii-management Implementation:

Single-Use Token Generation:

// csrf.ts lines 31-49
export async function generateCSRFToken(env: Env, sessionId: string): Promise<string> {
  const token = crypto.randomUUID();
  const key = `${CSRF_TOKEN_PREFIX}${sessionId}:${token}`;

  await env.ADMIN_SESSIONS.put(
    key,
    JSON.stringify({
      session_id: sessionId,
      created_at: Date.now(),
      expires_at: Date.now() + (CSRF_TOKEN_TTL * 1000),
    }),
    { expirationTtl: CSRF_TOKEN_TTL }  // 3600 seconds (1 hour)
  );

  return token;
}

Token Validation and Deletion:

// csrf.ts lines 65-104
// Validates session ownership, checks expiration, deletes after use (single-use pattern)

Middleware Application:

// index.ts line 146
app.use('*', csrfProtection());

CSRF Exemption for HMAC-Authenticated Requests: CSRF checks are skipped for unauthenticated requests (no session), as HMAC authentication provides inherent CSRF protection.

CORS Configuration

provii-management

Strict Origin Validation:

// index.ts lines 112-137
app.use('*', cors({
  origin: (origin) => {
    if (!origin) {
      return '*'; // Service Binding calls (worker-to-worker)
    }

    const allowedOrigins = [
      'https://admin.zerokp.id',
      'https://admin-staging.zerokp.id',
      'http://localhost:5173',
    ];

    if (!allowedOrigins.includes(origin)) {
      throw new HTTPException(403, { message: 'Origin not allowed' });
    }

    return origin;
  },
  credentials: true,
  allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
  allowHeaders: ['Content-Type', 'Authorization', 'X-Admin-Session-ID', 'X-Timestamp', 'X-Signature'],
  maxAge: 86400, // 24 hours
}));

provii-verifier

Origin Policy Enforcement:

  • Origin policies stored in KV namespace (VERIFIER_KV_CONFIG with origin:* key pattern)
  • Only approved origins can create challenges
  • HMAC authentication required for challenge creation and redemption endpoints
  • Public GET endpoints (challenge lookup, JWKS) do not require HMAC

Evidence Artifacts

Configuration Files

provii-verifier:

  • provii-verifier/wrangler.toml. Cloudflare Workers configuration
  • provii-verifier/src/security/headers.rs. Security headers implementation
  • provii-verifier/src/security/auth.rs. HMAC authentication (ClientAuthenticator)
  • provii-verifier/src/durable_objects/nonce_do.rs. Nonce replay prevention

provii-issuer:

  • provii-issuer/wrangler.toml. Cloudflare Workers configuration
  • provii-issuer/src/security.rs. HMAC verification, API key auth

provii-management:

  • provii-management/wrangler.toml. Cloudflare Workers configuration
  • provii-management/src/index.ts. Main application with middleware stack
  • provii-management/src/middleware/auth.ts. HMAC + session authentication
  • provii-management/src/middleware/security-headers.ts. Security headers
  • provii-management/src/middleware/csrf.ts. CSRF protection
  • provii-management/src/middleware/audit.ts. Audit logging
  • provii-management/src/utils/validation.ts. Input validation

provii-credit-management:

  • provii-credit-management/wrangler.toml. Cloudflare Workers configuration
  • provii-credit-management/src/utils/security-headers.ts. Security headers
  • provii-credit-management/src/utils/validation.ts. Input validation

Documentation

Security Policies (in provii-verifier/docs/security/):

  • RATE_LIMITING.md (41 KB)
  • AUTHENTICATION_PATHWAYS.md (44 KB)
  • DATA_CLASSIFICATION.md (64 KB)
  • CRYPTOGRAPHIC_INVENTORY.md (85 KB)
  • KEY_MANAGEMENT_POLICY.md (51 KB)
  • VALIDATION_RULES.md (103 KB)

Compliance:

  • src/content/trust/compliance/requirements/unified-control-matrix.md. Control matrix
  • src/content/trust/compliance/requirements/evidence-mapping.md. Evidence mapping

Monitoring and Alerting

Audit Logs:

  • KV namespace: MANAGEMENT_AUDIT_LOGS
  • Retention: 90 days (configured in wrangler.toml and audit.ts); critical security event logs are retained for up to 365 days
  • Format: JSON structured logs (AuditEvent interface)

Workers Logs in Grafana Loki:

  • Real-time request metrics derived from structured JSON log lines
  • HTTP status code distribution
  • Geographic distribution
  • Rate limit violation trends

Control Mapping Summary

ControlDescriptionStatusEvidence Location
UC-044Encryption in Transit (TLS)✅ ImplementedCloudflare Universal SSL, HSTS headers in all services
UC-045Authentication Mechanisms✅ ImplementedHMAC-SHA256 (provii-verifier, provii-issuer, provii-management), JWT (provii-management), API keys (provii-issuer)
UC-046Authorisation Controls✅ ImplementedRBAC (provii-management), BOLA protection (provii-verifier), ownership verification
UC-047Incident Response✅ ImplementedAudit logging to KV, incident response playbooks
UC-048Vulnerability Management✅ Implementednpm audit, cargo audit, Dependabot, Semgrep, Trivy, Gitleaks
UC-049Security Monitoring✅ ImplementedRate limiting via per-service KV counters, Cloudflare Workers Logs (Grafana Loki)
UC-050Penetration Testing🟡 PlannedAnnual external pentest, responsible disclosure programme
UC-051Security Awareness Training✅ ImplementedQuarterly training, annual security awareness day, security champions program

Additional API Security Controls:

  • Security Headers (13 headers on provii-verifier, on all services)
  • Input Validation (Zod schemas for TypeScript, strict newtype wrappers for Rust)
  • CSRF Protection (Single-use tokens with session binding)
  • CORS Configuration (Strict origin validation)
  • Secret Management (Cloudflare Secrets Store)
  • Cryptographic Agility (Documented in CRYPTOGRAPHIC_INVENTORY.md)

Recommendations

Short-Term (1-3 months)

  1. Implement CSP Reporting Dashboard:
  • Store CSP violation reports in KV/DO
  • Build visualisation dashboard
  • Set up alerting for unusual patterns
  1. Enhance Rate Limit Monitoring:
  • Deploy Grafana dashboards
  • Implement anomaly detection algorithms
  • Create runbooks for common attack patterns

Medium-Term (3-6 months)

  1. External Penetration Test:
  • Engage third-party security firm
  • Focus on OWASP API Security Top 10
  • Validate ASVS Level 3 compliance claims
  1. Automated Security Testing:
  • Integrate OWASP ZAP into CI/CD
  • Implement API fuzzing with RESTler
  • Set up continuous security scanning
  1. Advanced Threat Detection:
  • Deploy machine learning-based anomaly detection
  • Implement behavioural analysis for authentication attempts
  • Create automated incident response workflows

Long-Term (6-12 months)

  1. Post-Quantum Cryptography Migration:
  • Follow PQC migration roadmap
  • Implement hybrid cryptography (classical + PQC)
  • Monitor NIST PQC standardisation progress
  1. Zero Trust Architecture:
  • Implement mutual TLS for service-to-service communication
  • Deploy service mesh for microservices
  • Enhance identity verification at every layer
  1. Security Certification:
  • Pursue SOC 2 Type II certification
  • Pursue ISO 27001 certification when commercially justified
  • Complete PCI DSS compliance (if accepting card payments)

Conclusion

The Maelstrom AI backend infrastructure demonstrates a defence-in-depth approach to API security. Controls are implemented across all services and are designed to provide protection against common API vulnerabilities. The combination of Cloudflare’s edge security, HMAC authentication, RBAC authorisation, per-service KV counter rate limiting, and security headers is designed to address common API attack vectors.

Compliance Status:

  • OWASP API Security Top 10 (2023): Controls implemented across all services
  • OWASP ASVS 5.0.0 Level 3: Hardening fixes applied to provii-verifier
  • NIST SP 800-63B (Authentication): HMAC + PKCE authentication implemented
  • GDPR (Data Protection): Audit logging and data minimisation designed to support GDPR obligations

Evidence Collection Date: 2026-05-21 Next Review: 2026-11-21 (Quarterly) Document Owner: Security Lead Approver: ISMS Owner


Document Classification: Public Review Schedule: Quarterly Version: 2.0