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:
- Update the relevant constant in
dashboard/src/lib/license-versions.ts:export const LICENSE_VERSION = 'PolyForm-Noncommercial-1.0.0+AppendixA-2026-XX-XX'; - 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. - 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.):
- Revoke their Dashboard credential (rotate
ADMIN_BASIC_AUTHor update SSO). - Optionally, retain the row for audit history (recommended) — they cannot use the system without a session anyway.
- 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.
Related¶
- PRIVACY_POLICY.md — what is collected and why
- USAGE_POLICY.md — what use is permitted
- LICENSE — full legal text including Appendix A
dashboard/src/lib/audit.ts— implementation of the action auditdashboard/src/db/schema.ts— table definition