What the public log shows

Qoffee is designed to run in a public repository, because that is what makes the Actions minutes free. The consequence is that the run logs are readable by anyone.

So identifiers are replaced before they reach the log. This page shows the code that does it rather than describing it, because you can check code.

What a line looks like

14:22:07 3f9a1c2b INFO    qoffee: found 3 tracked job(s)
14:22:09 3f9a1c2b INFO    qoffee.core.engine: plan: notify=True actions=3
14:22:09 3f9a1c2b INFO    qoffee.core.engine:   resolve   job#3beed3 - completed successfully

job#3beed3 is a truncated SHA-256 of the real job ID. It is stable, so the same job produces the same hash everywhere in the log and you can follow it across lines. It is one way, so it does not give the ID back. The full ID is in your notification, which only you can see.

What gets replaced

Where the filtering happens

Not at the call sites. At the handler, once.

class RedactionFilter(logging.Filter):
    def filter(self, record):
        if not self.enabled:
            return True
        record.msg = redact(record.getMessage())
        record.args = ()
        if record.exc_info:
            formatter = logging.Formatter()
            record.exc_text = redact(formatter.formatException(record.exc_info))
            record.exc_info = None
        return True

Redacting at each call site leaks the first time anyone adds a debug line and forgets. Filtering at the handler catches everything that reaches the log whether or not the person who wrote it was thinking about redaction.

The exception traceback handling is the part that matters most in practice. Tracebacks from the IBM SDK can carry your instance CRN inside a request URL, and a traceback is formatted by the handler after every filter has run, so it has to be rendered early and redacted explicitly.

Ordering

Logging is configured with redaction on before configuration is even loaded. A configuration error that echoes a credential therefore cannot reach the log unfiltered, because the filter was installed first.

Turning it off

REDACT_LOGS = False in qoffee/settings.py.

There is no reason to do this in a public fork. The full IDs are already in your notification, so redaction costs you nothing there. In a private fork where you are debugging, it can be convenient.

What this does not cover

Redaction protects the log. It is not a guarantee about anything else.

Your secrets are held by GitHub and are unreadable once saved, including by you. Your notification goes to your own webhook. Qoffee makes no other outbound calls, and there is no server anywhere in the design, which is covered on how Qoffee works.

All of the above is in qoffee/logging_setup.py, which is 107 lines.