Skip to content

Audit Log — Operator's Guide

Read in your language: English — translations available on request via agallon@Cisco.com.

Scope status (post-Scope-Freeze 2026-05-10) — Audit log capability expanded to include Self-Upgrade tamper-evident hash chain (SELFUP-7 scaffold), Lab Deployment Staging telemetry events, SSH/TELNET orchestration command transcripts with redaction (per SSH-7), and RELAY.Art ingress/egress audit (ADR 0020). SOC 2 Type II evidence collection runbook lives at runbooks/soc2-evidence-collection.md.

This page describes the audit logging the TLSStress.Art Dashboard maintains. Audit logs are the operator's evidence that the LICENSE + USAGE_POLICY are being respected; they are also a forensic record useful when investigating incidents.

What is audited

Two distinct audit streams:

1. License acceptance audit — audit_license_acceptance table

One row per (operator, license_version) pair. Every authenticated user must produce one row before the Dashboard becomes usable. See PRIVACY_POLICY.md for the schema and rationale.

Schema (Drizzle migration 0019_license_acceptance.sql):

audit_license_acceptance (
  id                       uuid        PRIMARY KEY,
  session_user             text        NOT NULL,           -- session cookie 'agentcluster_session' user
  role                     enum        NOT NULL,           -- cisco_employee | cisco_partner
  declared_email           text        NOT NULL,           -- lowercased
  declared_email_raw       text        NOT NULL,           -- original casing
  license_version          text        NOT NULL,
  usage_policy_version     text        NOT NULL,
  privacy_policy_version   text        NOT NULL,
  telemetry_consent        boolean     NOT NULL DEFAULT false,
  client_ip                text,                           -- X-Forwarded-For of acceptance request
  user_agent               text,                           -- HTTP User-Agent
  accept_language          text,                           -- HTTP Accept-Language
  admin_note               text,                           -- free-form, for admin remediation
  accepted_at              timestamptz NOT NULL DEFAULT now(),
  UNIQUE (session_user, license_version)
);

2. Action audit — audit_log table (existing)

One row per non-trivial action (login attempts, config changes, run lifecycle events, scaling decisions). See dashboard/src/lib/audit.ts and dashboard/src/db/schema.ts.

How to query

Via the admin viewer page

Open /admin/audit/license-acceptances in the Dashboard (admin role required — same ADMIN_BASIC_AUTH env var that gates the rest of admin UI). Read-only paginated table with filters by date range, role, email substring.

Via SQL

Connect to the cluster's Postgres via psql (or pgAdmin / DBeaver):

# Get the connection details from the running pod:
kubectl exec -n web-agents postgres-0 -- env | grep POSTGRES_

# Open psql:
kubectl exec -it -n web-agents postgres-0 -- psql -U $POSTGRES_USER $POSTGRES_DB

Useful queries:

-- Most recent acceptances (latest first):
SELECT accepted_at, session_user, role, declared_email, client_ip, license_version
  FROM audit_license_acceptance
  ORDER BY accepted_at DESC
  LIMIT 50;

-- All Cisco partner acceptances in the last 30 days:
SELECT *
  FROM audit_license_acceptance
  WHERE role = 'cisco_partner'
    AND accepted_at > now() - INTERVAL '30 days'
  ORDER BY accepted_at DESC;

-- Operators who haven't accepted the latest license version yet:
SELECT DISTINCT session_user
  FROM audit_log     -- pull the list of recently-active users from the action audit
  WHERE created_at > now() - INTERVAL '7 days'
  EXCEPT
SELECT session_user
  FROM audit_license_acceptance
  WHERE license_version = 'PolyForm-Noncommercial-1.0.0+AppendixA-2026-05-06';

-- Count of acceptances per role:
SELECT role, count(*)
  FROM audit_license_acceptance
  GROUP BY role;

-- Operators who consented to telemetry:
SELECT session_user, declared_email, accepted_at
  FROM audit_license_acceptance
  WHERE telemetry_consent = true;

Via API (admin only)

The admin viewer page (/admin/audit/license-acceptances) reads GET /api/admin/audit/license-acceptances which returns a JSON array. The endpoint requires the agentcluster_session cookie AND admin authentication.

Operational tasks

Bumping license / privacy policy versions

When you make a material change to the LICENSE or any of the *_POLICY.md files:

  1. Update the relevant constant in dashboard/src/lib/license-versions.ts:
    export const LICENSE_VERSION = 'PolyForm-Noncommercial-1.0.0+AppendixA-2026-XX-XX';
    
  2. Build + deploy the Dashboard — every operator's next page load triggers the License Acceptance Modal because their existing acceptance row is for an older license_version.
  3. The old rows REMAIN in the audit table (audit history is immutable by design).

Removing an operator's authorisation

If an operator must lose access (left the company, partnership terminated, etc.):

  1. Revoke their Dashboard credential (rotate ADMIN_BASIC_AUTH or update SSO).
  2. Optionally, retain the row for audit history (recommended) — they cannot use the system without a session anyway.
  3. To explicitly signal "no longer authorised" without deleting the row:
    UPDATE audit_license_acceptance
       SET admin_note = 'Authorisation revoked on 2026-XX-XX by <admin>: <reason>'
     WHERE session_user = '<username>';
    

Wiping the database for handover

When you donate the cluster to another team:

TRUNCATE audit_license_acceptance, audit_log;

After truncation: - Every operator must re-accept the license (correct behaviour for a handover) - Past audit history is gone (intentional — the new team starts clean)

If you need to preserve the audit history before handover, dump first:

kubectl exec -n web-agents postgres-0 -- pg_dump --table audit_license_acceptance --table audit_log $POSTGRES_DB > audit-backup-$(date +%F).sql

Compliance posture

The audit table satisfies these compliance requirements:

Requirement Source How audit_license_acceptance satisfies
Records of consent GDPR Art. 7(1), LGPD Art. 8 §6 Every row IS a record of consent — what was accepted, when, by whom
Demonstrable lawful basis GDPR Art. 6, LGPD Art. 7 Acceptance row + this PRIVACY_POLICY.md establish the basis
Audit trail of access SOC 2 CC6, ISO 27001 A.12.4 Combined with audit_log table, full action history
Right to access (data subject) GDPR Art. 15 Operator can self-query by filtering on their own session_user
Right to erasure GDPR Art. 17 DELETE against the row when an operator requests it

This project is NOT a SaaS provider; the data lives on the operator's own infrastructure, so most compliance obligations fall on the operator (cluster-owner) rather than on the licensor. This page documents how to fulfill them.