Case Study  ·  Saleor Commerce

Logs are how you
and your agent should talk.

When you (vibe) code you are well served in having as many log or print statements as you can. Even though most coding agents today have a hard time wanting to put trace logs, they are how you and your agent understands the system and what happens during runtime.

Logs are good. They tell us a lot. From "Hello World" to "Here" and "Why is this not working?" to "What is the state of the system right now?" to "What did the user do before this error?" But, the risk, yes the risk. The risk is that those logs carry sensitive messages. The risk is that those logs are not only read by you and your agent, but also by attackers, scrapers, and over-permissioned tools. We took a popular e-commerce platform, ran its tests, and asked an LLM to find every place where customer data could leak. Here is what it found and what it looks like after.

We downloaded a real platform and ran its tests.

Saleor is one of the most widely deployed open-source e-commerce platforms in the world and we here are big fans of their coding practices. Out of love, we cloned their project and ran its standard test suite to see exactly what comes out of the core payment and order code when it is exercised. We did not write a single line of test code.

Then we gave the LLM one instruction:

“Convert the code for printing to TN. Find every place where sensative data could leak. Group the fields into categories based on who needs to read them. Use TN to Log. ”

We did not tell it which files to look in. We did not tell it which fields to protect. We did not write a list of PII fields or a data classification policy.

Before - actual Saleor code
Payment token passed directly to logger.info() as a format argument
# saleor/payment/tasks.py
for transaction_item, event in transactions_with_cancel_request_events:
    logger.info(
        "Releasing funds for transaction %s - canceling",
        transaction_item.token,  # <-- tok_visa_4242 in every log shipper
        extra={"transactionId": graphene.Node.to_global_id(...)}
    )
    except PaymentError as e:
        logger.warning(
            "Unable to cancel transaction %s. %s",
            transaction_item.token,  # same token, now in a warning
            str(e),
        )
in your log aggregator
INFO  Releasing funds for transaction tok_visa_4242 - canceling
WARN  Unable to cancel transaction tok_visa_4242. Gateway timeout
The token is passed as a %s format argument. It lands verbatim in Datadog, Splunk, CloudWatch — wherever you ship logs. No exception needed. Every normal operation leaks it.
After - the agent's conversion
Same process_payment() call. Structured, sealed, with the groups it chose.
# agent added one tn.info() call per operation
# groups it chose: finance (amounts/tokens), pii (customer)
# order_id and gateway stay in the clear for tracing

tn.info("payment.process.called",
    gateway=payment.gateway,        # default group, visible
    order_id=str(payment.order_id), # default group, visible
    amount=str(payment.total),      # finance group, sealed
    currency=payment.currency,      # finance group, sealed
    payment_token=str(payment.token) # finance group, sealed
)
sealed log entry
order_id ord_8fKq
gateway  mirumee.payments.dummy
finance  ●●● AXDwM/ljS4F9ZM2P…
The event is recorded. The order is traceable. Amount and token are locked inside the finance group, readable only by whoever holds that key.

Groups are yours to define

You and any agent type assistants you have define groups based on who needs to read which fields in your system. A group is just a name and an encryption key for everyon one who needs to encrypt that group. You can have two or twenty or 200.

For Saleor, the agent chose two. A more complex system might also want fraud for risk scores and device fingerprints, legal for dispute notes, support for contact history. The same pattern scales to any number of audiences.

pii

Customer identity

customer_email, billing_address across the five order lifecycle events: created, confirmed, cancelled, payment captured, fulfillment fulfilled.

The agent placed these here because compliance, legal, and support need them. Engineers debugging a failed webhook do not.

Secrets

Logs are full of juicy details

payment_token, third parties, comments about bugs You don't want these in your log aggregator. You don't want them in your SIEM. You don't want them in the hands of an attacker that got read access to your logs. But you do need to record them somewhere for debugging and incident response.

Finance reconciles every transaction without reading customer names. Engineers trace orders without reading amounts.

What each role sees.

This is real output from the benchmark run. Every entry in the log is the same sealed record. What you can open depends entirely on which key you hold.

No key - the attacker, the scraper, the over-permissioned tool
Gets shape. Nothing else.
event  order.created
pii    ●● AUZw+IQXD+5Vy…
finance ●● AbpkON5srFkw…
default ●● Aet4NIpWqlgh…
They can see that an order was created. They cannot read anything else.
Default key - the engineer, the on-call, the agent
Can trace. Cannot identify.
event    payment.capture.called
gateway  mirumee.payments.dummy
order_id 0e3851ca-a2a3…
finance  ●● ARPRcdoP8/BU…
Can follow an order end to end. Amounts and customer identity stay sealed. Enough to debug. Not enough to harm.
Finance key - accounting, reconciliation, reporting
Sees amounts. Not names.
event    payment.capture.called
amount   98.40
currency USD
email    ●● AUZw+IQXD+5V…
Can reconcile every dollar. Cannot read a customer name. The separation is enforced by the log itself, not by access policies you have to maintain.

Datadog still works. Our Chrome extension makes it actually readable.

TN ships an OpenTelemetry adapter and handlers for most common log shippers. Your SIEM, your dashboards, your alerts — they all keep working. Sealed fields just look like opaque ciphertext to them, and there's nothing left to redact downstream.

The Chrome extension is the unlock. Install it once, sign in with your vault, and every TN ciphertext on every page you have keys for — Datadog, Splunk, Kibana, your custom dashboards, even Slack — becomes readable in place. No copy-paste, no proxy, no separate viewer. Decryption happens entirely in your browser; the page never gets your keys, the SIEM never gets the plaintext.

And critically: contractors and consultants on-site to build your dashboards can't see sensitive data while they work. They get the fields they're granted, nothing else.

Datadog showing events with sealed ciphertext blobs
What Datadog receives. Event names, timestamps, order IDs. Sealed fields are opaque blobs.
Datadog with Chrome extension showing decrypted data
The same log, opened by a provisioned key holder via the Chrome extension. Decryption happens locally.

We have prompts for every major coding agent.

Claude Code, Cursor, Copilot, Windsurf, Aider. We maintain a skill and a prompt library for each one. Point your agent at your payment module, your auth flow, your event pipeline. Ask it to do what we did here.

The agents page has everything: the prompts, the AGENT.md reference the LLM reads, and the install instructions for each tool.

See the agent prompts

Don't leak your data.