Skip to content

Releases: maxie7/logscry

v0.12.36

Choose a tag to compare

@github-actions github-actions released this 30 Oct 13:00
2693010

v0.12.36 — a lost Docker connection is reported by its real cause

When logscry's connection to Docker's event stream broke and it retried, the status line sometimes named the wrong step: docker: events lost, reconnecting (attempt N, …): list: Cannot connect … instead of events: Cannot connect …. The Docker client library can report a refused connection a moment after it hands control back, so logscry's check occasionally ran first and went on to list the containers, and that request failed instead. Reconnecting always worked; only the reported cause varied, and the project's own test of it failed intermittently. Present since v0.12.34.

Found while working on #155.

What changed:

  • When a retry's container list fails, logscry now waits for the event subscription's own outcome and reports that, so a refused connection is always named as the subscription.
  • Successful reconnects, the number of requests made and the message for a Docker daemon that is unreachable at startup are unchanged.
  • Built with go1.26.10.

Measured:

  • The intermittent test, 500 runs under the race detector on a loaded 20-CPU machine: 58 failures before, 0 after.
  • With the race forced on every run: 100 of 100 failures before, 0 after.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • No new ones from this change. The full list is in the README's Known limitations.

Tracked as #159.

v0.12.35

Choose a tag to compare

@github-actions github-actions released this 30 Oct 10:26
919868a

v0.12.35 — no more 5-second wait at startup on a silent terminal

On a terminal that does not answer the background-colour query (the Linux console, a serial line, script with no terminal behind it, some multiplexer setups), every start of logscry waited 5 seconds before doing anything, --version and --plain included. The terminal UI library asks the terminal for its background colour while the program loads, and waited up to 5 seconds for an answer that never came. Present since the TUI was introduced.

Found while working on #100.

What changed:

  • --version and --plain no longer ask the terminal anything and start at once.
  • The TUI still asks, so its colours keep adapting to dark and light backgrounds, but now waits at most 1 second, then assumes a dark background (or uses COLORFGBG if set).
  • On terminals that answer (most of them), nothing changes: same timing, same colours.
  • Windows is unchanged.
  • Built with go1.26.10.

Measured (median of 10, on a terminal that never answers):

  • logscry --version: 5.05 s → 0.04 s.
  • --plain: 5.05 s → 0.04 s.
  • The TUI's first frame: 5.05 s → 1.05 s.
  • On terminals that answer dark or light: identical timings and identical colours to v0.12.34.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • On a terminal that answers nothing, the TUI still waits up to 1 second at start.
  • #N: the TUI package's tests do not compile on macOS and Windows; the binary itself is unaffected.
  • The full list is in the README's Known limitations.

Tracked as #155.

v0.12.34

Choose a tag to compare

@github-actions github-actions released this 28 Oct 16:54
9b811d0

v0.12.34 — the Docker source survives a broken events stream

When the connection to Docker's event stream broke (Docker Desktop restarting, a laptop waking from sleep, a socket or tunnel blip), logscry's Docker source ended: the containers it was following stopped streaming, and the only way back was to restart logscry, which re-read every container's history. Since v0.12.11 this was at least reported; now it is handled.

What changed:

  • logscry reconnects on its own and keeps following Docker. It asks the daemon to replay the events it missed, re-lists the running containers, and continues each one from the last line it had read, so no line is read twice and none is skipped.
  • A container that started during the outage is picked up; one that started and exited during the outage is read to its end.
  • Retries back off from 1 s to 30 s and continue for as long as logscry runs. The status bar (and stderr with --plain) shows docker: events lost, reconnecting (attempt N, next in Ds): <cause> and then docker: events reconnected, M container(s) re-attached.
  • If Docker is unreachable when logscry starts, it still stops at once with the connection error, as before.
  • The README's Go badge now reads "go ≥ v1.26.1", the minimum Go version to build.
  • Built with go1.26.10.

Measured:

  • Live on Docker 29.9.2, cutting the connection mid-run: v0.12.33 delivered 20 of a running container's 102 lines and never saw containers started during the cut; v0.12.34 delivered 102 of 102, all 31 lines of a container started during the cut, and all 5 of one that started and exited during it.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • A container that starts and exits during an outage longer than Docker's event buffer (its last 256 events), a --rm container that exits during any outage, and lines written while the Docker daemon itself is down can still be missed.
  • #155: on a terminal that does not answer the background-colour query, logscry waits 5 s before starting.
  • The full list is in the README's Known limitations.

Tracked as #100.

v0.12.33

Choose a tag to compare

@github-actions github-actions released this 28 Oct 12:18
55011d4

v0.12.33 — the Docker client moves to the maintained github.com/moby/moby modules

logscry talked to Docker through github.com/docker/docker, the module that also contains the Docker daemon. That module has stopped receiving releases since Moby v29 split it, and it carries several daemon-side security advisories with no patched version. logscry only ever used the client side (listing containers, following logs and events), so those advisories did not reach it, but they appeared in every dependency scan of the project and in the shipped binaries' module list.

What changed:

  • logscry now uses github.com/moby/moby/client and github.com/moby/moby/api, the maintained modules that replaced the client and API types.
  • github.com/docker/docker is gone from the dependency graph, and so are the OpenTelemetry trace exporter (which carried an advisory of its own) and seven other indirect modules.
  • The Docker source behaves exactly as before: the same containers, lines, stdout/stderr tagging, TTY handling, timestamps, auto-attach and error messages.
  • Docker Engine 19.03 or newer is now required (API 1.40). Against an older daemon, logscry reports docker: list: … client version 1.56 is too new instead of attaching. Docker 19.03 has been out of support for years.
  • Built with go1.26.10.

Measured:

  • govulncheck: two warnings in v0.12.32, no findings at any level in v0.12.33.
  • On a live Docker 29.9.2, v0.12.32 and v0.12.33 side by side: identical requests (apart from the negotiated API version), identical output and escalations.
  • The binary grows by 0.7%.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • No new ones from this change. The full list is in the README's Known limitations.

Tracked as #152.

v0.12.32

Choose a tag to compare

@github-actions github-actions released this 27 Oct 22:37
5bbdf83

v0.12.32 — release binaries are built with Go 1.26.10

Every release binary up to v0.12.31 was built with Go 1.26.1, because the build took its Go version from the module's minimum. That toolchain's standard library has 23 known vulnerabilities reachable from logscry's code, fixed in Go 1.26.2 through 1.26.10, most of them in net/http, crypto/tls and crypto/x509: the code logscry uses to reach a remote LLM endpoint over TLS. A local Ollama endpoint was less exposed, but every downloaded binary carried them.

What changed:

  • Built with go1.26.10, the latest 1.26 release. The build now pins the toolchain explicitly, and both CI and the release job fail if they ever run with a different Go version.
  • logscry --version now also prints the Go version that built the binary: logscry v0.12.32 (go1.26.10).
  • CI runs govulncheck on every change and weekly. It flags any reachable vulnerability that has a fix available, so the next Go patch release cannot go unnoticed.
  • Installing with go install still builds with your own Go toolchain (1.26.1 or newer); use a current one.

Measured:

  • govulncheck, vulnerabilities reachable from logscry's code in the standard library: 23 with Go 1.26.1, 0 with Go 1.26.10.
  • In the shipped binaries: 41 standard-library findings in v0.12.31, 0 in a build of this release.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • #152: the Docker SDK dependency (github.com/docker/docker) carries daemon-side advisories with no patched version; logscry uses only the client. The migration to github.com/moby/moby/client is next.
  • The full list is in the README's Known limitations.

Tracked as #151.

v0.12.31

Choose a tag to compare

@github-actions github-actions released this 27 Oct 21:33
a4628aa

v0.12.31 — Spring Boot, Logback, Python and other plaintext logs get their level read

logscry read a plaintext log level only when it stood at the very start of the line. Most logging frameworks print their own timestamp, process id or thread first, so the level of a line such as Spring Boot's default 2026-01-15 10:23:45.123 ERROR 12345 --- [main] com.example.App : boom was never seen. On stdin, a subprocess, Docker, and journald's default priority, such a line got no level: a new error in it scored like any new INFO line and did not interrupt. Present since v0.1.0.

Found while working on #24.

What changed:

  • When a line does not start with a level, logscry now walks past the logger's own header (timestamps, pids, threads, bracketed columns, source positions, a logger name column) and reads the first level column it reaches.
  • Read now: Spring Boot's console layout, Logback and Log4j2 defaults, Python's common formats, Celery, gunicorn, loguru, Go's log and slog, Rails, nginx, PostgreSQL and Kubernetes' klog.
  • The first ordinary word ends the walk, so a level mentioned in prose (retrying after ERROR from upstream) is not read, and a line that merely starts with a level word (PANIC button pressed) needs a delimiter after it to count.
  • Templates are unchanged: the message stays the whole line.

Measured:

  • 10 real journald boots, replayed: 113 lines gain a level (62 WARN, 51 INFO, none error-class); no template changes; escalations 59 before and after, every export byte-identical.
  • 400 benign INFO templates still produce no escalation.

Costs, pinned in the tests:

  • A level word right after header-like material is read as a level, e.g. 42 ERROR rows skipped.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • Chromium-family lines keep their in-bracket level unread on purpose: on a desktop it is browser chatter, and reading it took ten boots from 59 to 141 escalations.
  • ANSI-coloured lines, and level words outside the known vocabulary (JUL SEVERE, nginx alert, .NET fail), are not read yet.
  • The full list is in the README's Known limitations.

Tracked as #31.

v0.12.30

Choose a tag to compare

@github-actions github-actions released this 27 Oct 16:02
9a8b431

v0.12.30 — a vendor API key is no longer sent in part when another rule matches inside it

With --llm-anonymize, an API key could be split and partly sent to the model endpoint when its random body happened to contain something another masking rule recognises first: a short sk- key shape after a -, a fragment of another vendor's key, an AWS access key id, a private host suffix such as .lan, a JWT, a UUID or a home directory. The vendor-key rule then masked only the part on one side, and the rest went out in the clear. This happened mostly on the line's template, for a few keys in every ten thousand. The fail-closed re-scan read the remainder as clean. Stripe, GitHub, Slack and Google keys were affected since v0.12.15; the formats added in v0.12.29 and the AWS secret access key in v0.12.29 only. Twilio SIDs were never affected.

This fixes a credential leak. It was handled as a private security advisory: GHSA-8qq6-vx8j-rx37.

Found while working on #116.

What changed:

  • The vendor-key and AWS-secret rules still run last, but each also reads the line once more, on its own. Wherever an earlier rule left part of a key unmasked, the whole key now becomes one placeholder.
  • The same change closes a rarer case in v0.12.29: an AWS secret key sent whole after an access key id longer than a real one.
  • No masking rule changed or moved.

Measured:

  • 5,484,900 generated keys across every format, placement and escape: 582,379 were split in v0.12.29, none in v0.12.30.
  • With the new pass switched off, the code matches v0.12.29 byte for byte; a test requires that every byte v0.12.29 masked is still masked.
  • Wire check against a capturing endpoint: v0.12.29 sends key text on 16 of 17 test lines, v0.12.30 on none.
  • 10 real journald boots: nothing changes. Masking costs 18–29% more per escalation; it runs once per escalation, not per log line.

Costs, pinned in the tests:

  • A value that overlapped a split key, such as a host label, now goes into the key's placeholder.
  • A handful of generated lines that v0.12.29 sent with part of a key are now refused whole instead.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • No new ones from this change. The full list is in the README's Known limitations.

Tracked as GHSA-8qq6-vx8j-rx37.

v0.12.29

Choose a tag to compare

@github-actions github-actions released this 27 Oct 11:07
c819b9c

v0.12.29 — more vendor API keys, AWS secret keys and webhook URLs are masked

With --llm-anonymize, a number of common secret formats matched no masking rule and were sent to the model endpoint whole on the alerting line, its source and context, and on the line's template minus their digits: Twilio key and account SIDs, SendGrid, npm, GitLab and Hugging Face tokens, Shopify access tokens and app secrets, PyPI upload tokens, the AWS secret access key, and the secret part of Slack and Discord webhook URLs. The fail-closed re-scan read every such line as clean. Present since v0.4.0, when --llm-anonymize was introduced. With a local Ollama nothing left the machine.

This fixes a credential leak.

Found while working on #114.

What changed:

  • Twilio SK…/AC…, SendGrid SG.…, npm npm_…, GitLab (glpat-… and 12 more documented prefixes), Hugging Face hf_…, Shopify shpat_…/shpca_…/shppa_…/shpss_… and PyPI pypi-AgE… are masked as one placeholder each, on the raw line and on the template.
  • An AWS secret access key is masked when its name stands beside it (AWS_SECRET_ACCESS_KEY=, INI, YAML, JSON including SecretAccessKey, CLI flags) or when it follows an AKIA…/ASIA… key id.
  • In a Slack (hooks.slack.com/services/…) or Discord (discord.com/api/webhooks/…) webhook URL, the IDs and the token in the path are masked; the query stays visible.
  • A glued Slack user token's template no longer sends its hex letters.
  • The README's list of what is masked now names every vendor format, including Stripe, GitHub, Slack and Google, which were masked since v0.12.15 but not listed.

Measured:

  • A test runs the whole anonymizer against v0.12.28 and requires that every byte v0.12.28 masked is still masked.
  • False positives, counted over every .go file in a Go module cache (18,658 files), the Go standard library, 10 real journald boots and the project's test strings: 0 for every format.
  • Wire check against a capturing endpoint: v0.12.28 sends each secret, v0.12.29 sends none, in any field.

Costs, pinned in the tests:

  • An uppercase hex run glued after AC on a template is read as a Twilio SID.
  • A run of exactly 40 key characters after any masked secret is read as an AWS secret key.
  • A webhook's IDs and token become one placeholder.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • An AWS secret access key with no name or key id beside it, or after a name such as secret_key, is still sent.
  • GitLab tokens are matched by an inferred shape: a body shorter than 20 characters would be sent. The _gitlab_session cookie is not covered.
  • Twilio's actual secrets (the Auth Token and API key secret) have no prefix; only the SIDs are masked.
  • Slack workflow and trigger webhooks, and GovSlack, are not covered.
  • The full list is in the README's Known limitations.

Tracked as #116.

v0.12.28

Choose a tag to compare

@github-actions github-actions released this 26 Oct 18:35
2aefad3

v0.12.28 — a log line that contains one of logscry's own placeholders is no longer mixed up with a real value

With --llm-anonymize, a log line that already contained text shaped like logscry's own placeholders, such as <HOST_1> (logscry's output fed back in, or a test log), got that same placeholder assigned to a real value. login as <HOST_1> from db.corp.internal was sent as login as <HOST_1> from <HOST_1>, so the model was told the same host appeared twice. On the card the explanation came back as login as db.corp.internal from db.corp.internal: a real value where the log had literal text. A literal in a context line could take the alerting line's value the same way. In v0.12.27, a line with an escaped URL and such a collision was refused instead of sent. Present since v0.4.0.

This was a correctness defect, not a leak: nothing more reached the model endpoint.

Found while working on GHSA-p47p-xrhj-3x7p.

What changed:

  • Before masking a request, the anonymizer reserves every placeholder that already appears in it as text, and never assigns a reserved one: the line above is now sent as login as <HOST_1> from <HOST_2>, and the card shows login as <HOST_1> from db.corp.internal.
  • A placeholder the anonymizer never assigned is restored as the literal text it was.
  • Escaped lines with such text, refused in v0.12.27, are sent again.
  • Lines without placeholder-shaped text are masked exactly as before.

Measured:

  • Against v0.12.27, over more than 300,000 inputs, including 174,516 records from 10 real journald boots: no masked byte moves; only placeholder numbering changes, and only on lines that carry placeholder text.
  • Wire check against a capturing endpoint: v0.12.27 sends one placeholder for two different things and the card shows the wrong value; v0.12.28 sends distinct placeholders and the card shows what the log said.
  • No measurable cost on ordinary lines.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • No new ones from this change. The full list is in the README's Known limitations.

Tracked as #142.

v0.12.27

Choose a tag to compare

@github-actions github-actions released this 26 Oct 15:05
0cb15fd

v0.12.27 — a JSON-escaped connection string no longer sends its credentials

With --llm-anonymize, a URL or connection string whose slashes a JSON encoder had escaped was not recognised at all, and its username, password and host went to the model endpoint. PHP's json_encode writes slashes this way by default, and so does any JSON string nested inside another. {"dsn":"postgres:\/\/svcuser:hunter2@pgprimary\/app"} was sent as written. The same held for \u002f, \u002F, a doubly escaped \\/, and an encoder that also escapes : and @ (\u003a, \u0040). The values reached the model in the alerting line, its source, the context of every later escalation and, for plain-text and logfmt lines, the line's template. The fail-closed re-scan read every such line as clean. Present since v0.4.0, when --llm-anonymize was introduced: 52 releases. With a local Ollama nothing left the machine.

This fixes a credential leak. It was handled as a private security advisory: GHSA-p47p-xrhj-3x7p.

Found while working on #59.

What changed:

  • The anonymizer now also reads each line with its JSON escapes resolved and masks every value it finds there, in the line as written. The model still receives the original text, escapes included, apart from the placeholders.
  • Everything v0.12.26 masked is still masked: the result is the union of both readings.
  • A line without any escape is processed exactly as in v0.12.26.
  • No masking rule changed.

Measured:

  • A test runs the whole anonymizer against v0.12.26's and requires that every byte v0.12.26 masked is still masked. On 22,438 inputs: 0 less, none refused.
  • Wire check against a capturing endpoint: v0.12.26 and v0.4.0 send the username, password and host; v0.12.27 sends none, in any field.
  • 10 real journald boots (232,688 masked records): nothing changes.
  • An ordinary line costs the same as before (0.99×); a line with an escape takes about twice as long.

Costs, pinned in the tests:

  • Where an older rule had read an escape's letters as part of a value (u002fappuser@…), those letters stay masked, so a backslash can sit beside a placeholder.
  • \/ outside a JSON string, such as an escaped regex or C:\/Users\/…, is read as a path and may be masked.
  • A line that contains both an escape and one of logscry's own placeholders as literal text is refused rather than sent.
  • Where v0.12.26 had masked a credential's @ by accident, the escaped form is now one placeholder, and the host has no tag of its own.

Install: download the binary for your OS from the assets below, or go install github.com/maxie7/logscry/cmd/logscry@latest.

Known limitations:

  • On the template of a plain-text or logfmt line, a credential whose : or @ is written as a \u escape is not read.
  • Other \u escapes of URL characters, such as \u002e for a dot, are not read.
  • The full list is in the README's Known limitations.

Tracked as GHSA-p47p-xrhj-3x7p.