The iec-builder runs under PM2 on the builder host and writes structured pino logs to
~/.pm2/logs/iec-builder-out.log and -error.log. Because pino-http logs request metadata,
log lines historically contained plaintext Authorization: Bearer <JWT> tokens. Three layers
now keep those logs safe and bounded.
redact)The pino logger (src/utils/logger.ts) redacts secret-bearing paths from every record before
it is written, censoring with [REDACTED]:
req.headers.authorization, req.headers.cookie,
req.headers["x-internal-key"], req.headers["x-api-key"], req.headers["set-cookie"]res.headers["set-cookie"]*.authorization, *.password, *.secret, *.tokenBIO_CLIENT_SECRET, CONFIG_ENCRYPTION_KEYThis prevents new tokens from ever being written.
The builder host runs pm2-logrotate so logs never grow unbounded:
| Setting | Value |
|---|---|
max_size | 50M |
retain | 14 |
compress | true (gzip) |
rotateInterval | 0 0 * * * (daily at midnight) |
workerInterval | 30 (seconds) |
Rotated archives are named iec-builder-{out,error}__<timestamp>.log.gz and live alongside the
live logs in ~/.pm2/logs. The pre-existing 762 MB log was scrubbed of Bearer tokens before the
first compression so no archive leaks secrets.
Historical lines (written before layer 1 existed) still contain tokens, so every line surfaced
through the API is redacted again at read time via regex
(Bearer <token>, "authorization":"…", "x-internal-key":"…", "cookie":"…").
Both endpoints live in src/routes/platform.ts and require super_admin or service role
(tawa-web uses X-Internal-Key). They read from the same host the builder runs on.
GET /platform/logsReturns the live log inventory, rotated archives, and rotation status.
{
"success": true,
"data": {
"files": [
{ "stream": "out", "path": "...", "sizeBytes": 1234, "sizeHuman": "1.2 KB", "modifiedAt": "2026-06-24T…Z" }
],
"rotated": [
{ "name": "iec-builder-out__2026-06-24_00-00-00.log.gz", "sizeBytes": 1234, "sizeHuman": "1.2 KB", "modifiedAt": "…Z" }
],
"totalBytes": 2468,
"totalHuman": "2.4 KB",
"rotation": { "enabled": true, "maxSize": "50M", "retain": 14, "compress": true }
}
}
files — the two live streams (out, error).rotated — compressed archives in ~/.pm2/logs matching iec-builder-*.totalBytes / totalHuman — sum of live + rotated.rotation — best-effort from pm2 conf pm2-logrotate; fields are null when undeterminable.GET /platform/logs/tail?stream=out|error&lines=N&level=all|error|warnTails a stream by reading only the end of the file (~2 KB budget per line — never the whole 762 MB file).
{
"success": true,
"data": {
"stream": "out",
"lines": [
{ "ts": "2026-06-24T…Z", "level": "error", "msg": "request errored", "raw": "{…redacted…}" }
],
"returned": 1,
"truncated": false
}
}
| Query param | Default | Notes |
|---|---|---|
stream | out | out or error |
lines | 200 | clamped to a max of 1000 |
level | all | all, warn (warn+error+fatal), or error (error+fatal) |
Each line is parsed as pino JSON for ts / level / msg; if parsing fails those fields are
null but raw is still redacted. truncated is true when older lines were dropped to fit the
requested lines.
pm2 jlist (pm_out_log_path / pm_err_log_path) with a
$HOME/.pm2/logs/iec-builder-{out,error}.log fallback.Last updated: July 15, 2026