DocsSecurity & governance

Compliance, audit & data governance

An append-only audit log, a pull-based SIEM export, GDPR self-service, and per-organization retention.

Audit log#

Every privileged mutation across the entire platform — permission and role changes, deletions, settings changes, sign-ins, token creation, impersonation — is written to a single append-only AuditLog table via one shared AuditService.log() call site convention, with a consistent shape:

ts
{ actorId, organizationId?, repositoryId?, action, resourceType, resourceId?, ipAddress?, metadata? }

action is a stable, namespaced string (e.g. repo_file_template.applied, org_template.applied, access_token.created) rather than a free-text description, so entries are reliably filterable and groupable by exactly what happened, not just by who did it. Because logging goes through one shared call rather than being reimplemented ad hoc per feature, adding audit coverage to a new mutation is a one-line addition at the point of the mutation itself — which is why every feature shipped on the platform, including newly added ones, is covered without special-casing.

Organization admins can view their own organization's log (Organizations → Audit, gated on ORG_VIEW_AUDIT_LOG); site admins can view the instance-wide log.

SIEM export#

Rather than pushing events to an external system, KHUB exposes a pull-based, cursor-paginated export endpoint for security tooling to poll:

text
GET /api/siem/audit-events?cursor=<opaque>&limit=<1-1000>
Authorization: Bearer khub_siem_...

Each response returns a page of raw audit-log rows in creation order plus a nextCursor (an opaque, base64url-encoded {createdAt, id} position) to resume from — a stable, gap-free pagination scheme even under concurrent writes, since the cursor compares on (createdAt, id) rather than an offset. Authenticated with a separate, revocable SIEM token (khub_siem_..., created under Admin → SIEM export), distinct from both personal access tokens and SCIM tokens, so a compromised log-shipper credential can be revoked without touching any other integration.

GDPR data rights#

A user can request an export of their own data (Settings → Security), returned as a downloadable JSON document assembled from every table referencing that user — profile, memberships, authored content metadata, and audit entries attributed to them. Account deletion is self-service but intentionally guarded: a request is rejected with an explicit, actionable error if the account still has content other records depend on (issues, pull requests, comments, authored chat messages) that would otherwise need to silently lose attribution or cascade-delete shared history — those must be reassigned or removed first, which keeps deletion an explicit, reviewable act rather than one that can quietly take other people's referenced content with it.

Retention policies#

See Package & container registries for the exact knobs (CI artifacts, package version count, untagged OCI manifests) — retention is configured per organization and swept on a schedule, independent of GDPR-driven deletion, which operates on a specific user's data rather than age-based bulk cleanup.

Instance security policy#

Site admins set instance-wide floors under Admin → Security policy — minimum password strength, mandatory two-factor authentication, maximum session lifetime, and which visibility levels are permitted at all on this instance. These are floors, not defaults: an organization can be stricter than the instance policy but never looser.