Turn a %Ankusa.Config{} into a JSON-encodable map safe to hand to anyone
who can reach the admin API.
Adapter options are an open vocabulary (every adapter, and every third-party adapter, names its own credentials), so redaction is an allowlist, not a blocklist. There are two modes.
Open covers %Ankusa.Config{}'s own fields and a source entry's own
fields (tenant, name, source_id, ingest_path, on_verify_failure,
trust_url_tenant):
none of them holds a secret, so values are converted as they are. Maps and
keyword lists become string-keyed maps, atoms become strings (module atoms
via inspect/1), numbers, booleans, nil and binaries stay, functions
become "#Function", and any other term goes through inspect/1. A value
shaped like a {module, opts} pair (an atom head, an Erlang module included,
with list or map opts), or a list of them, is never converted as it is: its
opts switch to closed mode.
Closed covers everything inside a {module, opts} pair (sinks,
verifiers, source store, blob store, codec, retry policy, route resolver,
routes store, lifecycle sinks) and a source's stored JSON spec. For each
key/value, the first matching rule wins:
- under
headers, names stay and values become"[REDACTED]"; - a
{module, opts}pair (or a list of them) becomes%{"module" => "Ankusa.Sink.Log", "opts" => %{}}, its opts closed; - a
{module, fun, args}callback becomes"Module.fun/arity", never its args (a static GCS token is a bare arg); - numbers, booleans,
niland atoms stay; functions become"#Function"; - structs, maps and non-empty keyword lists become closed maps;
- a list made only of such maps, keyword lists and pairs is converted element by element;
- under
url,endpointorresourcea binary is shown with the password ofuser:pass@userinfo hidden, a lonetoken@userinfo hidden, and every query value hidden (names stay); - a value under a key on the shown-key allowlist (ids, names, regions, topics and other non-secret identifiers) is shown;
- anything else, strings and charlists included, is
"[REDACTED]".
A new adapter option is therefore hidden until its key is added to
@shown_keys. Path and host of a URL are shown, so a capability URL whose
secret sits in the path is not protected.
Summary
Functions
A redacted, JSON-encodable view of the whole config.
reduce_hidden/3 without an accumulator: replace every hidden string by fun.(string).
Walk a stored source spec (string keys, plain JSON values) and hand every
string the closed-mode rules would hide to fun, threading an accumulator.
Everything else comes back as it was.
A redacted, JSON-encodable view of one tenant-scoped source, built from the
Ankusa.SourceStore.stored() map: the shape the admin API's source routes
return and the data of a source lifecycle event.
Functions
@spec config(Ankusa.Config.t()) :: map()
A redacted, JSON-encodable view of the whole config.
@spec source_entry(Ankusa.SourceStore.stored()) :: map()
A redacted, JSON-encodable view of one tenant-scoped source, built from the
Ankusa.SourceStore.stored() map: the shape the admin API's source routes
return and the data of a source lifecycle event.
The stored spec is plain JSON (string keys, plus strings, numbers, maps, and
lists), so its verify and sinks take the closed-mode rules of the module
doc.