Conversation
| DATABASE_ENCRYPTION_KEY: ${DATABASE_ENCRYPTION_KEY:-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef} | ||
| NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789} | ||
| MEDIA_SERVER_WEBHOOK_SECRET: ${MEDIA_SERVER_WEBHOOK_SECRET:-fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210} | ||
| CRON_SECRET: ${CRON_SECRET:-0f1e2d3c4b5a69780f1e2d3c4b5a69780f1e2d3c4b5a69780f1e2d3c4b5a6978} |
There was a problem hiding this comment.
P2: Do not ship a predictable fallback secret for externally reachable maintenance endpoints
A predictable compose fallback authenticates the new recovery and deletion endpoints if operators omit CRON_SECRET.
Require a unique CRON_SECRET; do not provide a production compose fallback for this endpoint-authentication secret.
AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.
<file name="docker-compose.yml">
<violation number="1" location="docker-compose.yml:20">
<priority>P2</priority>
<title>Do not ship a predictable fallback secret for externally reachable maintenance endpoints</title>
<evidence>The newly added CRON_SECRET configuration supplies a deterministic built-in fallback instead of requiring an operator-provided secret. The same fallback is passed to cap-cron, so anyone who knows the published compose file can authenticate to the maintenance routes when this default is left unchanged. These routes perform recovery and deletion operations, expanding the impact beyond a normal local-development placeholder.</evidence>
<recommendation>Remove the CRON_SECRET fallback and fail the deployment or disable cap-cron when CRON_SECRET is unset. If a local-development default is required, make it opt-in through a clearly named development override and ensure production compose validation rejects it; keep the production and Coolify paths dependent on a unique generated secret.</recommendation>
</violation>
</file>
| # Secret the cap-cron service uses to call Cap Web's scheduled maintenance endpoints | ||
| # Generate with: openssl rand -hex 32 |
There was a problem hiding this comment.
Routine comments violate policy
These comments only restate the variable’s purpose and its standard generation command. This violates the repository directive to avoid code comments unless they preserve non-obvious context from a bug or complex investigation, so this requirement must be satisfied before merging.
| # Secret the cap-cron service uses to call Cap Web's scheduled maintenance endpoints | |
| # Generate with: openssl rand -hex 32 |
Context Used: AGENTS.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: docker-compose.coolify.env.example
Line: 23-24
Comment:
**Routine comments violate policy**
These comments only restate the variable’s purpose and its standard generation command. This violates the repository directive to avoid code comments unless they preserve non-obvious context from a bug or complex investigation, so this requirement must be satisfied before merging.
```suggestion
```
**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| for route in recover-failed-video-processing finalize-stale-desktop-segments cleanup-agent-api; do | ||
| code=$(docker exec cap-cron sh -c 'curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $CRON_SECRET" "$CAP_WEB_INTERNAL_URL/api/cron/'"$route"'"') |
There was a problem hiding this comment.
Scheduler path remains untested
The workflow invokes each endpoint directly with docker exec, bypassing the new scheduler’s timing, route interpolation, and locking logic. A regression that leaves cap-cron healthy but prevents scheduled requests from launching would therefore still pass CI. Exercise at least one request through the scheduler path or make that scheduling logic independently testable.
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/test-self-hosting.yml
Line: 102-103
Comment:
**Scheduler path remains untested**
The workflow invokes each endpoint directly with `docker exec`, bypassing the new scheduler’s timing, route interpolation, and locking logic. A regression that leaves `cap-cron` healthy but prevents scheduled requests from launching would therefore still pass CI. Exercise at least one request through the scheduler path or make that scheduling logic independently testable.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Problem
apps/web/vercel.jsonschedules the recovery endpoints as Vercel Cron Jobs. Self-hosted deployments have nothing that calls them, andCRON_SECRETisn't set in either compose file, so on every Docker Compose or Coolify install:Server is busy,processVideoOnMediaServer … exceeded max retries) stay inphase = errorpermanentlyChange
cap-cronservice indocker-compose.ymlanddocker-compose.coolify.yml(curlimages/curl, no build) calls the same endpoints on the same UTC schedules asvercel.json, withAuthorization: Bearer $CRON_SECRET:recover-failed-video-processing:7,22,37,52 * * * *finalize-stale-desktop-segments:*/15 * * * *cleanup-agent-api:17 3 * * */api/cron/sync-loopsis deliberately left out. It syncs contacts to Loops for Cap.so's own email and has no purpose on a self-hosted instance.minio-setupservice. It handles SIGTERM, skips a route whose previous run is still going, reaps finished background jobs, and writes a heartbeat file for its healthcheck.CRON_SECRETis set oncap-webandcap-cron:docker-compose.yml: a placeholder default, like the other secrets.SERVICE_HEX_32_CRONSECRET.CRON_SECRETadded to the production checklist.test-self-hosting.yml: waits forcap-cronto be healthy, asserts each endpoint returns 200 using the container's secret, and asserts a wrong secret gets 401.Testing
Ran
docker compose up -dfrom this branch against the publishedghcr.io/capsoftware/cap-web:latest:docker stop cap-cronexited 0 immediately.The same design has been running in production on a self-hosted instance, where the first scheduled calls returned 200.
Heads up: MinIO images (not in this PR)
minio/minio:latestandminio/mc:latestcan no longer be pulled from Docker Hub (pull access denied);quay.io/minio/minioandquay.io/minio/mcstill work. Sotest-self-hosting.ymlwill likely fail on this PR, and on any other, at the image pull, independent of this change. I tested locally with a compose override pointing MinIO at quay.io. I kept that out of this PR to keep scope tight and am happy to send a separate fix.The scheduler implementation appears functionally safe, but the explicit repository comments requirement must be satisfied before merging; scheduler-path test coverage is a non-blocking follow-up.
Findings
Fix with agent prompt
Summary
This PR adds scheduled maintenance to Docker Compose and Coolify deployments through a lightweight
cap-cronservice.Reviews (1) · Last reviewed commit: "feat: run scheduled maintenance endpoint..."