Measured on a live 0.18.3 daemon (6 modules, 48 clients) after another seat hit a rotation boundary it took for a quiet period. The retention policy is fine. What sets its effective depth is volume, and the volume is almost entirely two lines.
What fills the file
Current generation, 5 h 29 m, all written by the real daemon:
15537 74.0% INFO subc authenticated connection count changed
5445 25.9% INFO subc route.open accepted
13 0.1% WARN subc subc connection ended with error
The first line alone is 2.27 MB of the file's 2.86 MB (79% of bytes). Everything an operator reads this file for — refusals, drains, module registration, warnings — is the remaining 0.1%.
Why that matters: retention is by size
The daemon has no log block in subc.jsonc here, so it uses cortexkit_log::Retention::default() at the pinned rev: max_file_mb: 32, keep: 2, max_age_days: 14. rotate_paths shifts .1 → .2 and the live file to .1, deletes .2, and does no compression. So the most you ever have is the live file plus two 32 MiB archives, and anything older is deleted.
Measured rates: the closed generation filled 32 MiB at ~7.8 MiB/day of real-daemon output, and the live one is running at ~11.9 MiB/day. That gives ~3–4 days per generation, and roughly 6–12 days of total history. The 14-day age cap never comes into play at this rate, because size forces rotation well before age would.
So a WARN written today survives about as long as it takes 48 clients to reconnect enough times to fill 64–96 MiB. Retention of rare events depends on how often connections churn, and nobody picks that number. A route storm or a fleet restart can push 32 MiB through in hours, and when that happens the refusal lines from the incident being investigated are what gets deleted.
If only the connection-count line moved to debug!, a generation would last about 5× longer, and the rare lines would effectively be kept until the 14-day age cap, which is the policy the defaults seem intended to express.
On route.open accepted
That line was added on purpose in 06461269 ("record accepted route.open, not only refusals"), and I'm not proposing to reverse it without knowing why. If it's there so the accepted/refused ratio can be read from the log, a counter next to route_open_refused_by_code would carry the same information with no log volume. If it's there for per-route forensics, then the connection-count line should go first. At 74% of the file it's the larger lever, and nothing I can find reads it that connected_clients on server.describe doesn't already provide.
I suggested demoting it on #94. That issue closed with the refusal telemetry, and the line is still info! at observability.rs:80 and :102 on f193a178.
Options
- Demote
authenticated connection count changed to debug!. One-line change and most of the gain. connected_clients is still available from ck daemon.
- (1), and move
route.open accepted to a counter, if the ratio is its purpose.
- Leave the levels as they are and set a larger default
keep/max_file_mb for the daemon's own file. This also works, but it spends disk to hold lines nobody reads, and because Retention is shared with module capture files, it would change their policy as well.
My preference is (1). It fixes the problem without touching cortexkit-log or the capture-file policy.
Something that affects anyone reading these files for forensics, found while measuring this: 493 of the 496 subc daemon starting lines across the two generations come from test daemons, not the real one. They're subc-client-rs real-daemon suites writing into the production log because their data home was not isolated. ba59b085 fixed that, and the last one is at 2026-09-19T07:22Z. The lines stay in the archive until it rotates out, though, so anyone locating a boot by that line needs to also filter on connection_file= pointing at the real runtime dir, not under /tmp/.
Measured on a live 0.18.3 daemon (6 modules, 48 clients) after another seat hit a rotation boundary it took for a quiet period. The retention policy is fine. What sets its effective depth is volume, and the volume is almost entirely two lines.
What fills the file
Current generation, 5 h 29 m, all written by the real daemon:
The first line alone is 2.27 MB of the file's 2.86 MB (79% of bytes). Everything an operator reads this file for — refusals, drains, module registration, warnings — is the remaining 0.1%.
Why that matters: retention is by size
The daemon has no
logblock insubc.jsonchere, so it usescortexkit_log::Retention::default()at the pinned rev:max_file_mb: 32, keep: 2, max_age_days: 14.rotate_pathsshifts.1 → .2and the live file to.1, deletes.2, and does no compression. So the most you ever have is the live file plus two 32 MiB archives, and anything older is deleted.Measured rates: the closed generation filled 32 MiB at ~7.8 MiB/day of real-daemon output, and the live one is running at ~11.9 MiB/day. That gives ~3–4 days per generation, and roughly 6–12 days of total history. The 14-day age cap never comes into play at this rate, because size forces rotation well before age would.
So a
WARNwritten today survives about as long as it takes 48 clients to reconnect enough times to fill 64–96 MiB. Retention of rare events depends on how often connections churn, and nobody picks that number. A route storm or a fleet restart can push 32 MiB through in hours, and when that happens the refusal lines from the incident being investigated are what gets deleted.If only the connection-count line moved to
debug!, a generation would last about 5× longer, and the rare lines would effectively be kept until the 14-day age cap, which is the policy the defaults seem intended to express.On
route.open acceptedThat line was added on purpose in
06461269("record accepted route.open, not only refusals"), and I'm not proposing to reverse it without knowing why. If it's there so the accepted/refused ratio can be read from the log, a counter next toroute_open_refused_by_codewould carry the same information with no log volume. If it's there for per-route forensics, then the connection-count line should go first. At 74% of the file it's the larger lever, and nothing I can find reads it thatconnected_clientsonserver.describedoesn't already provide.I suggested demoting it on #94. That issue closed with the refusal telemetry, and the line is still
info!atobservability.rs:80and:102onf193a178.Options
authenticated connection count changedtodebug!. One-line change and most of the gain.connected_clientsis still available fromck daemon.route.open acceptedto a counter, if the ratio is its purpose.keep/max_file_mbfor the daemon's own file. This also works, but it spends disk to hold lines nobody reads, and becauseRetentionis shared with module capture files, it would change their policy as well.My preference is (1). It fixes the problem without touching
cortexkit-logor the capture-file policy.Something that affects anyone reading these files for forensics, found while measuring this: 493 of the 496
subc daemon startinglines across the two generations come from test daemons, not the real one. They'resubc-client-rsreal-daemon suites writing into the production log because their data home was not isolated.ba59b085fixed that, and the last one is at 2026-09-19T07:22Z. The lines stay in the archive until it rotates out, though, so anyone locating a boot by that line needs to also filter onconnection_file=pointing at the real runtime dir, not under/tmp/.