Skip to main content

Security Reality Audit

Trust Architecture Certification — 9/12/2026

62%

Overall Trust Architecture Score

8 PASS2 PARTIAL3 FAIL

Feature Certification

Protection Score / Trust Engine

identityTrustEngine computes from 9 real sources. SecurityDashboard now prefers canonical score.

PASS

Trusted Devices — view

Real entity reads. Device ID, platform, revoke all work.

PASS

Trusted Devices — location

Location field never populated. No IP geolocation implemented.

FAIL

Active Sessions — view

registerSession() writes Session entity on every login. session_id bound client-side.

PASS

Active Sessions — revocation

Konekt internal session guard blocks revoked sessions. Base44 token remains platform-managed — no token blacklist API exists.

PARTIAL

Login History

Session entity written on every login via securityEventBus.create_session.

PASS

Security Alerts — preferences

Full per-type per-channel toggle backed by real entity.

PASS

Security Alerts — delivery

Preferences stored, never dispatched. No delivery pipeline.

FAIL

Risk Center — ATO/SessionRisk

Real entity reads from attackResponseEngine.

PASS

Risk Center — fraud auto-detection

Impossible travel, rapid device switching: not computed.

FAIL

Recovery Health

Backed by PhoneVerificationStatus + EmailVerificationStatus + RecoveryMethod.

PASS

Account Timeline — events

All events read from real entity fields with real timestamps.

PASS

Account Timeline — immutability

Reconstructed from mutable fields, not append-only log.

PARTIAL

Critical Gaps to Fix

PLATFORM LIMITATION: Base44 auth token cannot be server-side invalidated — no token blacklist API exists. Konekt internal session guard (KonektSessionGuard + konektSessionGuardEngine) blocks revoked sessions across all protected routes and financial actions. Base44 token remains platform-managed.

Approximate location is never populated — Session.location_approx always null.

Fraud auto-detection not implemented — impossible travel, device switching: manual only.

Confirmed Real Features

identityTrustEngine: 9-signal trust computation with policy-driven weights and hard-stops.

Protection Score: all 7 factors backed by real entity writes (phone, email, PIN hash, bio, KYC).

PIN: SHA-256(pin+userId) hash stored in SecurityPreference. Never plaintext. Verified server-side.

SecurityAuditLog: logSecurityEvent() called on all security actions (pin change, device revoke, etc.).

KonektSession binding: session_id stored in sessionStorage on every login. Bound to device fingerprint.

KonektSessionGuard: wraps every PhoneVerificationGate-protected route. Checks session validity on load + 45s heartbeat.

Financial action guard: requireActiveSession() blocks revoked sessions from initiating money movement.

Session heartbeat: 45s interval updates last_seen_at and detects revocations in real-time.

Sign out all devices: revokes all Session records, immutable log, critical-severity alert delivered.

Trusted device removal cascade: removes TrustedDevice + revokes all bound Sessions + delivers alert.

Alert delivery: SecurityEventBus delivers in-app + email alerts for all high/critical security events.

Recovery methods: RecoveryMethod entity with verified status, identityTrustEngine reads it.

ATO/SessionRisk events: attackResponseEngine queries real AccountTakeoverEvent + SessionRiskEvent.

Detailed Phase Audit

Phone Verified (+20pts)

REAL

Written by otpEngine on OTP success. Queried by accountIdentityEngine.buildAccountState() and identityTrustEngine.scorePhone().

Source: PhoneVerificationStatus.verified

Email Verified (+10pts)

REAL

Written by emailVerificationEngine on OTP/magic-link success. identityTrustEngine.scoreEmail() reads it.

Source: EmailVerificationStatus.verified

PIN Configured (+15pts)

REAL

pinPersistence.savePinHash() writes SHA-256(pin+userId) to SecurityPreference. accountIdentityEngine cross-checks both pin_set=true AND pin_hash present to prevent false positives.

Source: SecurityPreference.pin_set + pin_hash

Biometrics Enabled (+10pts)

REAL

markBiometricSeen() in accountIdentityEngine writes both flags. biometricAuth.js stores device credential. SecurityPreference is source of truth.

Source: SecurityPreference.biometric_enabled + biometric_setup_seen

Trusted Device (+10pts)

PARTIAL

TrustedDevice records exist and are read. However, device linking is currently manual (ensureCurrentDevice auto-registers on load). No server-side assertion on login.

Source: TrustedDevice entity

⚠ Gap: Device trust is not asserted at authentication time — only at screen load.

Recovery Method (+5pts)

REAL

AccountRecovery screen writes RecoveryMethod records. identityTrustEngine.scoreRecovery() evaluates both phone+email verified AND RecoveryMethod.verified.

Source: RecoveryMethod.verified

Identity Verified (+30pts)

REAL

accountIdentityEngine.buildAccountState() applies a 3-layer check: ISM snapshot → KYCVerificationStatus → KYCCase. identityTrustEngine.scoreKYC() reads face_match_verdict and liveness_passed for document quality.

Source: KYCCase.status + KYCVerificationStatus + IdentityStateSnapshot

Score uses identityTrustEngine

REAL

SecurityDashboard.jsx now prefers canonical trust engine score (5-min TTL cache, auto-recomputes if stale). Falls back to local computation only if trust engine unreachable.

Source: identityTrustEngine.get_trust

Device ID collected

REAL

getDeviceId() generates a stable UUID from browser fingerprint stored in localStorage. Written to TrustedDevice and SecurityAuditLog on every security event.

Source: biometricAuth.getDeviceId()

Platform detected

REAL

getPlatform() returns ios/android/web from navigator.userAgent. Stored on TrustedDevice record.

Source: biometricAuth.getPlatform()

Device Name

PARTIAL

Auto-generated as "iPhone — This device". User cannot rename device in current UI.

Source: TrustedDevice.device_name

⚠ Gap: Rename device action not yet implemented in TrustedDevices screen.

Last Seen

REAL

updated_date is maintained by the entity layer on every update. relativeTime() utility formats it.

Source: TrustedDevice.updated_date

Approximate Location

FAKE

No IP geolocation is performed. Session.location_approx field exists but is never populated by any backend function.

Source: None

⚠ Gap: Location is never populated. Consider enriching from IP on login.

Untrust / Revoke

REAL

handleRevoke() sets trusted=false, biometric_enabled=false, revoked_at, and writes a SecurityAuditLog entry. Device is excluded from future trusted checks.

Source: TrustedDevice.trusted=false + logSecurityEvent()

Sign Out Device

PARTIAL

Revoking a device sets trusted=false in the entity, but does NOT explicitly invalidate the auth token for that device.

Source: TrustedDevice.trusted=false

⚠ Gap: No server-side session token revocation on device untrust.

Sessions tracked

REAL

registerSession() is called on every successful login (SignIn.jsx). It invokes securityEventBus.create_session, which creates a real Session entity record. session_id is stored in sessionStorage via storeSessionId().

Source: Session entity + registerSession()

Sign Out Session

REAL

handleRevokeOne() calls securityEventBus.revoke_session which updates Session.status=revoked, writes immutable SecurityAuditLog, and delivers in-app + email alert.

Source: Session.status=revoked + securityEventBus

Sign Out All Others

REAL

handleRevokeAll() calls securityEventBus.revoke_all_sessions. All sessions except current are revoked in the entity. Immutable log + critical-severity alert delivered.

Source: Session.status=revoked (loop) + alert

Base44 token revocation

FAKE

PLATFORM LIMITATION: Base44 auth tokens are platform-managed and cannot be server-side invalidated. No token blacklist API is exposed. Konekt cannot revoke the underlying JWT/session token.

Source: Platform limitation — no Base44 token blacklist API

⚠ Gap: Base44 token remains valid after session revocation. Konekt compensating controls (below) block access at the Konekt layer.

KonektSession compensating control

REAL

Every login binds a session_id (stored in sessionStorage). KonektSessionGuard wraps every protected route via PhoneVerificationGate. On each navigation, the guard checks Session.status via konektSessionGuardEngine. Revoked sessions are redirected to /signin with a security message.

Source: lib/konektSession.js + konektSessionGuardEngine

Route-level session guard

REAL

KonektSessionGuard is composed inside PhoneVerificationGate, which wraps every user-facing protected route in App.jsx. Auth check + Konekt session check + heartbeat all run at route level.

Source: KonektSessionGuard.jsx → PhoneVerificationGate

Financial action guard

REAL

requireActiveSession() is available for all money-movement flows (send, cash-out, QR, agent, etc.). Calls konektSessionGuardEngine.require_active. Throws BlockedBySessionError if session is revoked. Logs permission_denied security event.

Source: lib/financialSessionGuard.js → requireActiveSession()

Session heartbeat (45s)

REAL

Heartbeat runs every 45 seconds while app is active. Updates last_seen_at on Session entity. Checks revocation status. If revoked is detected mid-session, force-logs out and redirects to /signin.

Source: lib/konektSession.js → startSessionHeartbeat()

Trusted device removal cascade

REAL

Removing a trusted device calls securityEventBus.revoke_device which revokes all Session records bound to that device_id, updates TrustedDevice.trusted=false, writes immutable log, and delivers high-severity alert.

Source: securityEventBus.revoke_device

Login events stored

PARTIAL

LoginHistory reads Session entity filtered by user. Sessions are visible if populated.

Source: Session entity

⚠ Gap: No confirmed automatic session creation on login. Login history may be empty for most users until sessions are explicitly created.

Success/failure status

PARTIAL

Sessions with status="failed" or "revoked" render as failed. Only valid if sessions were written with correct status.

Source: Session.status

SecurityAuditLog for login

PARTIAL

EVENT_LABELS defines login_success and login_failed event types. securityHub.logSecurityEvent() can write them. But no confirmed call site in the auth flow that writes these automatically.

Source: SecurityAuditLog (login_success, login_failed)

⚠ Gap: SignIn.jsx does not call logSecurityEvent() on auth success/failure.

Alert preferences stored

REAL

SecurityAlerts screen reads/writes SecurityAlertPreference per user. loadOrCreateAlertPreference() ensures record exists. Toggle writes immediately.

Source: SecurityAlertPreference entity

PIN change alert

PARTIAL

logSecurityEvent() is called with "pin_changed" from ChangePin. Record written to SecurityAuditLog. But the actual delivery (push/SMS/email) of alert is not implemented.

Source: SecurityAuditLog (pin_changed)

⚠ Gap: Alert preferences are stored but alerts are never dispatched to the user.

New device login alert

PARTIAL

device_trusted event is logged. SecurityAlertPreference has login_new_device_push/sms/email fields. Delivery engine not implemented.

Source: TrustedDevice + SecurityAuditLog

⚠ Gap: Same gap: preferences exist, dispatch does not.

Suspicious activity alert

PARTIAL

attackResponseEngine creates AttackEvent and AttackAlert for admin. User-facing alert delivery not implemented.

Source: AttackEvent / AccountTakeoverEvent

Large transaction alert

FAKE

Preference field exists. moneyFlowEngine does not check alert prefs or dispatch alerts on large transactions.

Source: SecurityAlertPreference.large_transaction_push

⚠ Gap: Not wired to any transaction pipeline.

AccountTakeoverEvent

REAL

attackResponseEngine.getMySecurityEvents() filters AccountTakeoverEvent by user_id. RiskCenter and SecurityDashboard read these.

Source: AccountTakeoverEvent entity

SessionRiskEvent

REAL

Same action returns SessionRiskEvent records. Both entity reads are real.

Source: SessionRiskEvent entity

ContinuousRiskScore

REAL

getMySecurityEvents returns risk_score from ContinuousRiskScore. accountIdentityEngine.getGlobalState() also reads it and factors it into trust computation.

Source: ContinuousRiskScore entity

Impossible Travel detection

FAKE

No backend function computes impossible travel from login IP sequences. AttackEvent must be manually created by admin.

Source: None

⚠ Gap: Fraud detection signals (impossible travel, rapid device switching) are not automatically generated.

Failed PIN attempt tracking

PARTIAL

SecurityPreference has failed_pin_attempts and pin_locked_until fields. securityEnforcementEngine increments them on verify. RiskCenter does not display this value.

Source: SecurityPreference.failed_pin_attempts

⚠ Gap: The field exists and is written, but RiskCenter does not surface it.

Risk level computed

PARTIAL

RiskCenter derives risk level from ATO events severity, not from ContinuousRiskScore directly. ContinuousRiskScore is read in accountIdentityEngine but not in RiskCenter.

Source: ContinuousRiskScore.overall_score

⚠ Gap: RiskCenter should use ContinuousRiskScore.overall_score as primary input.

Recovery email

REAL

accountIdentityEngine reads EmailVerificationStatus. identityTrustEngine.scoreRecovery() factors it in. AccountRecovery screen reads RecoveryMethod.

Source: EmailVerificationStatus.verified

Recovery phone

REAL

PhoneVerificationStatus is the primary phone truth source. Verified status is used in both trust engine and routing engine.

Source: PhoneVerificationStatus.verified

Recovery method health score

PARTIAL

AccountRecovery shows verified/unverified state. No composite "Recovery Health Score" (0-100) is computed or displayed.

Source: RecoveryMethod entity

⚠ Gap: Recovery health is binary (has/does not have), not a scored metric.

Account Created

REAL

Read from base44.auth.me().created_date. Immutable platform field.

Source: User.created_date

Phone Verified

REAL

Read directly from entity. verified_at is set by otpEngine.

Source: PhoneVerificationStatus.verified_at

PIN Created

REAL

pinPersistence.savePinHash() writes pin_set_at on creation.

Source: SecurityPreference.pin_set_at

Email Verified

REAL

emailVerificationEngine sets verified_at when verification succeeds.

Source: EmailVerificationStatus.verified_at

Biometrics Enabled

REAL

markBiometricSeen() writes biometric_seen_at.

Source: SecurityPreference.biometric_seen_at

KYC Submitted / Approved

REAL

KYCCase is queried by created_by=user.email. Both submission and review timestamps exist.

Source: KYCCase.created_date / reviewed_at

Events are immutable

PARTIAL

Timeline is reconstructed from entity fields on every load — not from an append-only event log. A corrective update to SecurityPreference would silently change the timeline.

Source: Entity records

⚠ Gap: Timeline should read from SecurityAuditLog for immutability. Currently reads mutable entity fields.