Builder Logs — Redaction, Rotation & Admin API
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…
Ruledeploy
About
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.
Skill Content
This is the raw markdown that gets installed as a Claude Code rule.
# Builder Logs — Redaction, Rotation & Admin API
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.
## 1. Write-time redaction (pino `redact`)
The pino logger (`src/utils/logger.ts`) redacts secret-bearing paths from every record before
it is written, censoring with `[REDACTED]`:
- Request headers: `req.headers.authorization`, `req.headers.cookie`,
`req.headers["x-internal-key"]`, `req.headers["x-api-key"]`, `req.headers["set-cookie"]`
- Response headers: `res.headers["set-cookie"]`
- Wildcard fields: `*.authorization`, `*.password`, `*.secret`, `*.token`
- Named secrets: `BIO_CLIENT_SECRET`, `CONFIG_ENCRYPTION_KEY`
This prevents new tokens from ever being written.
## 2. Server-side rotation (pm2-logrotate)
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.
## 3. Read-time redaction + platform-admin Logs API
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/logs`
Returns the live log inventory, rotated archives, and rotation status.
```jsonc
{
"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|warn`
Tails a stream by reading **only the end of the file** (~2 KB budget per line — never the whole
762 MB file).
```jsonc
{
"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`.
## Key facts
- Redaction is two independent layers (write-time pino + read-time regex) — neither alone is
sufficient because of historical lines.
- The logs API never returns secrets even when reading old, unredacted log content.
- PM2 log paths are resolved from `pm2 jlist` (`pm_out_log_path` / `pm_err_log_path`) with a
`$HOME/.pm2/logs/iec-builder-{out,error}.log` fallback.
Install
Copy the skill content and save it to:
~/.claude/rules/tawa-builder-logs.mdComing soon via CLI:
tawa chaac install tawa-builder-logsDetails
- Format
- Rule
- Category
- deploy
- Version
- 1.0.69292
- Tokens
- ~1,023
- Updated
- 2026-06-24
platformlogssecurityredactionrotationplatform-adminpm2