Skip to main content

Transparency Logs

Early Access

Transparency logs are currently in early access, available starting v3.21.0-ea.3.

Traefik Hub can keep a second, cryptographic record alongside your normal logs and access logs. If someone later asks whether a log line was edited or removed, this record proves that it wasn't. That matters most for compliance, where an auditor needs proof your logs are complete and unaltered.

This is a Hub-wide setting, not something you attach to a route or a middleware. Turn it on once, and it covers log lines and access log entries from every gateway, API, AI, and MCP, though not request or response bodies.

Token exchange delegates authority to the caller, and an authorization step such as AuthZEN decides whether to allow the action. Transparency Logs records that decision.

License Requirement

Transparency Logs is a licensed feature. Contact the Traefik Labs sales team to add it to your license.

How transparency logs stay tamper-evident

  • Every entry gets a cheap, verifiable proof of inclusion. Each log line and access log entry is hashed and added to an append-only Merkle tree, built with Tessera. Proving one entry belongs to the tree costs about 30 hashes whether the tree holds a thousand entries or a billion, so verification stays cheap as the log grows.
  • Periodic checkpoints prove nothing was edited or removed. Traefik Hub signs a checkpoint, a record of the tree's current size and root hash. Because the tree only ever grows, Tessera can prove any two checkpoints are consistent, meaning the newer one adds entries on top of the older one and never rewrites what was already there.
  • The log itself isn't enough to verify anything. It only stores IDs and hashes, not content, so verifying an entry later needs both the transparency log and your original log file.

Closing the trust gap with a witness

On its own, a checkpoint only proves something if you trust whoever stores it. A witness closes that gap. Witnesses are outside parties that periodically cosign a checkpoint after checking it's consistent with the last checkpoint they've already cosigned. Adding a witness is optional, but without one you're exposed to a split view. Someone with access to the log's storage could fork history at an old checkpoint, grow a different, conflicting tree from that point on, and produce a second checkpoint that's equally valid. Nothing in the log itself tells you which of the two is genuine.

A witness only cosigns the checkpoint it's actually shown. Rewriting history after that point would need a cosignature for a different, conflicting checkpoint that the witness never received. Any new checkpoint it does see must still be consistent with the one it already holds.

A witness must be reachable. If a configured witness is unreachable, Traefik Hub still starts and keeps serving traffic. It retries building the transparency log appender every 10 seconds in the background, and until that succeeds, incoming log lines aren't recorded to the transparency log at all. If the appender already initialized and a witness later becomes unreachable, Traefik Hub keeps appending entries to the tree, but stops publishing checkpoints until the witness is reachable again. Either way, it logs an error each time cosigning fails, naming the reason, for example Transparency log witness is unreachable; the checkpoint cannot be cosigned. Ensure your witnesses are as available as Traefik Hub itself.

Prerequisites

  • You must set log.format and accessLog.format to json. Otherwise, Traefik Hub will not start, with the error transparency logs require the Traefik log format to be json, got "common" (and the equivalent message for the access log format). Both values.yaml examples below set these values.
  • If you also send access logs to an OTLP collector (accessLog.otlp), you must set accessLog.dualOutput: true. Otherwise, Traefik Hub will not write the access log file, while still hashing and committing each entry to the transparency log, so a verifier will report those entries as not found.
  • Traefik Hub writes logs to stdout by default on Kubernetes. Set log.filePath and accessLog.filePath so they're written somewhere a verifier can read them later.

Enable transparency logs

First, generate a signing key with the keygen command that ships with Traefik Hub:

traefik-hub keygen --name my-hub-signer --outputDir ./keys

This writes private.key (used to sign checkpoints) and public.key (shared with anyone who needs to verify them) to ./keys. --name becomes part of the key's identity: Tessera writes it as the checkpoint's origin, so anyone verifying the log later needs this exact value. Use a schema-less URL for it, such as example.com/my-log. It doesn't need to resolve to anything, but a plain name risks colliding with another log's origin, since nothing enforces uniqueness otherwise. Pick a name you won't need to change. Mount the private key into your Traefik Hub pod, then point transparencyLogs.signerPrivateKey at it:

values.yaml
log:
format: json
accessLog:
format: json
hub:
transparencyLogs:
driver:
posix:
path: /data/transparency-logs
signerPrivateKey: /etc/traefik-hub/transparency-logs/private.key
checkpointInterval: 10s

driver is where the tree itself is stored. This can be a local path (as above), a GCS bucket backed by Spanner, or an S3 bucket backed by MySQL. If you use a local path, mount a persistent volume there. Without one, the tree lives on the pod's own ephemeral storage and is lost on every restart or reschedule, along with your entire tamper-evident history. checkpointInterval controls how often a new checkpoint is signed; the example above signs one every 10 seconds.

Before adding a witness, keep in mind:

  • A witness you run yourself only proves to you that nothing changed. You could still roll back your log and your own witness together before anyone checks. For the guarantee to mean anything to an outside auditor, the witness needs to run independently of you.
  • There's no hosted witness service to point at yet. Run your own with the open source transparency-dev/witness implementation, or ask another team to run one independently on your behalf.
  • Witnesses aren't dynamic. Whoever runs the witness has to add your log to its configuration before it will cosign anything. Send them your log's origin (the --name you gave keygen) and its public key, then confirm they've added it before continuing.
  • Don't generate a witness's keypair with traefik-hub keygen. It only produces plain Ed25519 keys, and some verification tooling only recognizes the cosignature format a witness key needs. If you're self-hosting a witness with transparency-dev/witness, generate its keypair with that project's own generate_keys command instead, which produces the correct format.

Add the witness's URL and public key under witnessGroup:

values.yaml
log:
format: json
accessLog:
format: json
hub:
transparencyLogs:
driver:
posix:
path: /data/transparency-logs
signerPrivateKey: /etc/traefik-hub/transparency-logs/private.key
checkpointInterval: 10s
witnessGroup:
threshold: 1
witnesses:
- url: https://witness.example.com
key: /etc/traefik-hub/transparency-logs/witness-public.key

threshold is how many witnesses must cosign a checkpoint before Traefik Hub considers it committed; with one witness configured, set it to 1.

Once you have the configuration adjusted to your needs, use Helm to deploy Traefik Hub with the new values:

Install Traefik Hub using Helm
helm repo add --force-update traefik https://traefik.github.io/charts
helm upgrade --install traefik -n traefik --wait \
--values values.yaml \
traefik/traefik
Testing before you go to production

Setting transparencyLogs.debug: true also stores the full rendered log line in the transparency log, not only its hash, which makes it easier to check things are working while you set this up. Turn it off again before going to production to avoid storing your log content a second time. See the Metrics section of the reference page to monitor transparency logs once it's running.

  • See the Transparency Logs reference for the full transparencyLogs configuration, including the storage driver and witness group fields.