Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
services:
api:
image: honcho:latest
build:
context: .
dockerfile: Dockerfile
entrypoint: ["sh", "docker/entrypoint.sh"]
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
ports:
- 8000:8000
volumes:
- .:/app
- venv:/app/.venv
environment:
- DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
- CACHE_URL=redis://redis:6379/0?suppress=true
env_file:
- path: .env
required: false
Comment on lines +18 to +23
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "docker-compose.yml" -o -name "docker-compose.yaml"

Repository: plastic-labs/honcho

Length of output: 83


🏁 Script executed:

cat -n ./docker-compose.yml

Repository: plastic-labs/honcho

Length of output: 3720


🌐 Web query:

Docker Compose environment variable precedence env_file vs environment

💡 Result:

In Docker Compose, when the same environment variable is defined in both the environment attribute and env_file attribute in the compose file, the environment attribute takes precedence over env_file. The full order of precedence (highest to lowest) for environment variables in the container is: 1. Set using docker compose run --env (CLI). 2. Set with environment or env_file but with value interpolated from shell or .env file (via ${VAR}). 3. Set using just the environment attribute (plain values) in the Compose file. 4. Set using the env_file attribute in the Compose file. 5. Set in the container image via ENV in Dockerfile. Example from official docs: services: webapp: image: 'webapp' env_file: - ./webapp.env # VALUE=1.6 environment: - VALUE=1.7 Result: Container gets VALUE=1.7 from environment, overriding env_file. Note: The project-level .env file (auto-loaded) is for interpolation in the compose file, not directly for container env (unless referenced via env_file or interpolated). Multiple env_files are merged with later files overriding earlier ones.

Citations:


🌐 Web query:

Postgres POSTGRES_HOST_AUTH_METHOD=trust security implications docker official image

💡 Result:

In the official postgres Docker image, setting POSTGRES_HOST_AUTH_METHOD=trust tells the entrypoint (at initial database creation) to configure host (TCP/IP) connections to use the trust auth method—so POSTGRES_PASSWORD is not required. The image already enables trust for some local access inside the container, but this variable extends the idea to host connections. [1]

Security implications

  • No authentication for TCP clients: PostgreSQL’s trust method means anyone who can connect to the server can log in as any PostgreSQL user without a password (including potentially postgres). [2]
  • Risk becomes “network = security boundary”: If you publish Postgres to the host (-p 5432:5432), run on a shared network, misconfigure Kubernetes/Cloud networking, or otherwise make the port reachable, an attacker who can reach the port can likely get in immediately. (This is why “trust” is widely warned against outside isolated dev/test.) [2][3]
  • Hardening tools may block it: Docker’s “Hardened Images” guidance for Postgres explicitly disallows trust / password for POSTGRES_HOST_AUTH_METHOD “for security reasons” (showing how unsafe trust is considered for production-like usage). [4]

Practical guidance

  • Use POSTGRES_PASSWORD (or secrets) and a password-based method (preferably SCRAM) instead of trust, and don’t expose 5432 publicly—keep Postgres on a private Docker network or behind strict firewalling / tunneling. [1][2]

Sources: Docker Official Image docs for postgres [1], PostgreSQL docs on trust meaning “login as any user without a password” [2], Cloud Security Alliance note on enabling trust via POSTGRES_HOST_AUTH_METHOD [3], Docker Hardened Images guide noting restrictions for security [4].


Remove hardcoded database credentials and don't expose Postgres with trust authentication.

The api and deriver services hard-code DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres, and Docker Compose gives environment precedence over env_file, so any safer values in .env are ignored. The database service publishes port 5432 to all host interfaces with POSTGRES_HOST_AUTH_METHOD=trust, which allows anyone with network access to connect without a password—no credentials required. On a VPS or shared network, this is an exposed superuser database.

🛡️ Suggested changes
   api:
     environment:
-      - DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
+      - DB_CONNECTION_URI=postgresql+psycopg://postgres:${POSTGRES_PASSWORD}@database:5432/postgres
       - CACHE_URL=redis://redis:6379/0?suppress=true

   deriver:
     environment:
-      - DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
+      - DB_CONNECTION_URI=postgresql+psycopg://postgres:${POSTGRES_PASSWORD}@database:5432/postgres
       - CACHE_URL=redis://redis:6379/0?suppress=true

   database:
-    ports:
-      - 5432:5432
     environment:
       - POSTGRES_DB=postgres
       - POSTGRES_USER=postgres
-      - POSTGRES_PASSWORD=postgres
-      - POSTGRES_HOST_AUTH_METHOD=trust
+      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 18 - 23, The docker-compose currently
hardcodes DB_CONNECTION_URI in the api and deriver services and sets the
database to use trust auth and publishes 5432; remove the hardcoded connection
string from the api and deriver service environment blocks so they do not
override .env (use env_file or variable interpolation like
DB_CONNECTION_URI=${DB_CONNECTION_URI}), remove POSTGRES_HOST_AUTH_METHOD=trust
from the database service, stop publishing port 5432 publicly (either remove
ports or bind to 127.0.0.1:5432), and set explicit secure vars for the DB in
env_file (e.g., POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB) so credentials
come from .env and not from the compose file. Ensure references:
DB_CONNECTION_URI, env_file, database service, api service, deriver service, and
POSTGRES_HOST_AUTH_METHOD are updated accordingly.


deriver:
build:
context: .
dockerfile: Dockerfile
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
Comment on lines +29 to +34
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and read the docker-compose.yml file
find . -name "docker-compose.yml" -type f | head -5

Repository: plastic-labs/honcho

Length of output: 83


🏁 Script executed:

# Search for entrypoint.sh and provision_db.py
find . -name "entrypoint.sh" -o -name "provision_db.py" | head -20

Repository: plastic-labs/honcho

Length of output: 111


🏁 Script executed:

cat -n ./docker-compose.yml

Repository: plastic-labs/honcho

Length of output: 3720


🏁 Script executed:

cat -n ./docker/entrypoint.sh

Repository: plastic-labs/honcho

Length of output: 310


🏁 Script executed:

head -50 ./scripts/provision_db.py

Repository: plastic-labs/honcho

Length of output: 545


🏁 Script executed:

# Check if provision_db.py is essential or if init.sql covers the schema
wc -l ./scripts/provision_db.py

Repository: plastic-labs/honcho

Length of output: 91


🏁 Script executed:

# Double-check if api has any healthcheck defined further down
grep -A 20 "api:" ./docker-compose.yml | grep -i healthcheck

Repository: plastic-labs/honcho

Length of output: 45


🏁 Script executed:

# Verify what init_db() does to confirm it's schema initialization
grep -A 30 "async def init_db" ./src/db.py 2>/dev/null | head -40

Repository: plastic-labs/honcho

Length of output: 674


Wait for API startup before starting the worker.

docker/entrypoint.sh runs scripts/provision_db.py, which executes Alembic migrations and schema setup (create schema, install pgvector, run migrations). The deriver service bypasses this entirely and starts src.deriver directly. Since docker-compose only waits for services listed under depends_on, the worker can start as soon as Postgres and Redis are healthy, even before the API finishes schema initialization.

The suggested fix references api: condition: service_healthy, but the api service has no healthcheck defined, making that impossible to implement. Use condition: service_started instead to wait for the API to start, which triggers the migration sequence:

   deriver:
     depends_on:
+      api:
+        condition: service_started
       database:
         condition: service_healthy
       redis:
         condition: service_healthy
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
depends_on:
api:
condition: service_started
database:
condition: service_healthy
redis:
condition: service_healthy
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 29 - 34, The deriver service starts
src.deriver directly and bypasses docker/entrypoint.sh (which runs
scripts/provision_db.py to apply migrations), so change the deriver service's
depends_on to also wait for the API to start: add the api service under
depends_on for the deriver service and use condition: service_started (since api
has no healthcheck) so the API’s entrypoint can run migrations before
src.deriver launches; reference the deriver service, entrypoint, src.deriver,
docker/entrypoint.sh, and scripts/provision_db.py when making the change.

volumes:
- .:/app
- venv:/app/.venv
environment:
- DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
- CACHE_URL=redis://redis:6379/0?suppress=true
- METRICS_ENABLED=false
env_file:
- path: .env
required: false
healthcheck:
test: ["CMD-SHELL", "kill -0 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s

database:
image: pgvector/pgvector:pg15
restart: always
ports:
- 5432:5432
command: ["postgres", "-c", "max_connections=800"]
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_HOST_AUTH_METHOD=trust
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
- pgdata:/var/lib/postgresql/data/
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
interval: 5s
timeout: 5s
retries: 5

redis:
image: redis:8.2
restart: always
ports:
- 6379:6379
volumes:
- ./redis-data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli ping"]
interval: 5s
timeout: 5s
retries: 5

prometheus:
image: prom/prometheus:v3.2.1
ports:
- 9090:9090
volumes:
- ./docker/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
depends_on:
api:
condition: service_started

grafana:
image: grafana/grafana:11.4.0
ports:
- 3000:3000
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
Comment on lines +99 to +105
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the docker-compose.yml file
fd -t f "docker-compose.yml"

Repository: plastic-labs/honcho

Length of output: 89


🏁 Script executed:

# Read the docker-compose.yml.example file to see lines 99-105 in context
cat -n docker-compose.yml.example | sed -n '90,115p'

Repository: plastic-labs/honcho

Length of output: 607


🏁 Script executed:

# Read more context around the Grafana service to see if ports are defined
cat -n docker-compose.yml.example | sed -n '75,100p'

Repository: plastic-labs/honcho

Length of output: 983


🏁 Script executed:

# Get more context - check if there's a .env file or how credentials are typically managed
fd -t f "\.env" | head -5
cat -n docker-compose.yml.example | head -10

Repository: plastic-labs/honcho

Length of output: 345


🏁 Script executed:

# Check if there's a .env.example or documentation about environment variables
fd -t f "\.env" 
# Also check the README or any documentation
fd -t f "README.md"

Repository: plastic-labs/honcho

Length of output: 353


🏁 Script executed:

# Read the main README to understand the project context
head -100 README.md

Repository: plastic-labs/honcho

Length of output: 3614


🏁 Script executed:

# Check if there's any documentation in the docker directory or compose file about setup
cat -n docker-compose.yml.example | head -20
# Also check if there's a .dockerignore or setup guide
fd -t f "setup\|SETUP\|CONTRIBUTING" | head -5

Repository: plastic-labs/honcho

Length of output: 702


🏁 Script executed:

# Check the context - is this file meant to be used as-is or is there documentation about customization?
# Look for any comments or instructions in docker-compose.yml.example
head -50 docker-compose.yml.example

Repository: plastic-labs/honcho

Length of output: 1328


🏁 Script executed:

# Check if there's any .gitignore or documentation about how to use this file
cat .gitignore | grep -A 5 -B 5 "docker-compose"
# Also check if there are instructions in the docker directory
ls -la docker/

Repository: plastic-labs/honcho

Length of output: 513


🏁 Script executed:

# Check the structure - this is docker-compose.yml.example (a template)
# The actual docker-compose.yml is in .gitignore (local file)
# Let's verify: does the review correctly reference the file, and what is the context for this being an example file?

# First, let's check if there's any README or documentation about the docker setup
find . -path ./node_modules -prune -o -name "*docker*" -o -name "*compose*" | grep -i readme

Repository: plastic-labs/honcho

Length of output: 45


🏁 Script executed:

# Let's verify Grafana documentation about these settings
# And check if the concern is valid for an example/template file

# First, check if there are any comments or instructions at the top of the file
head -5 docker-compose.yml.example

# Check if there's any CONTRIBUTING or setup documentation
find . -type f \( -name "CONTRIBUTING*" -o -name "DEVELOPMENT*" -o -name "*.md" \) | xargs grep -l "docker-compose" | head -3

Repository: plastic-labs/honcho

Length of output: 183


🏁 Script executed:

# Check if README or CHANGELOG mentions docker-compose setup
grep -A 5 -B 5 "docker-compose\|docker\|grafana" README.md | head -50

Repository: plastic-labs/honcho

Length of output: 1659


🏁 Script executed:

# Let me search for any documentation or comments about the security settings
# Check if Grafana service in docker-compose has env_file setup
grep -A 15 "grafana:" docker-compose.yml.example

Repository: plastic-labs/honcho

Length of output: 549


🏁 Script executed:

# Check if there's a .env.template file that shows what variables should be set
fd -t f "\.env\."
# Also check if other services in the compose file use env_file
grep -B 5 -A 10 "env_file:" docker-compose.yml.example

Repository: plastic-labs/honcho

Length of output: 981


🏁 Script executed:

# Check Grafana version and verify it supports environment variable substitution
grep "grafana:" -A 2 docker-compose.yml.example
# Verify the pattern used in the file - does it support ${VAR} syntax?
grep "\${" docker-compose.yml.example | head -5

Repository: plastic-labs/honcho

Length of output: 136


🏁 Script executed:

# Check if the suggested ${VAR} syntax is used elsewhere or if env_file is the pattern
grep -E "\$\{[A-Z_]+\}" docker-compose.yml.example | head -10
# Also verify the Grafana version supports these environment variables
# Let's look at what the actual pattern is in this project for env vars
head -100 docker-compose.yml.example | grep -E "environment:|env_file:|=\$"

Repository: plastic-labs/honcho

Length of output: 158


Add env_file entry and remove hardcoded Grafana admin credentials.

Grafana is configured with default credentials (admin/admin) and anonymous access enabled on port 3000. While this is an example template, it sets a poor security baseline that users may not customize. Other services in the compose file use env_file to manage secrets—Grafana should follow the same pattern:

🔐 Suggested change
  grafana:
    image: grafana/grafana:11.4.0
    ports:
      - 3000:3000
+   env_file:
+     - path: .env
+       required: false
    environment:
-     - GF_SECURITY_ADMIN_USER=admin
-     - GF_SECURITY_ADMIN_PASSWORD=admin
-     - GF_AUTH_ANONYMOUS_ENABLED=true
+     - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER}
+     - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
+     - GF_AUTH_ANONYMOUS_ENABLED=false
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 99 - 105, Replace the hardcoded Grafana
environment variables (GF_SECURITY_ADMIN_USER, GF_SECURITY_ADMIN_PASSWORD,
GF_AUTH_ANONYMOUS_ENABLED, GF_AUTH_ANONYMOUS_ORG_ROLE) with an env_file
reference so secrets/config are loaded from a separate .env or secrets file;
update the docker-compose service block that exposes port 3000 to include an
env_file: entry and remove the inline GF_SECURITY_ADMIN_USER and
GF_SECURITY_ADMIN_PASSWORD lines (also consider removing or setting
GF_AUTH_ANONYMOUS_ENABLED to false in the env file) so credentials are not
committed in the compose file.

volumes:
- ./grafana-data:/var/lib/grafana
- ./docker/grafana-datasource.yml:/etc/grafana/provisioning/datasources/datasource.yml:ro
depends_on:
prometheus:
condition: service_started

volumes:
pgdata:
venv:
prometheus-data: