Portal Community

TEE Audit Log Architecture

Append-Only Chain

Each audit log entry includes a hash of the previous entry — forming a hash chain. Deleting or modifying any entry breaks the chain, which is detectable during audit verification. Sequence numbers detect inserted or missing entries.

Attestation Signing

Each batch of audit records is signed by the TEE-bound private key (see Attestation-Based Log Integrity). The signature proves the record came from attested code, not from a modified or impersonating process.

Immutable Egress

Audit records are pushed to an S3-compatible store with Object Lock (WORM) enabled. Once written, records cannot be modified or deleted — even by infrastructure administrators — until the retention period expires.

Dual-Copy Strategy

Audit records are kept both inside the TEE (in-TEE Loki, for investigation) and outside the TEE (WORM S3, for compliance). The external copy is sanitized; the internal copy has full detail accessible via attested channel.

Audit Record Schema

// TEE Audit Record — emitted for every significant workflow event
// This record is part of the attestation-signed log chain

{
  // Chain integrity fields
  "seq": 1042,                              // Monotonically increasing within TEE lifetime
  "prev_hash": "sha256:a1b2c3...f9",        // SHA256 of previous record (hash chain)
  "record_hash": "sha256:b2c3d4...fa",      // SHA256 of this record (before signing)

  // TEE identity
  "tee_measurement": "sha384:c3d4e5...fb", // PCR[2]: code version that produced this record
  "tee_instance_id": "nitro-001-us-east",  // TEE instance identifier

  // Event
  "timestamp_utc": "2026-05-25T10:30:00.000Z",
  "event_type": "workflow.completed",       // Enum: workflow.started, node.completed, hil.approved, etc.
  "execution_id": "exec-abc-123",           // GUID — no payload encoded
  "workflow_type": "credit-assessment",     // Enum — not user-supplied string
  "tenant_hash": "sha256:tenant-hash",      // Hash of tenant ID

  // Operational context (no payload data)
  "duration_ms": 1423,
  "node_count": 7,
  "outcome": "completed",                   // Enum: completed, failed, timeout, cancelled

  // Signature (covers all above fields)
  "signature": "base64:MEQCIBx...",
  "signing_cert_thumbprint": "sha256:d4e5f6..."
}

Audit Event Types

Event TypeWhen EmittedKey Fields
workflow.startedWorkflow execution beginsexecutionId, workflowType, tenantHash, triggeredBy (system/api/schedule)
workflow.completedWorkflow completes successfullyexecutionId, durationMs, nodeCount, outcome
workflow.failedWorkflow terminates with failureexecutionId, errorCode, failedNodeType, durationMs
node.startedIndividual node begins executionexecutionId, nodeType, nodeIndex, tenantHash
node.completedIndividual node completesexecutionId, nodeType, nodeIndex, durationMs, outcome
hil.requestedHuman-in-loop approval requestedexecutionId, approvalType, tenantHash — NOT the approval context data
hil.approvedHIL decision recordedexecutionId, approverRoleHash (hashed role, not name), decision (approved/rejected)
tee.startedTEE instance initialized and attestedteeInstanceId, teeMeasurement, attestationCertThumbprint
tee.policy.changedTEE egress or attestation policy updatedpolicyVersion, changeType, authorizedBy (role hash)
audit.chain.verifiedAudit chain integrity check performedcheckedFrom, checkedTo, result (intact/gap-detected)

WORM S3 Storage Configuration

# S3 bucket for TEE audit records — WORM (Write Once, Read Many)
# Object Lock prevents modification or deletion during retention period

aws s3api create-bucket \
  --bucket bizfirstgo-tee-audit-logs \
  --object-lock-enabled-for-bucket

aws s3api put-object-lock-configuration \
  --bucket bizfirstgo-tee-audit-logs \
  --object-lock-configuration '{
    "ObjectLockEnabled": "Enabled",
    "Rule": {
      "DefaultRetention": {
        "Mode": "COMPLIANCE",   # COMPLIANCE = even bucket owner cannot delete
        "Years": 7              # 7-year retention for SOX/financial compliance
      }
    }
  }'

# Audit records written by the external OTel Collector → S3 exporter:
exporters:
  awss3:
    s3_uploader:
      region: us-east-1
      s3_bucket: bizfirstgo-tee-audit-logs
      s3_prefix: "tee-audit/"
      s3_partition: minute   # One object per minute; efficient for range queries
    marshaler: otlp_json

# When writing audit records:
# - Object key: tee-audit/2026/05/25/10/30/seq-1042.json
# - Object lock: inherited from bucket default (7-year COMPLIANCE)
# - Cannot be deleted or modified until 2033-05-25

Audit Chain Verification Query

# LogQL: Find audit records for a specific execution
{job="tee-audit"} | json | execution_id = "exec-abc-123"
  | line_format "{{.seq}} {{.event_type}} {{.timestamp_utc}} {{.outcome}}"

# LogQL: Detect sequence gaps (potential log deletion)
{job="tee-audit", tee_instance_id="nitro-001-us-east"}
  | json
  | seq > 1000 and seq < 2000
  # Compare seq values in Grafana visualization — gaps indicate tampering

# Programmatic audit chain verification:
# 1. Fetch all records sorted by seq from S3
# 2. For each record: SHA256(record_json) == record.record_hash
# 3. For each record: SHA256(previous_record_json) == record.prev_hash
# 4. Signature verification: tee_public_key.Verify(record_hash, record.signature)
# 5. If any check fails: alert + retain record as evidence of tampering
Audit Log Is Separate from Operational Log

The TEE audit log is not the same as the operational Loki log stream. Operational logs (executionId, timing, error codes) serve the operations team for debugging and alerting. The audit log (event type, chain hash, attestation signature) serves compliance, legal, and security teams. Use separate Loki streams, separate OTel Collector pipelines, and separate storage buckets — mixing them creates retention and access control conflicts.