Fix deploy env-template gaps and stale ai-agent docs from discussion #305#312
Conversation
- Document the agent_type llm/acp split and where each type's credentials live (overview.md, api-design.md), including the new ACP bridge token endpoints. - deploy/.env.production.example: prefix image vars with the pacaai/ registry namespace, add PACA_AI_AGENT_IMAGE, document SITE_ADDRESS, and clarify that INTERNAL_API_KEY is required even with the AI agent scaled to 0 while AGENT_API_KEY and ENCRYPTION_KEY are not. - deploy/README.md: document PACA_YES=1 for non-interactive/CI installs. - Warn at startup when ENCRYPTION_KEY is unset, since secrets were previously persisted in plaintext with no signal. Addresses self-host install feedback from https://github.com/orgs/Paca-AI/discussions/305 Co-authored-by: HCH-hash <278330872+HCH-hash@users.noreply.github.com>
ListConversations/ListTasks/ListProjects/ListUsers fell back to the default page_size on any invalid value (0, negative, >max, non-numeric), silently returning a different page size than the caller asked for and breaking offset math for paginators computing offsets as N * page_size. parsePageSize() now returns 400 for an explicit invalid value; an absent value still defaults normally. Co-authored-by: HCH-hash <278330872+HCH-hash@users.noreply.github.com>
There was a problem hiding this comment.
Important
Stale e2e pagination tests will break — see below.
Reviewed changes — clarifies ACP vs LLM agent credential requirements in self-host documentation and changes page_size query parameter handling from silent clamping to explicit rejection in four REST handlers, with a new parsePageSize helper.
- Document ACP / LLM agent type split in API design docs — rewrites
docs/ai-agent/api-design.mdanddocs/ai-agent/overview.mdto cover both agent types, adds new endpoints for ACP bridge token/status, agent env vars, skill templates, and LLM models. Rewrites conversations endpoint from agent-scoped to project-scoped cursor pagination. - Add
SITE_ADDRESS,INTERNAL_API_KEY, andAGENT_API_KEYto deploy environment —deploy/.env.production.exampleanddeploy/README.mdnow document the TLS/domain control var, the unconditional internal key requirement, and the optional agent callback key. Also documentsPACA_YES=1for CI installs. - Surface missing
ENCRYPTION_KEYas a startup warning —app.gologs aWarnwhenENCRYPTION_KEYis absent, replacing the previous silent fallback to plaintext storage. - Reject invalid
page_sizeinstead of silently clamping —helpers.gogainsparsePageSize; four handlers (conversation,task,user,project) switch from manualAtoi+clamp toparsePageSizewith error propagation. Unit tests updated.
⚠️ Stale e2e pagination tests still assert silent page_size clamping
The e2e test suites for task and conversation pagination were not updated to match the new rejection behavior. They send page_size=0 and page_size=201 and assert a 200 response with the value clamped to 20 — but the handlers now return 400 Bad Request for both.
The unit-level conversation_handler_test.go was updated correctly, but the corresponding e2e tests were missed. These will fail on CI.
Technical details
# Stale e2e pagination tests still assert silent `page_size` clamping
## Affected sites
- `services/api/test/e2e/task_pagination_test.go:171` — `page_size_zero_is_clamped_to_default` asserts 200 with `page_size=20` when `page_size=0` is sent; the handler now returns 400.
- `services/api/test/e2e/task_pagination_test.go:178` — `page_size_over_max_is_clamped_to_default` asserts 200 with `page_size=20` when `page_size=201` is sent; same issue.
- `services/api/test/e2e/conversation_pagination_test.go:210` — `page_size_zero_is_clamped_to_default` same pattern for the conversations endpoint.
- `services/api/test/e2e/conversation_pagination_test.go:217` — `page_size_over_max_is_clamped_to_default` same pattern.
## Required outcome
- Update these four e2e test cases to expect HTTP 400 (Bad Request) and an error body, with a matching `{"error": "page_size must be an integer between 1 and 200"}` assertion, instead of asserting a successful clamped response.DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏
There was a problem hiding this comment.
Reviewed changes — one commit addresses the prior review's e2e test concern and does a cosmetic parameter rename in parsePageSize.
- Updated e2e pagination tests to match the
page_sizerejection behavior — the four stale test cases that asserted silent clamping forpage_size=0andpage_size=201inconversation_pagination_test.goandtask_pagination_test.gonow assert HTTP 400 Bad Request, matching the handler logic introduced in0ac3dbe. - Renamed
parsePageSizeparametermax→maxSizefor consistency with thedefaultSizeparameter name.
DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏
…s, adding validation for offset and limit parameters

Summary
Follow-up to discussion #305 (first-install feedback from @HCH-hash): fixes the deploy/env-template gaps it identified, plus two doc and code issues found while investigating the ACP-agent credential question in that thread.
Docs:
docs/ai-agent/overview.md,docs/ai-agent/api-design.mdBoth predated the ACP agent feature entirely — they described a single LLM/Docker-sandboxed execution model, a since-removed "Agent Type" template concept, and REST paths/response shapes that no longer match the router (
GET /api/v1/agent-types404s; conversations moved from agent-scoped to project-scoped; several envelopes and query params had drifted). Rewrote both against the current router, DTOs, and service layer, including:agent_type(llm/acp) split and where each executespaca-acp-bridgedaemon — never anAGENT_API_KEYorllm_api_keyDeploy:
deploy/.env.production.example,deploy/README.mdPACA_*_IMAGEdefaults were missing thepacaai/registry prefix the compose file actually falls back to (barepaca-api:latestetc. don't resolve)INTERNAL_API_KEY,AGENT_API_KEY,SITE_ADDRESS, andPACA_AI_AGENT_IMAGE, none of which were in the template despite being referenced bydocker-compose.prod.ymland the README's own manual-setup instructionsINTERNAL_API_KEYis unconditionally required by theapiservice at startup — including with--scale ai-agent=0— which the README previously implied was conditional on using the AI agentPACA_YES=1, the install script's existing non-interactive/CI mode, which wasn't mentioned anywhere indeploy/README.mdAPI: pagination + encryption-key visibility
task,conversation,project, anduserlist handlers silently substituted a defaultpage_sizefor any out-of-range or non-numeric value instead of erroring. A caller driving pagination off the size it requested (e.g. computing the next offset asN * page_size) would get a smaller page back with no signal — skipping or duplicating rows against its own math. An absentpage_sizestill defaults silently as before; an explicitly supplied invalid value now returns400via a new sharedparsePageSizehelper.ENCRYPTION_KEYis unset. Previously fully silent:encryptKey()falls back to storing the plaintext unchanged, so LLM-type agent API keys and plugin secrets would end up in the database unencrypted with nothing in the logs to explain why. Left as a warning, not a hard failure — ACP-only deployments with no plugins storing secrets have no use for it.AGENT_API_KEYandINTERNAL_API_KEYwere also investigated as fail-fast candidates:INTERNAL_API_KEYis already a hardrequireEnvfailure at startup, andAGENT_API_KEYis correctly optional by design, so neither needed a code change — just the env-template fix above.Test plan
go build ./...andgo test ./...pass inservices/apigofmt -lclean on touched Go filesinstall.shwithPACA_YES=1end-to-end against the updated.env.production.exampleguidance