This issue covers Phase 1 only: the audit event pipeline and local storage. SIEM export (OTel log signal, Splunk HEC, syslog/CEF) is tracked separately.
What gets built
- AuditEvent struct
pub struct AuditEvent {
pub id: u64,
pub timestamp: OffsetDateTime,
pub operation: String, // e.g. "Encrypt", "Create", "Destroy"
pub user: String, // from AuthenticatedUser or "unauthenticated"
pub object_uid: Option<String>,
pub algorithm: Option<String>,
pub client_ip: Option<String>,
pub result: AuditResult, // Success | Failure(code)
pub duration_ms: u64,
pub prev_hash: [u8; 32], // SHA-256 of previous event canonical bytes
pub row_hash: [u8; 32], // SHA-256 of this event (excluding row_hash)
}
- Audit middleware: crate/server/src/middlewares/audit.rs
- Follows the EnsureAuth structural pattern (Transform + Service)
- Registered as the outermost .wrap() on default_scope in start_kms_server.rs
- Reads AuthenticatedUser from request extensions (falls back to "unauthenticated" for 401s)
- Captures response status after service.call(req).await to determine outcome
- Feeds completed AuditEvent into the configured sinks
- [audit] config section :
text
[audit]
enabled = true
[audit.file]
enabled = true
path = "/var/log/cosmian_kms/audit.log" # default if not set
[audit.db]
enabled = false
url = "postgresql://audit_writer:secret@localhost/kms_audit"
[audit.otlp], [audit.splunk_hec], [audit.syslog] slots are reserved but not implemented here.
-
Storage backends
This isn't a strict requirement yet - to be researched
Current proposition :
[audit.file] — always-on default, append-only JSONL file, hash-chained. Zero external dependencies. Every deployment gets a working audit trail with no config required.
[audit.db] — optional. SQLite or PostgreSQL, separate from the key DB. KMS connects with a write-only credential (INSERT only). ckms audit export uses a separate read-only credential.
-
ckms audit CLI subcommands
ckms audit export --since <date> --format json|cef # reads from the local audit store
ckms audit verify # walks the hash chain, reports first broken link
Out of scope for this issue
- OTel log signal export
- Splunk HEC export
- Syslog/CEF export
- auth_method field on AuthenticatedUser (noted as a possible future extension)
Acceptance criteria
Schema & payload safety
Runtime behavior
Storage & tamper-evidence
CLI & export
Configuration & compliance defaults
Other info
Write AuditEvent to a dedicated local store (Postgres/SQLite/MySQL, separate from key storage)
Hash chaining for tamper-evidence
ckms audit export --since <date> --format json|cef reads from this local store
This alone satisfies most of the compliance requirement: you have a durable, tamper-evident, user-attributed record of every key operation
What is marked in bold is not a suggestion but a requirement for this issue approval - not satisfying that can fail the Audit phase in the future
It's also mandatory (for audit compliance) that the KMS is proven to ONLY write logs, and not backward tamper with them
This issue covers Phase 1 only: the audit event pipeline and local storage. SIEM export (OTel log signal, Splunk HEC, syslog/CEF) is tracked separately.
What gets built
Storage backends
This isn't a strict requirement yet - to be researched
Current proposition :
[audit.file] — always-on default, append-only JSONL file, hash-chained. Zero external dependencies. Every deployment gets a working audit trail with no config required.
[audit.db] — optional. SQLite or PostgreSQL, separate from the key DB. KMS connects with a write-only credential (INSERT only). ckms audit export uses a separate read-only credential.
ckms audit CLI subcommands
ckms audit export --since <date> --format json|cef # reads from the local audit storeckms audit verify # walks the hash chain, reports first broken linkOut of scope for this issue
Acceptance criteria
Schema & payload safety
AuditEventstruct captures: who (user), what (operation), when (timestamp), where (client_ip), on what (object_uid), and with what outcome (result,duration_ms) — all fields must be populated or explicitlyNonewith a documented reasonRuntime behavior
default_scopeproduce anAuditEventuser = "unauthenticated"and the client IPERRORin diagnostic logs but does not return an error to the clientStorage & tamper-evidence
[audit.db]and the key DB are demonstrably separate — different connection strings in test setupckms audit verifyckms audit verifyexits non-zero and identifies the broken link when any past event is modified or deletedCLI & export
ckms audit export --format jsonproduces valid, parseable JSONckms audit export --format cefproduces valid CEF-formatted lines on stdoutckms audit exportoutput must not expose more personal data than what was logged — no enrichment, no joins against user directories at export time (GDPR/RGPD compliance)Configuration & compliance defaults
[audit]section in config — audit is disabled by default, no error[audit] enabled = truewith no further config, KMS writes to a stable default path without error[audit.otlp],[audit.splunk_hec],[audit.syslog]sub-tables are reserved in the config schema — Phase 2 can add them without breaking existing configsOther info
Write AuditEvent to a dedicated local store (Postgres/SQLite/MySQL, separate from key storage)
Hash chaining for tamper-evidence
ckms audit export --since <date> --format json|cefreads from this local storeThis alone satisfies most of the compliance requirement: you have a durable, tamper-evident, user-attributed record of every key operation
What is marked in bold is not a suggestion but a requirement for this issue approval - not satisfying that can fail the Audit phase in the future
It's also mandatory (for audit compliance) that the KMS is proven to ONLY write logs, and not backward tamper with them