Keystne
Platform Hubs Architecture How it works
Docs Try the demo
Home/Legal/Incident Response
Privacy Policy Terms of Service POPIA Compliance Incident Response

Security Incident & Personal-Information-Breach Response Procedure

Accuracy notice. Sections 2, 3 and 5 were rewritten on 2026-08-04 against the Go implementation. The previous version listed five detection sources as implemented, of which four did not exist, and instructed responders to query auth_events / query_audit_events tables and rotate per-tenant ClickHouse credentials — none of which exist. Every source reference now resolves to a real file, enforced by frontend/src/__tests__/docsSources.test.ts.

Owner: Information Officer, Cognizance Processing (Pty) Ltd Last updated: June 2026 · Version: 1.0 Statutory basis: POPIA s22 (notification of security compromises), s19 (security safeguards), s21 (operator duties).

This procedure is grounded in KeyOne’s actual architecture. It does not invent capabilities the system lacks; where a control is a deployment requirement rather than an implemented feature, it is marked [deploy].


1. Scope

A “security compromise” (POPIA s22) is any event giving reasonable grounds to believe that personal information has been accessed or acquired by an unauthorised person. This covers KeyOne acting as responsible party (our users/employees) and as operator (customer-uploaded data).

2. Detection sources

Checked against the Go implementation on 2026-08-04. The previous version of this table was headed “implemented” and listed five sources, of which four described the retired Python stack and do not exist in KeyOne. An incident response plan whose detection sources are fictional fails at exactly the moment it is needed, so what is actually available is separated from what is not.

Available today:

SourceWhere
Authentication eventsLogin success and failure, refresh-token reuse (distinct from an ordinary rejected token), logout, TOTP enrolment/verification/failure, password-reset requests and invite redemption — all written to audit_log with target_type = 'auth', the client IP, and a server-side failure reason the client never sees (core/internal/api/authaudit.go). Failed logins against addresses with no account are recorded too.
Refresh-token reuseDetected on rotation and revokes the whole token family; detection is sticky and is not rolled back by a later legitimate refresh (core/internal/auth/refresh.go), and now leaves an auth.refresh_reuse row.
Action audit trailaudit_log records who did what to which object, under RLS, readable through the ops surface (migration 0014, core/internal/ops/auditlog.go). Scope is decision actions.
Application logsStructured server logs from the single Go binary, via the platform’s log drain.
Database-level controlsPostgres row-level security FORCEd on 32 tables, exercised by TestRLS_TenantIsolation (core/internal/store/rls_test.go). Prevents cross-tenant reads; does not itself report attempts.

Not available (previously listed here as implemented):

Claimed sourceActual position
auth_events table with login_failed / refresh_reuseNo such table, and there never was. The events themselves are now recorded — in audit_log under target_type = 'auth', see above.
query_audit_events data-access trailNo such table. Query execution is not audit-logged.
Per-tenant ClickHouse user + isolation verificationKeyOne has no ClickHouse. Isolation is Postgres RLS only.
Production fail-closed on default secrets (SEC-K01)Config validation does not check for default secrets (core/internal/config/validate.go).
Inbound data-platform alerts (watch_breach / freshness_stale / query_failed)No such webhook or event path exists in the Go tree.

What this means for response. An authentication trail now exists, so a credential compromise can be reconstructed: which addresses were attempted, from which IP, whether any succeeded, and whether a refresh token was replayed. Two limits are real and must be stated. Nothing alerts — the rows are only found by someone looking. And the trail starts from the deploy that added it; it says nothing about anything earlier.

3. Response stages

  1. Detect & record — capture the audit_log rows for the window (target_type = 'auth' for authentication events, plus any action rows) and the application logs. Where the trail cannot answer something — anything before the trail existed, or any question about which data was read, which is still not recorded — say so explicitly in the incident record rather than letting “no evidence” read as “no access”.
  2. Contain — KeyOne is a single-tenant binary: one deployment serves one customer, so a credential compromise is bounded by that deployment and cannot reach another customer’s data. Containment actions: revoke the affected user’s sessions (refresh-token revocation revokes the whole family), rotate the relevant secret, and rotate the JWT signing key in signing_keys if token forgery is suspected.
  3. Assess scope — enumerate what can be established from audit_log and application logs, and state plainly what cannot be established because it is not recorded (data-access events are still not audited). POPIA permits the time reasonably necessary to determine scope before notifying (s22(2)).
  4. Notify (see §4).
  5. Remediate & learn — fix root cause, add a regression test, record the incident in the security register (SECURITY_REGISTER.md).

4. Notification obligations (POPIA-accurate)

RecipientTriggerTimingBasis
Information RegulatorCompromise of personal information for which KeyOne is the responsible partyAs soon as reasonably possible after discovery, allowing time to determine scope and restore system integrityPOPIA s22(1)–(2) — no fixed hour count
Affected data subjectsSame as above, unless their identity cannot be establishedAs soon as reasonably possible (may be delayed only if law enforcement/Regulator directs)POPIA s22(1)
Affected customers (when KeyOne is the operator of their data)Compromise affecting Customer DataWithin 72 hours of discovery — a contractual SLA, not the statutory standardOperator duty (s21) + DPA

Regulator contact: Information Regulator of South Africa, JD House, 27 Stiemens Street, Braamfontein, Johannesburg · inforeg@inforegulator.org.za · inforegulator.org.za.

Notifications to data subjects must, per s22(5), describe the possible consequences, the measures KeyOne intends/took, recommended protective steps, and (if known) the identity of the unauthorised person.

5. Known hardening items (must close before processing real personal information)

Verified against the Go tree on 2026-08-04. The previous version of this list opened with “TOTP secrets — FIXED”, describing Fernet envelope encryption in a Python file that no longer exists, with a contradicting correction spliced into the middle of the same bullet. Both halves are replaced by the actual position.

  • No field-level encryption. core/internal/store/crypto.go provides an AES-256-GCM encryptor for sources.credentials_ciphertext, but nothing calls it. TOTP secrets are stored in plaintext in the totp_secret column. Stored data is protected only by the database volume and by row-level security. A KEYONE_FIELD_ENCRYPTION_KEY named in earlier revisions does not exist.
  • No alerting. The authentication trail is recorded but nothing watches it. A credential-stuffing run produces rows and no notification; someone has to look. This is now the largest gap in this procedure.
  • The trail has no retention policy. audit_log grows without bound and nothing prunes it. Under POPIA’s retention limits this needs a decision before real personal information is processed.
  • Encryption at rest (storage volumes) relies on the host platform’s encrypted volumes — provision encrypted Postgres and object storage in africa-south1. [deploy]

Two documents cited here in earlier revisions, HONESTY_AUDIT.md and SECURITY_REGISTER.md, do not exist in this repository. The register referenced in §3 stage 5 is likewise not a file that exists; recording incidents somewhere durable remains an organisational commitment, not an implemented artefact.

Keystne

From raw retail data to shelf-level action — one commercial cockpit, ranked decision hubs, closed-loop execution.

Platform

Decision Engine Decision Hubs Architecture How it works Live demo

Company

Documentation Why KeyOne

Legal

Privacy Terms POPIA Incident response
© 2026 Cognizance Processing (Pty) Ltd t/a KeyOne. All rights reserved. South Africa · hello@keyone.sh