Open Connector
Security

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 prevHash point at a hash that is no longer its predecessor.
  • Tampering is detectable — changing any field invalidates that record's hash and 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:

ActionTrigger
connector.proxyAgent proxy-executes a tool (POST /tools/execute/proxy)
connector.connectA credential is submitted (hosted connect/link submit, native POST /connected_accounts)
connector.initiateAn OAuth flow is started (/oauth/start, interactive connect)
license.issue / license.canceled / license.payment_failedEE license lifecycle (Stripe webhook)
webhook_subscription.delivery / webhook_endpoint.deliveryAn outbound webhook delivery attempt
trigger.event.receivedA verified inbound trigger event is ingested
auth.emailA 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 under audit.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 verifyAuditChain to produce that evidence on demand.
  • Long-term retention: Archive the .audit/*.jsonl files (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-audit license 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.

On this page