Audit Trail
Every credential use is recorded in a tamper-evident, hash-chained audit journal.
Open Connector records every brokered credential use in a tamper-evident, hash-chained audit journal. The audit trail answers the question: "Which agent, acting for which user, called which tool, at what time, and what was the outcome?" — with cryptographic evidence that the record has not been altered after the fact.
The journal is an append-only NDJSON file on disk (.audit/*.jsonl), one record per line, written by apps/server/src/audit.ts on top of evlog, and — when OTLP_ENDPOINT is configured — mirrored to your observability stack. The on-disk file is the integrity artifact; OTLP is the queryable copy.
What the audit trail captures
Each record is one wide event. The audit payload lives under the audit key:
Prop
Type
The audit trail never records credential values, third-party request bodies, or response payloads — only metadata (ids, action, outcome, status). Any changes field is redacted before the record is written (see Redaction).
The hash-chain mechanism
Each record's hash is a SHA-256 over the canonical record content plus the previous record's hash (prevHash). The chain works like a blockchain: each record commits to the record before it. This means:
- Insertion attacks are detectable — inserting a record in the middle breaks the chain from that point forward.
- Deletion / reordering are detectable — removing or moving a record makes its successor's
prevHashpoint at a hash that is no longer its predecessor. - Tampering is detectable — changing any field invalidates that record's
hashand every hash after it.
No separate verifier service is required. The repo ships the verifier: verifyAuditChain(readAuditJournal(dir)) (in apps/server/src/audit.ts) re-derives the chain through evlog's own signer and returns -1 when intact, or the index of the first broken record. See the engineering guide docs/how-to/verify-the-audit-journal.md and docs/reference/audit-journal.md.
Audit events
The journal records every credential touch plus key lifecycle events. The actions emitted today:
| Action | Trigger |
|---|---|
connector.proxy | Agent proxy-executes a tool (POST /tools/execute/proxy) |
connector.connect | A credential is submitted (hosted connect/link submit, native POST /connected_accounts) |
connector.initiate | An OAuth flow is started (/oauth/start, interactive connect) |
license.issue / license.canceled / license.payment_failed | EE license lifecycle (Stripe webhook) |
webhook_subscription.delivery / webhook_endpoint.delivery | An outbound webhook delivery attempt |
trigger.event.received | A verified inbound trigger event is ingested |
auth.email | A transactional auth email (verify/reset/invite) is sent |
This list is descriptive, not a frozen contract — new credential surfaces add their own action.
Redaction
Secrets and PII are masked before the record is written (in the initLogger pipeline, ahead of the drain):
- Path scrub (
auditRedactPreset) masks the enumerated secret fields underaudit.changes.*(token/apiKey/accessToken/refreshToken/secret/password/auth headers) to"[REDACTED]". - Content maskers (email / JWT / IPv4 / credit-card) are on in production for full PII protection, and off in dev so local debug links stay readable.
Accessing the journal
There is no audit read/export HTTP API today. You access the trail two ways:
On disk (the integrity artifact)
Read apps/server/.audit/*.jsonl directly, and verify it hasn't been altered with the shipped verifier — see docs/how-to/verify-the-audit-journal.md.
Via OTLP / Loki (the queryable mirror)
When OTLP_ENDPOINT is set, the same events drain to OpenTelemetry Logs. In Loki:
{service="open-connector-server"} | json | audit_action =~ "connector\\..*"To find all proxy calls for a specific connection:
{service="open-connector-server"} | json
| audit_action = "connector.proxy"
| audit_target_id = "conn_01j9xyz789"See docs/observability/queries.md for the full verified query library. Loki is operator tooling with Loki-shaped ids — it is the convenience mirror, not the tamper-evidence artifact.
Retention and compliance
The journal is append-only NDJSON files on disk (plus the optional OTLP copy) — not a database table. Retention is your responsibility as the operator:
- GDPR / right to erasure: The trail does not store user PII — only ids (
actor/target), not names or emails. To satisfy an erasure request, delete the connected account; the audit records referencing its id remain for compliance continuity but contain no personally identifiable information. - SOC 2 / ISO 27001: The hash chain provides non-repudiation evidence that access logs have not been altered after the fact. Run
verifyAuditChainto produce that evidence on demand. - Long-term retention: Archive the
.audit/*.jsonlfiles (and/or the OTLP copy) to an append-only store (S3, GCS) on a schedule. Premium long-term retention/export is gated by the@open-connector/ee-auditlicense entitlement (auditPolicy()/assertAuditExport()); the tenant-scoped read/export API those gate is a tracked follow-up, not yet built.
The default hash-chain state is in-memory, single-process. For a multi-process or multi-instance deployment, back the chain with shared state (signed(..., { strategy: "hash-chain", state })) so it survives restarts and spans instances. See docs/explanation/audit-journal.md for the trade-offs.