Author: Olivier Vitrac, PhD, HDR | olivier.vitrac@adservio.fr | Adservio Version: 1.0.0 Date: 2026-02-13 RAGIX Version: 0.66+ KOAS Version: 1.0
- Introduction
- Design Principles
- Event Schema
- Actor Model
- Event Types
- Sovereignty Attestation
- Hash Chain Integrity
- Querying Events
- Broker Integration
- Configuration
- API Reference
- Related Documentation
KOAS Activity Logging provides a centralized, auditable governance layer for all kernel and LLM operations. It answers:
- Which kernels ran, in which order, and why?
- Which LLM calls were triggered, reused from cache, or replayed offline?
- Who (or what) initiated the workflow: internal process, operator, external orchestrator?
- Can we prove that all processing happened locally?
The system logs what happened, not what was processed.
| Logged | NOT Logged |
|---|---|
| Kernel name, version, stage | Document content |
| Execution timestamps | Raw text excerpts |
| Input/output hashes | File contents |
| Decisions (cache hit/miss) | Prompt/response text |
| Actor identity | Personal data |
| Metrics (counts, scores) | Internal paths (in external view) |
All activity flows into one append-only file:
.KOAS/activity/events.jsonl <- canonical source
.KOAS/activity/exports/ <- audit bundles (generated on demand)
Source: ragix_kernels/activity.py (731 lines)
| Principle | Mechanism |
|---|---|
| Prove local execution | sovereignty.local_only: true in every event |
| Track who did what | Actor field with type + auth method |
| Enable audits | Structured events + append-only stream |
| Detect misuse | Centralized stream + rate limiting (broker) |
| No content leakage | Only hashes, metrics, and decisions recorded |
| Forward compatibility | Schema versioned as koas.event/1.0 |
Every event is a self-contained JSON envelope:
{
"v": "koas.event/1.0",
"ts": "2026-01-30T20:50:11.142+00:00",
"event_id": "a7b3c4d5-e6f7-8901-2345-67890abcdef0",
"run_id": "run_20260130_215011_348bc4",
"actor": {
"type": "system",
"id": "koas",
"auth": "none"
},
"scope": "docs.kernel",
"phase": "end",
"kernel": {
"name": "doc_metadata",
"version": "1.0.0",
"stage": 1
},
"decision": {
"success": true,
"cache_hit": false
},
"metrics": {
"duration_ms": 42,
"item_count": 79
},
"sovereignty": {
"local_only": true
}
}| Field | Type | Required | Description |
|---|---|---|---|
v |
string | Yes | Schema version (koas.event/1.0) |
ts |
ISO 8601 | Yes | UTC timestamp with milliseconds |
event_id |
UUID v4 | Yes | Unique event identifier |
run_id |
string | Yes | Groups events within a single pipeline run |
actor |
object | Yes | Who/what initiated the action |
scope |
string | Yes | Event category (e.g., docs.kernel, docs.llm) |
phase |
string | No | Phase within scope (start, end, cache_hit) |
kernel |
object | No | Kernel name, version, stage (for kernel events) |
node_ref |
object | No | Document hierarchy reference (level, id) |
io |
object | No | Input/output hashes for provenance |
decision |
object | No | Decision metadata (cache hit/miss, branch) |
metrics |
object | No | Quantitative: duration_ms, item_count, scores |
refs |
object | No | References to related entities |
sovereignty |
object | No | Sovereignty attestation |
Every event carries an actor identifying who initiated the action.
| Type | Description | Auth |
|---|---|---|
system |
Internal KOAS process (default) | none |
operator |
Human operator via CLI/web | api_key or none |
external_orchestrator |
External LLM (Claude, GPT-4) via broker | api_key or hmac |
auditor |
Read-only audit access | api_key |
| Method | Description |
|---|---|
none |
Direct CLI/MCP access (relaxed mode) |
api_key |
SHA256-hashed API key with ACL scopes |
hmac |
HMAC-signed requests (production) |
| Phase | When | Key Metrics |
|---|---|---|
start |
Kernel begins execution | kernel name, version, stage |
end |
Kernel completes | duration_ms, item_count, success |
| Phase | When | Key Metrics |
|---|---|---|
call |
LLM invocation | model, prompt_hash, temperature |
cache_hit |
Response served from cache | model, call_hash |
end |
LLM response received | duration_ms, eval_count |
| Phase | When | Key Metrics |
|---|---|---|
started |
Pipeline run begins | run_id, config summary |
completed |
Pipeline run finishes | total_duration_ms, kernel_count |
| Phase | When | Key Metrics |
|---|---|---|
success |
Valid key + allowed scope | client_id, scope |
denied |
Invalid key or scope violation | client_id, reason |
Every event includes a sovereignty field:
{
"sovereignty": {
"local_only": true,
"endpoint": "http://localhost:11434"
}
}| Field | Description |
|---|---|
local_only |
true if all processing was local (Ollama) |
endpoint |
LLM endpoint used (for verification) |
For LLM events, the sovereignty field is populated by SovereigntyInfo from the cache layer, which captures hostname, user, endpoint, and timestamp.
Data Containment: Under correct configuration (enforce_local: true), no document content traverses the sovereign perimeter. The activity stream itself contains only hashes, metrics, and decisions β never raw content.
The orchestrator maintains a SHA256 chain across kernel executions (orchestrator.py:366):
entry["chain_hash"] = hashlib.sha256(chain_content.encode()).hexdigest()[:16]Each audit log entry's hash incorporates the previous entry, creating a tamper-evident chain. If any entry is modified or removed, the chain breaks.
Additionally, the Merkle tree module (merkle.py) computes inputs_merkle_root for pyramidal provenance β ensuring that document-level summaries can be traced back to their source chunks via cryptographic hashes.
# Count events by scope
cat .KOAS/activity/events.jsonl | \
jq -s 'group_by(.scope) | map({scope: .[0].scope, count: length})'
# List all kernel executions with duration
cat .KOAS/activity/events.jsonl | \
jq 'select(.scope | endswith(".kernel")) | select(.phase == "end") |
{kernel: .kernel.name, duration_ms: .metrics.duration_ms}'
# Verify sovereignty β find any non-local events
cat .KOAS/activity/events.jsonl | \
jq 'select(.sovereignty.local_only != true)'
# Expected: empty (all local)
# Find LLM cache hits
cat .KOAS/activity/events.jsonl | \
jq 'select(.scope | endswith(".llm")) | select(.decision.cache_hit == true)'
# Count events per run
cat .KOAS/activity/events.jsonl | \
jq -s 'group_by(.run_id) | map({run_id: .[0].run_id, events: length})'from ragix_kernels.activity import ActivityReader
reader = ActivityReader(Path(".KOAS/activity/events.jsonl"))
# Get all events for a run
events = reader.get_events(run_id="run_20260130_215011_348bc4")
# Filter by scope
kernel_events = reader.get_events(scope="docs.kernel")
# Get summary statistics
summary = reader.get_summary()
print(f"Total events: {summary['total']}")
print(f"Kernel executions: {summary['kernel_count']}")
print(f"LLM calls: {summary['llm_count']}")
print(f"Cache hit rate: {summary['cache_hit_rate']:.1%}")When a broker gateway mediates access, activity logging captures additional information:
| Direct Mode | Brokered Mode |
|---|---|
Actor: system / operator |
Actor: external_orchestrator |
Auth: none |
Auth: api_key / hmac |
| No scope filtering | Scopes enforced per client |
| Full event stream visible | Only docs.status + docs.export_external |
The broker logs authentication events (system.auth) before forwarding requests to KOAS. Failed authentication attempts are logged with phase: denied.
See the demo at demos/koas_docs_audit/ for a working example with relaxed and restricted modes.
Activity logging is enabled in the workspace manifest:
activity:
enabled: true
stream: ".KOAS/activity/events.jsonl"
# Optional: broker authentication
auth:
enabled: false # true for restricted mode
acl_file: ".KOAS/auth/acl.yaml"
require_hmac: false # true for productionThe activity writer is initialized at workflow start:
from ragix_kernels.activity import init_activity_writer, get_activity_writer
# At workflow start
writer = init_activity_writer(workspace=workspace, run_id=run_id)
# Events are then emitted automatically by:
# - orchestrator.py (kernel start/end)
# - llm_wrapper.py (LLM call/cache_hit)| Class | Purpose |
|---|---|
ActivityEvent |
Event data structure with to_dict() / to_json() |
ActivityWriter |
Append-only JSONL writer (thread-safe) |
ActivityReader |
Event querying and summary generation |
Actor |
Actor identity (type, id, auth, session) |
KernelInfo |
Kernel metadata (name, version, stage) |
NodeRef |
Document hierarchy reference (level, id) |
| Enum | Values |
|---|---|
ActorType |
SYSTEM, OPERATOR, EXTERNAL_ORCHESTRATOR, AUDITOR |
AuthMethod |
NONE, API_KEY, HMAC |
| Function | Purpose |
|---|---|
init_activity_writer(workspace, run_id) |
Initialize global writer |
get_activity_writer() |
Get current writer instance |
| Document | Description |
|---|---|
| SOVEREIGN_LLM_OPERATIONS.md | Sovereignty architecture and policy enforcement |
| KOAS.md | KOAS philosophy and kernel families |
| developer/ROADMAP_ACTIVITY_LOGGING.md | Design specification and implementation roadmap |
| ragix_kernels/README.md | Kernel developer reference |