README
¶
devguard-maint
Maintenance CLI for DevGuard. Handles release management, log inspection, and scanner documentation generation.
Installation
go install github.com/l3montree-dev/devguard/cmd/devguard-maint@latest
Or build from source inside the devguard repo:
go build -o devguard-maint ./cmd/devguard-maint && mv devguard-maint $(go env GOPATH)/bin
Directory layout requirement
All release commands work with sibling directories. Before running any release subcommand, navigate to the parent directory that contains all DevGuard repositories side by side:
~/workspace/
├── devguard/ ← main backend repo (also where you build this tool)
├── devguard-web/ ← frontend repo
├── devguard-helm-chart/ ← Helm chart repo
├── devguard-ci-component/ ← CI component repo
├── devguard-docker-deployment/ ← Docker Compose deployment repo
└── devguard-documentation/ ← public documentation repo
cd ~/workspace # <-- run devguard-maint from here, NOT from inside a repo
devguard-maint release devguard v1.8.0
The directory names must match exactly:
| Expected name | Repo |
|---|---|
devguard |
github.com/l3montree-dev/devguard |
devguard-web |
github.com/l3montree-dev/devguard-web |
devguard-helm-chart |
github.com/l3montree-dev/devguard-helm-chart |
devguard-ci-component |
github.com/l3montree-dev/devguard-ci-component |
devguard-docker-deployment |
github.com/l3montree-dev/devguard-docker-deployment |
devguard-documentation |
github.com/l3montree-dev/devguard-documentation |
Commands
release devguard <tag>
Tags and pushes the devguard backend only. Fails if devguard/CHANGELOG.md has no entry for <tag>.
Before tagging, it also runs make docs inside devguard, failing if that command errors. If it produces changes under devguard/docs, those are automatically committed (docs: regenerate for <tag>) and pushed. It then copies the generated devguard/docs/scanner/*.md files into devguard-documentation/src/pages/reference/scanner/, and if that produces changes, commits (docs: regenerate scanner reference for <tag>) and pushes them in the devguard-documentation repo too.
Requires a devguard-documentation sibling directory (see layout above).
devguard-maint release devguard v1.8.0
release web <tag>
Bumps package.json, commits, tags, and pushes devguard-web only. Fails if devguard-web/CHANGELOG.md has no entry for <tag>.
devguard-maint release web v1.8.0
release docker-deployment <tag>
Updates devguard-docker-deployment/.env.example with the latest detected devguard and devguard-web patch tags for the same minor version (devguard/postgresql/kratos all track the devguard tag), then commits and pushes devguard-docker-deployment. Not tagged — this repo has no version tags of its own. Fails if:
devguard-docker-deployment/CHANGELOG.mdhas no entry for<tag>(you will be offered to auto-generate one documenting just the image bumps)- No
devguardordevguard-webrelease exists with the same minor version
devguard-maint release docker-deployment v1.8.1
release helm-chart <tag>
Regenerates Chart.yaml and values.yaml from the schema with the latest detected devguard, devguard-web, and devguard-ci-components patch tags for the same minor version (postgresql/kratos track the devguard tag), then commits, pushes, and tags devguard-helm-chart. Fails if:
devguard-helm-chart/CHANGELOG.mdhas no entry for<tag>- No
devguard,devguard-web, ordevguard-ci-componentsrelease exists with the same minor version
devguard-maint release helm-chart v1.8.1
release ci-components <tag>
Pins the devguard scanner image in src/container-image-versions.ts, runs bun run generate to regenerate all templates, tags devguard-ci-component, then reverts and regenerates again so main always uses scanner:main. Fails if:
devguard-ci-component/CHANGELOG.mdhas no entry for<tag>- No
devguardrelease exists with the same minor version
Requires bun to be installed.
devguard-maint release ci-components v1.8.0
docs [output-dir]
Generates markdown documentation for devguard-scanner into output-dir (default: docs/scanner).
devguard-maint docs
devguard-maint docs /tmp/scanner-docs
logs
Inspect and correlate log files from any devguard component. The format is detected per file:
| Format | Produced by | Notes |
|---|---|---|
api |
devguard-api, scanner (zerolog console writer) | wall clock only — no date, no timezone |
kratos |
Ory Kratos (logfmt) | absolute timestamps |
postgres |
PostgreSQL server log | absolute timestamps; DETAIL/HINT/STATEMENT/CONTEXT are folded into the entry they annotate |
web |
devguard-web (Next.js server output) | no timestamps; stack frames folded into the error, grouped by Next.js digest |
Levels are normalised to DBG/INF/WRN/ERR/FTL across every format, so
--level, errors and timeline behave the same whichever log you point them
at. Override detection with --format/-F, and check what a file looks like with
logs formats.
Any log collected with kubectl logs --timestamps is also understood — the
RFC3339 prefix is stripped and used as the entry's timestamp. That is the only
way to put the Next.js web log on a timeline.
Log content is printed in full: nothing is truncated by width, and the continuation lines of a multi-line entry are printed indented under it. Lines matching neither a format's parse nor its continuation rules are counted and reported on stderr rather than silently dropped.
devguard-maint logs -f api.log summary # levels, top sources, top messages
devguard-maint logs -f api.log errors # every ERR and FTL entry
devguard-maint logs -f api.log filter -l ERR -c auswaertiges-amt
devguard-maint logs -f postgres.log timeline # per-minute level histogram
devguard-maint logs -f api.log durations # request latency plotted over time
devguard-maint logs -f web.log formats # what did it detect, and why
Counting a kind of failure
summary groups on the exact message, so events carrying a request id, address
or duration each land in their own bucket. --normalize/-N replaces those tokens
with placeholders first, which collapses one kind of failure into one row.
Combine it with --top/-t to reach past the default 15:
devguard-maint logs -f api.log summary -N --top 200 | grep "connection refused"
12 could not get session from cookie error="... dial tcp <addr>: connect: connection refused"
12 critical error encountered msg="kratos: could not get identity from cookie" error="... <addr> ..."
2 failed to get identity err="... /admin/identities/<uuid> ... <addr> ..."
Tokens replaced: <ts>, <date>, <uuid>, <addr> (IP, optionally with port),
<hex> (16+ hex chars), <dur>, <n>. For a plain occurrence count of one
substring, filter -c "connection refused" reports the number of matches.
logs durations
Plots request latency over time from the api log's handled request entries,
which are the only ones carrying a duration= field. URLs are reduced to their
route — query string dropped, org, project, asset, ref and id segments replaced
by placeholders — so the same endpoint hit against different assets groups into
one row.
devguard-maint logs -f api.log durations
devguard-maint logs -f api.log durations --bucket second --slow 5s
devguard-maint logs -f api.log durations --route stats/risk-history
Sections, in order: overall percentiles, the per-bucket plot, stalls, the routes that consume the most total time, the slowest individual requests, and the precursor ranking.
The plot's INFLIGHT column is the one that usually explains a slowdown. It is
reconstructed by subtracting each entry's duration from its completion time, so
it counts requests that were still running during a bucket rather than those
that finished in it. A bucket completing 8 requests while 116 were in flight is
a queue, not idleness:
BUCKET REQS INFLIGHT P50 P95 MAX P95
08:24 8 21 58.31s 1.0m 1.0m ! ███████████
08:25 0 18 - - - ! (no request completed)
08:28 116 163 904ms 3.7m 3.9m ! ████████████████████████████████████████
A bucket where nothing completed at all is kept as a row rather than skipped,
and consecutive ones are summarised under Stalls. On an instance serving
traffic every minute those are hard outages, and they are invisible in a plot
that only draws buckets it has data for.
Buckets whose p95 crosses the slow threshold are marked !. The threshold
defaults to the p95 of the worst tenth of buckets, so it scales with the log;
pin it with --slow.
The precursor section takes each latency onset — a slow bucket whose predecessor
was not slow — and ranks the non-request event kinds over-represented in the
buckets just before it, by lift over their baseline rate. It ranks coincidence
rather than cause: an event that only ever fires under load scores high whether
it is the trigger or another symptom. Tune the window with --lead, or skip the
section with --no-precursors.
logs correlate <file> <file> [file...]
Lines several logs up on one timeline so an error in one component can be read
against what every other component was doing at that instant. The matrix shows
total/errors per bucket, and ! marks any bucket containing an ERR or FTL.
devguard-maint logs correlate api.log kratos.log postgres.log --only-errors
BUCKET api kratos postgres
! 2026-08-11 14:09 31/4 72 16
! 2026-08-11 14:11 431/25 121 50
! 2026-08-11 14:12 142/2 290 92
Then drop into the actual interleaved lines around a moment of interest:
devguard-maint logs correlate api.log kratos.log --around 14:11 --window 30s
Timestamps differ per component, and this is the main source of wrong
conclusions. Postgres and Kratos log absolute dates in UTC. The api log prints
a wall clock with no date and no timezone; it is anchored so its last entry lands
on the last date seen in the dated logs, and is assumed to share their zone —
the header states when a date was inferred. Its stamps have minute resolution, so
api entries land at second :00 relative to postgres and kratos. If a log really
is in another zone, shift it:
devguard-maint logs correlate api.log kratos.log --offset api=+2h
A log with no timestamps at all cannot be aligned; it is reported separately with its errors listed, not folded silently into the matrix.
Other flags: --bucket second|minute|hour, --level, and --date YYYY-MM-DD to
anchor undated logs explicitly.
Typical release order
- Update all CHANGELOGs with the new version entry
release devguard <tag>— backendrelease web <tag>— frontend (can be skipped for backend-only patches)release ci-components <tag>— CI templates (auto-detects latest scanner tag)release docker-deployment <tag>— Docker Compose deployment (auto-detects latest backend/web tags)release helm-chart <tag>— Helm chart (auto-detects latest backend/web/ci-components tags)
Documentation
¶
There is no documentation for this package.