fix(deploy): distroless volume perms + viewer proxy + budget loop (#299, #301)#304
Conversation
…, #301) @flamerged reported three issues on a real production deployment: 1. (#301) v0.9.7's working-directory fix moved iii-config paths from ./data/... to /data/... so the named volume mount is actually reached. But iiidev/iii is distroless and runs as UID 65532, while `docker volume create` initializes the named volume mountpoint as root:root mode 755. Engine writes fail Permission denied (os error 13), the API silently buffers in RAM, every API call returns success, and state evaporates on every container restart — exactly what 0.9.7 set out to fix. Fix: ship a one-shot iii-init service in docker-compose.yml (busybox:1.36, ~4MB, exits in <100ms) that chowns /data to 65532:65532. iii-engine now has user: "65532:65532" and depends_on.iii-init.condition: service_completed_successfully. Verified live: pre-fix volume stayed 4.0K after API writes; post- fix volume grows to 44K with state_store.db/mem%3A*.bin files written through the named volume. 2. (#299) src/viewer/index.html ports detection hardcoded ':3113' as the fallback when window.location.port is empty (page served on 80/443 behind a reverse proxy). Every browser-side /agentmemory/* fetch went to <host>:3113, which is typically loopback-only on the self-hosted shape — the dashboard rendered cleanly but every panel showed the empty "first run" state. Fix: when neither ?port=N nor window.location.port is set, use window.location.origin as the REST base and window.location.host for the WebSocket URL — same-origin path works for both REST and live updates. Explicit ?port=N / non-default window.location.port paths unchanged. 3. (bundled) mem::context budget loop used `break` on first oversized block. With #288's new pinned-slot injection sorting first via recency: Date.now(), one fat pinned slot could starve every smaller block downstream that would have fit. Switched to `continue` so smaller blocks still slip into remaining budget. Total tokens still bounded by tokenBudget; only composition under contention changes. Bumping 0.9.9 -> 0.9.10 across the 8 standard files (package.json, packages/mcp/package.json, plugin/.claude-plugin/plugin.json, src/version.ts, src/types.ts ExportData literal, src/functions/export-import.ts supportedVersions, the export round-trip test expectation, and CHANGELOG.md). 868 / 868 tests pass. Build clean. Volume + viewer fixes verified end-to-end live.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (11)
📝 WalkthroughWalkthroughThis PR releases version 0.9.10 with three behavioral fixes and infrastructure updates: docker-compose volume permissions initialization, viewer URL construction for reverse-proxy environments on standard ports, and refined context token-budget selection that skips oversized blocks instead of terminating early. All version sources and export-format support are coordinated to the new version. Changes0.9.10 Release Coordination
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Three deployment-shape fixes reported live by @flamerged on a real production deployment, all closed in this PR. Plus a small budget-loop hardening I noticed while reviewing #288's pinned-slot priority semantic.
#301 — distroless engine can't write to root-owned named volume (volume perm fix)
v0.9.7 moved
iii-config.docker.yamlpaths from./data/...to/data/...so the named volume mount is actually reached. But theiiidev/iiiimage is distroless and runs as UID 65532, whiledocker volume createinitializes the named volume mountpoint as root:root mode 755. The engine has nosh/chownto self-heal at startup, so writes failPermission denied (os error 13), the API silently buffers in RAM, every call returnssuccess: true, and state evaporates on every container restart — exactly what v0.9.7 set out to fix.Live repro (before fix, on
main)Fix
docker-compose.ymlnow ships aniii-initone-shot service (busybox:1.36, ~4 MB image, exits in <100 ms) that runschown -R 65532:65532 /data && chmod 755 /dataonce. Theiii-engineservice now declaresuser: "65532:65532"anddepends_on.iii-init.condition: service_completed_successfullyso it never starts before the volume is owner-correct.Verified post-fix (live)
$ docker compose up -d [+] Running 4/4 ✔ Container agentmemory-iii-init-1 Started ✔ Container agentmemory-iii-init-1 Waiting ✔ Container agentmemory-iii-init-1 Exited ✔ Container agentmemory-iii-engine-1 Started $ docker run --rm -v agentmemory_iii-data:/data alpine:3.19 \ sh -c 'stat -c "%U:%G %u:%g %a" /data' UNKNOWN:UNKNOWN 65532:65532 755 # ← owner-correct $ curl -sX POST localhost:3111/agentmemory/remember -d '...' {"success":true,...} $ docker run --rm -v agentmemory_iii-data:/data alpine:3.19 \ sh -c 'find /data -type f; du -sh /data' /data/state_store.db/mem%3Ahealth.bin /data/state_store.db/mem%3Amemories.bin 44.0K /data # ← state persisted through named volumeWhy busybox not alpine? ~3 MB smaller image, same
sh + chown + chmodsurface, identical behaviour for this use.Migration: existing deployments that already hit the bug should
docker compose down && docker compose up -dafter upgrading — the init container fixes the volume in place on first run, no manualchownneeded anymore.#299 — viewer dashboard empty behind reverse proxy on 80/443
src/viewer/index.htmlresolved the REST base URL through:When the viewer is served on port 80 / 443 (e.g. through nginx / Caddy / Nginx Proxy Manager terminating on a public port),
window.location.portis the empty string. The fallback kicks in to the literal'3113', and every browser-side/agentmemory/*fetch hits<host>:3113— typically loopback-bound on the self-hosted shape, so unreachable from outside. The viewer rendered cleanly but every panel showed the empty "first run" state, even whencurl <proxy-host>/agentmemory/sessions(no explicit port) returned correct data.Fix
WebSocket gets the same treatment — defaults to
window.location.host(whatever port the page was served on) instead ofwsPort - 1so live updates work through the proxy too.Bundled —
mem::contextbudget loop nowcontinues on oversized blocksThe selection loop in
src/functions/context.tsusedbreakwhenusedTokens + block.tokens > budget. With #288's new pinned-slot injection sorting first viarecency: Date.now(), a single fat pinned slot at the top of the list could starve every smaller block downstream that would have fit. Switched tocontinueso smaller blocks still slip into the remaining budget. Total tokens still bounded bytokenBudget(default 2000); only the composition under contention changes.Version bump
8 standard files:
package.json,packages/mcp/package.json,plugin/.claude-plugin/plugin.json,src/version.ts,src/types.ts(ExportData.versionliteral),src/functions/export-import.ts(supportedVersionsset),test/export-import.test.ts(round-trip expectation),CHANGELOG.md.Test plan
npm test— 868 / 868.npm run build— tsdown clean.Follow-ups (not in this PR)
iii-hq/iii: ship a thin entrypoint script in the engine image that handles UID 65532 ownership at startup, so the init-container dance isn't needed by every downstream consumer.Summary by CodeRabbit
New Features
Bug Fixes
Chores