# sunscreen Credential-redacting slog handler that keeps API keys, tokens and passwords out of application logs. `import "git.golem15.com/golem15/summercms/modules/sunscreen"` ## Overview `sunscreen` wraps any `log/slog` handler and rewrites every record before it is written: values of sensitive attribute keys become `[REDACTED]`, and credential shapes inside messages and string values are scrubbed. It is the SummerCMS counterpart of a Monolog tap that a WinterCMS application wires into its log channels to scrub contexts and messages. Every application `main` that `summer build` generates calls `sunscreen.InstallDefault(os.Stderr)` as its first statement, so the default logger, the standard `log` package and every plugin that falls back to `slog.Default` log through it from the start. ## Features - Sensitive keys (`sunscreen.RedactedKeys`): `api_key`, `apikey`, `authorization`, `bearer`, `password`, `secret`, `token`, `webhook_secret`, `admin_password`, `openai_api_key`, `anthropic_api_key` and `perplexity_api_key`, compared case-insensitively at any group depth. The value of such an attribute becomes `sunscreen.Redacted`, whatever its kind. - Scrub patterns (`sunscreen.Scrub`): `Bearer ` becomes `Bearer [REDACTED]`, `sk-` followed by 20 or more key characters (dashed keys included) becomes `sk-[REDACTED]`, and `x-api-key: ` becomes `x-api-key: [REDACTED]`. The Bearer and x-api-key matches are case-insensitive. - Applied to the record message, string values, error values, byte slices and the formatted text of any other value; maps with string keys, such as `http.Header`, are redacted key by key. - `slog.LogValuer` values are resolved before redaction, groups are walked recursively, and attributes added with `WithAttrs` are redacted before they reach the wrapped handler. - `sunscreen.InstallDefault` builds a fresh text handler instead of wrapping the existing default, whose output goes through the `log` package that `slog.SetDefault` redirects back into the new handler. ## Usage The generated `main` already installs it: ```go func run(args []string, out io.Writer) error { sunscreen.InstallDefault(os.Stderr) cfg, err := compass.Load("config") // ... } ``` To redact another handler, for example a JSON handler in a custom binary, wrap it: ```go logger := slog.New(sunscreen.Wrap(slog.NewJSONHandler(os.Stderr, nil))) logger.Error("vendor call failed", "api_key", key, "err", err) // {"level":"ERROR","msg":"vendor call failed","api_key":"[REDACTED]","err":"..."} ``` Redaction is a safety net, not a licence: log ids and outcomes, never request bodies, tokens or arguments that carry them. ## API reference | Identifier | Description | |------------|-------------| | `sunscreen.Wrap` | Returns a `slog.Handler` that redacts every record and every `WithAttrs` attribute before the wrapped handler sees it. Wrapping a sunscreen handler again returns it unchanged. | | `sunscreen.InstallDefault` | Makes a redacting text handler writing to the given writer the process default logger. | | `sunscreen.Scrub` | Replaces the Bearer, `sk-` and `x-api-key:` credential shapes in a string. | | `sunscreen.Redacted` | The replacement text, `[REDACTED]`. | | `sunscreen.RedactedKeys` | Returns a copy of the attribute keys whose values are always redacted. | ## Dependencies - SummerCMS modules: none. - Third-party: none. - Standard library: `context`, `fmt`, `io`, `log/slog`, `reflect`, `regexp`, `slices`, `strings`. ## Testing ```sh go test ./modules/sunscreen/... ``` The tests log through the handler into a buffer and need no external services.