Skip to content
Merged
Show file tree
Hide file tree
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
16 changes: 11 additions & 5 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ docs/
# Documentation build output (Zensical)
_site/

# Astro / Node.js (landing page)
site/node_modules/
site/dist/
site/.astro/
# Astro landing page (not needed by any Docker build)
site/

# Virtual environments
.venv/
Expand All @@ -39,6 +37,9 @@ __pycache__/
.ruff_cache/
.pytest_cache/

# Hypothesis test database
.hypothesis/

# Coverage
htmlcov/
coverage.xml
Expand All @@ -57,6 +58,9 @@ coverage.xml
.idea/
.vscode/

# CI/build utility scripts (not needed in Docker builds)
scripts/

# CLI (Go binary, not needed in Docker builds)
cli/

Expand All @@ -71,10 +75,12 @@ logs/
Thumbs.db
.DS_Store

# Web dashboard build artifacts
# Web dashboard build artifacts and test infrastructure
web/node_modules/
web/dist/
web/.env
web/__tests__/
web/vitest.config.*

# uv
.python-version
135 changes: 111 additions & 24 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,24 +110,59 @@ jobs:
id: scan-ref
run: echo "ref=ghcr.io/aureliolo/synthorg-backend:sha-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"

- name: Trivy scan (critical — hard fail)
# Single Trivy run for CRITICAL + HIGH (saves ~30s vs two separate runs).
# JSON output lets us fail on CRITICAL only while still reporting HIGH.
- name: Trivy scan
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
image-ref: ${{ steps.scan-ref.outputs.ref }}
format: table
exit-code: "1"
severity: CRITICAL
format: json
output: trivy-backend.json
exit-code: "0"
severity: CRITICAL,HIGH
trivyignores: .github/.trivyignore.yaml
Comment thread
greptile-apps[bot] marked this conversation as resolved.

- name: Trivy scan (high — warn only)
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
continue-on-error: true
- name: Evaluate Trivy results
run: |
if [ ! -f trivy-backend.json ]; then
echo "::error::trivy-backend.json not found — Trivy scan may have failed to produce output"
exit 1
fi
echo "## Trivy Scan — Backend"
Comment thread
greptile-apps[bot] marked this conversation as resolved.
TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-backend.json)
if [ "$TOTAL" -eq 0 ]; then
Comment on lines +132 to +133
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 | 🟡 Minor

Add validation for jq output before arithmetic comparison.

If jq fails (e.g., malformed JSON from Trivy), TOTAL will be empty or contain an error message, causing [ "$TOTAL" -eq 0 ] to fail with "integer expression expected". The same applies to CRITICAL and HIGH variables on lines 147-148.

🛡️ Proposed fix to add validation
-          TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-backend.json)
+          TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-backend.json) || {
+            echo "::error::Failed to parse trivy-backend.json"
+            exit 1
+          }
+          if ! [[ "$TOTAL" =~ ^[0-9]+$ ]]; then
+            echo "::error::Invalid vulnerability count: $TOTAL"
+            exit 1
+          fi

Apply the same pattern to CRITICAL and HIGH variable assignments on lines 147-148.

📝 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
TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-backend.json)
if [ "$TOTAL" -eq 0 ]; then
TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-backend.json) || {
echo "::error::Failed to parse trivy-backend.json"
exit 1
}
if ! [[ "$TOTAL" =~ ^[0-9]+$ ]]; then
echo "::error::Invalid vulnerability count: $TOTAL"
exit 1
fi
if [ "$TOTAL" -eq 0 ]; then
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker.yml around lines 132 - 133, TOTAL, CRITICAL and
HIGH may be non-numeric if jq fails; update the shell logic that assigns these
variables (the lines using TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] |
length' trivy-backend.json) and the analogous CRITICAL/HIGH assignments) to
validate jq output and coerce a safe numeric fallback (e.g., check the variable
with a numeric regex or test before using -eq and set to 0 on failure); ensure
subsequent comparisons use the validated numeric value so [ "$TOTAL" -eq 0 ]
cannot error if jq returns empty or an error string.

echo "No CRITICAL or HIGH vulnerabilities found."
echo "## Trivy Scan — Backend: No CRITICAL or HIGH vulnerabilities found." >> "$GITHUB_STEP_SUMMARY"
else
TABLE=$(jq -r '
["SEVERITY","CVE","PACKAGE","VERSION","TITLE"],
(.Results[]?.Vulnerabilities[]? |
[.Severity, .VulnerabilityID, .PkgName, .InstalledVersion, (.Title // "")[0:60]]) |
@tsv
' trivy-backend.json | column -t -s$'\t')
echo "$TABLE"
printf '## Trivy Scan — Backend\n```\n%s\n```\n' "$TABLE" >> "$GITHUB_STEP_SUMMARY"
fi

CRITICAL=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' trivy-backend.json)
HIGH=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' trivy-backend.json)
echo "Critical: $CRITICAL, High: $HIGH"
echo "**Critical: $CRITICAL, High: $HIGH**" >> "$GITHUB_STEP_SUMMARY"
if [ "$HIGH" -gt 0 ]; then
echo "::warning::Found $HIGH HIGH severity vulnerabilities"
fi
if [ "$CRITICAL" -gt 0 ]; then
echo "::error::Found $CRITICAL CRITICAL vulnerabilities — failing build"
exit 1
fi
Comment thread
greptile-apps[bot] marked this conversation as resolved.

- name: Upload Trivy report (backend)
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
image-ref: ${{ steps.scan-ref.outputs.ref }}
format: table
exit-code: "0"
severity: HIGH
trivyignores: .github/.trivyignore.yaml
name: trivy-backend-report
path: trivy-backend.json
retention-days: 30

- name: Grype scan
uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2
Expand All @@ -137,6 +172,16 @@ jobs:
severity-cutoff: critical
config: .github/.grype.yaml
Comment on lines 167 to 173
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Grype scan skipped when Trivy finds CRITICAL vulnerabilities

In the old design, the Trivy scan (high — warn only) step had continue-on-error: true, which kept the job in a "success" state so that Grype always ran independently. In the new design, Evaluate Trivy results calls exit 1 when CRITICAL vulnerabilities are found. Because Grype scan has no if condition it defaults to if: success() — so it is skipped entirely whenever Trivy flags a CRITICAL CVE.

This silently breaks the "two independent scanners" guarantee described in both CLAUDE.md and docs/security.md. If the Grype database contains a CRITICAL CVE that Trivy misses, that finding is only surfaced when Trivy's own scan is clean, which defeats the purpose of running two independent tools.

Adding if: always() to the Grype step (and, optionally, the CIS benchmark step) in both jobs restores independent coverage:

Suggested change
- name: Grype scan
if: always()
uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2
with:
image: ${{ steps.scan-ref.outputs.ref }}
fail-build: true
severity-cutoff: critical
config: .github/.grype.yaml

The same applies to the Grype scan step in the build-web job (around line 337).

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/docker.yml
Line: 167-173

Comment:
**Grype scan skipped when Trivy finds CRITICAL vulnerabilities**

In the old design, the `Trivy scan (high — warn only)` step had `continue-on-error: true`, which kept the job in a "success" state so that Grype always ran independently. In the new design, `Evaluate Trivy results` calls `exit 1` when CRITICAL vulnerabilities are found. Because `Grype scan` has no `if` condition it defaults to `if: success()` — so it is skipped entirely whenever Trivy flags a CRITICAL CVE.

This silently breaks the "two independent scanners" guarantee described in both `CLAUDE.md` and `docs/security.md`. If the Grype database contains a CRITICAL CVE that Trivy misses, that finding is only surfaced when Trivy's own scan is clean, which defeats the purpose of running two independent tools.

Adding `if: always()` to the Grype step (and, optionally, the CIS benchmark step) in both jobs restores independent coverage:

```suggestion
      - name: Grype scan
        if: always()
        uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2
        with:
          image: ${{ steps.scan-ref.outputs.ref }}
          fail-build: true
          severity-cutoff: critical
          config: .github/.grype.yaml
```

The same applies to the `Grype scan` step in the `build-web` job (around line 337).

How can I resolve this? If you propose a fix, please make it concise.


# CIS Docker Benchmark v1.6.0 compliance check (uses trivy installed above).
# Informational for now — remove continue-on-error once baseline is clean.
- name: CIS Docker Benchmark (backend)
continue-on-error: true
env:
IMAGE_REF: ${{ steps.scan-ref.outputs.ref }}
run: |
command -v trivy >/dev/null 2>&1 || { echo "::warning::trivy not on PATH, skipping CIS scan"; exit 0; }
trivy image --compliance docker-cis-1.6.0 --format table "$IMAGE_REF"
Comment on lines +177 to +183
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CIS benchmark steps may silently skip on certain runner configurations

The step guards with command -v trivy and emits a ::warning:: if trivy isn't on PATH. Whether trivy is available depends on the aquasecurity/trivy-action implementation detail — recent versions install the binary directly on the runner host, but this isn't guaranteed across all runner environments or action versions.

Since the step is already continue-on-error: true (informational), the silent skip is low-risk. However, the same pattern also appears at the web job (line ~318). If you want certainty that the CIS scan always runs, consider using a second aquasecurity/trivy-action invocation with scan-type: image and appropriate flags instead of relying on the host trivy binary:

- name: CIS Docker Benchmark (backend)
  continue-on-error: true
  uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
  with:
    image-ref: ${{ steps.scan-ref.outputs.ref }}
    scan-type: image
    format: table
    exit-code: "0"
    compliance: docker-cis-1.6.0
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/docker.yml
Line: 161-167

Comment:
**CIS benchmark steps may silently skip on certain runner configurations**

The step guards with `command -v trivy` and emits a `::warning::` if trivy isn't on `PATH`. Whether trivy is available depends on the `aquasecurity/trivy-action` implementation detail — recent versions install the binary directly on the runner host, but this isn't guaranteed across all runner environments or action versions.

Since the step is already `continue-on-error: true` (informational), the silent skip is low-risk. However, the same pattern also appears at the web job (line ~318). If you want certainty that the CIS scan always runs, consider using a second `aquasecurity/trivy-action` invocation with `scan-type: image` and appropriate flags instead of relying on the host `trivy` binary:

```yaml
- name: CIS Docker Benchmark (backend)
  continue-on-error: true
  uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
  with:
    image-ref: ${{ steps.scan-ref.outputs.ref }}
    scan-type: image
    format: table
    exit-code: "0"
    compliance: docker-cis-1.6.0
```

How can I resolve this? If you propose a fix, please make it concise.


# Push only after scans pass — prevents publishing vulnerable images.
# NOTE: This is a separate build invocation from the scan step. GHA cache
# ensures deterministic layer content, but the manifest digest differs due
Expand Down Expand Up @@ -236,24 +281,58 @@ jobs:
id: scan-ref
run: echo "ref=ghcr.io/aureliolo/synthorg-web:sha-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"

- name: Trivy scan (critical — hard fail)
# Single Trivy run for CRITICAL + HIGH (saves ~12s vs two separate runs).
- name: Trivy scan
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
image-ref: ${{ steps.scan-ref.outputs.ref }}
format: table
exit-code: "1"
severity: CRITICAL
format: json
output: trivy-web.json
exit-code: "0"
severity: CRITICAL,HIGH
trivyignores: .github/.trivyignore.yaml

- name: Trivy scan (high — warn only)
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
continue-on-error: true
- name: Evaluate Trivy results
run: |
if [ ! -f trivy-web.json ]; then
echo "::error::trivy-web.json not found — Trivy scan may have failed to produce output"
exit 1
fi
echo "## Trivy Scan — Web"
TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-web.json)
if [ "$TOTAL" -eq 0 ]; then
Comment on lines +302 to +303
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 | 🟡 Minor

Same jq validation issue as backend section.

Apply the same numeric validation fix as recommended for the backend section (lines 132-133). The CRITICAL and HIGH variables on lines 317-318 also need validation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/docker.yml around lines 302 - 303, The jq
numeric-validation is missing for the scan totals and severity counts: ensure
TOTAL, CRITICAL and HIGH are produced as numbers with a safe default (0) by
updating their jq invocations to coerce or fallback to 0 (e.g. use | tonumber //
0 or ... | length // 0) so the bash comparisons won't fail on null/empty; update
the assignments that set TOTAL, CRITICAL and HIGH (the variables named TOTAL,
CRITICAL, HIGH in the script) to use this pattern.

echo "No CRITICAL or HIGH vulnerabilities found."
echo "## Trivy Scan — Web: No CRITICAL or HIGH vulnerabilities found." >> "$GITHUB_STEP_SUMMARY"
else
TABLE=$(jq -r '
["SEVERITY","CVE","PACKAGE","VERSION","TITLE"],
(.Results[]?.Vulnerabilities[]? |
[.Severity, .VulnerabilityID, .PkgName, .InstalledVersion, (.Title // "")[0:60]]) |
@tsv
' trivy-web.json | column -t -s$'\t')
echo "$TABLE"
printf '## Trivy Scan — Web\n```\n%s\n```\n' "$TABLE" >> "$GITHUB_STEP_SUMMARY"
fi

CRITICAL=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' trivy-web.json)
HIGH=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' trivy-web.json)
echo "Critical: $CRITICAL, High: $HIGH"
echo "**Critical: $CRITICAL, High: $HIGH**" >> "$GITHUB_STEP_SUMMARY"
if [ "$HIGH" -gt 0 ]; then
echo "::warning::Found $HIGH HIGH severity vulnerabilities"
fi
if [ "$CRITICAL" -gt 0 ]; then
echo "::error::Found $CRITICAL CRITICAL vulnerabilities — failing build"
exit 1
fi

- name: Upload Trivy report (web)
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
image-ref: ${{ steps.scan-ref.outputs.ref }}
format: table
exit-code: "0"
severity: HIGH
trivyignores: .github/.trivyignore.yaml
name: trivy-web-report
path: trivy-web.json
retention-days: 30

- name: Grype scan
uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2
Expand All @@ -263,6 +342,14 @@ jobs:
severity-cutoff: critical
config: .github/.grype.yaml

- name: CIS Docker Benchmark (web)
continue-on-error: true
env:
IMAGE_REF: ${{ steps.scan-ref.outputs.ref }}
run: |
command -v trivy >/dev/null 2>&1 || { echo "::warning::trivy not on PATH, skipping CIS scan"; exit 0; }
trivy image --compliance docker-cis-1.6.0 --format table "$IMAGE_REF"

# Push only after scans pass — prevents publishing vulnerable images.
# NOTE: Separate build invocation; GHA cache ensures deterministic layers,
# but manifest digest differs due to SBOM + provenance attestation layers.
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ site/ # Astro landing page (synthorg.io)
- Build job runs regardless (catches build failures); deploy job skips on fork PRs (same-repo check via job output)
- Cleanup job deletes preview comment and Cloudflare deployments on PR close (pull_request events only)
- Concurrency group cancels stale builds on rapid pushes
- **Docker**: `.github/workflows/docker.yml` — builds backend + web images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via `actions/attest-build-provenance` (SHA-pinned, Sigstore-signed, pushed to registry). Scans: Trivy (CRITICAL = hard fail, HIGH = warn-only) + Grype (critical cutoff). CVE triage via `.github/.trivyignore.yaml` and `.github/.grype.yaml`. Images only pushed after scans pass. Triggers on push to main and version tags (`v*`).
- **Docker**: `.github/workflows/docker.yml` — builds backend + web images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via `actions/attest-build-provenance` (SHA-pinned, Sigstore-signed, pushed to registry). Scans: Trivy (CRITICAL = hard fail, HIGH = warn-only) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via `.github/.trivyignore.yaml` and `.github/.grype.yaml`. Images only pushed after scans pass. Triggers on push to main and version tags (`v*`).
- **Matrix**: Python 3.14
- **CLI**: `.github/workflows/cli.yml` — Go lint (`golangci-lint` + `go vet`) + test (`-race -coverprofile`) + build (cross-compile matrix: linux/darwin/windows × amd64/arm64) + vulnerability check (`govulncheck`) + fuzz testing (main-only, 30s/target, `continue-on-error`, matrix over 4 packages) on `cli/**` changes. `cli-pass` gate includes fuzz result as informational warning. GoReleaser release on `v*` tags (attaches assets to the draft Release Please release). SLSA L3 provenance attestations via `actions/attest-build-provenance` (SHA-pinned, Sigstore-signed). Post-release step appends install instructions + checksum table + provenance verification instructions to the draft release notes (while still in draft — before finalize-release publishes).
- **Dependabot**: daily uv + github-actions + npm + pre-commit + docker + gomod updates, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging
Expand Down
47 changes: 36 additions & 11 deletions cli/internal/compose/compose.yml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,35 @@ services:
SYNTHORG_JWT_SECRET: {{yamlStr .JWTSecret}}
{{- end}}
user: "65532:65532"
# CIS Docker Benchmark v1.6.0 hardening (5.3, 5.12, 5.25)
# CIS Docker Benchmark v1.6.0 hardening (5.3, 5.12, 5.25, 5.28)
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=64m
- /tmp:noexec,nosuid,nodev,size=64m
pids_limit: 256
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
memory: 4G
cpus: "2.0"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# Healthcheck assumes internal port is always 8000 (container-side port
# mapping is fixed). Dockerfile healthcheck reads UVICORN_PORT dynamically,
# but compose-level exec form cannot expand env vars without a shell.
healthcheck:
test: ["/usr/bin/python", "-c", "import json,urllib.request,sys; r=urllib.request.urlopen('http://localhost:8000/api/v1/health'); d=json.loads(r.read()); sys.exit(0 if d.get('data',{}).get('status')=='ok' else 1)"]
test: ["CMD", "/usr/bin/python", "-c", "import json,urllib.request,sys; r=urllib.request.urlopen('http://localhost:8000/api/v1/health'); d=json.loads(r.read()); sys.exit(0 if d.get('data',{}).get('status')=='ok' else 1)"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
start_period: 30s

web:
image: ghcr.io/aureliolo/synthorg-web:{{.ImageTag}}
Expand All @@ -53,15 +62,21 @@ services:
- ALL
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=16m
- /var/cache/nginx:size=32m
- /var/run:size=1m
- /tmp:noexec,nosuid,nodev,size=16m
- /var/cache/nginx:noexec,nosuid,nodev,size=32m
- /var/run:noexec,nosuid,nodev,size=1m
pids_limit: 64
restart: unless-stopped
deploy:
resources:
limits:
memory: 128M
memory: 256M
cpus: "0.5"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
{{- if .Sandbox}}

sandbox:
Expand All @@ -78,14 +93,24 @@ services:
- ALL
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=128m
- /tmp:noexec,nosuid,nodev,size=128m
pids_limit: 128
restart: unless-stopped
deploy:
resources:
limits:
memory: 256M
cpus: "0.5"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
{{- end}}

networks:
default:
name: synthorg-net

volumes:
synthorg-data:
39 changes: 29 additions & 10 deletions cli/testdata/compose_custom_ports.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,35 @@ services:
SYNTHORG_LOG_LEVEL: "debug"
SYNTHORG_JWT_SECRET: "test-secret-value"
user: "65532:65532"
# CIS Docker Benchmark v1.6.0 hardening (5.3, 5.12, 5.25)
# CIS Docker Benchmark v1.6.0 hardening (5.3, 5.12, 5.25, 5.28)
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=64m
- /tmp:noexec,nosuid,nodev,size=64m
pids_limit: 256
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
memory: 4G
cpus: "2.0"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# Healthcheck assumes internal port is always 8000 (container-side port
# mapping is fixed). Dockerfile healthcheck reads UVICORN_PORT dynamically,
# but compose-level exec form cannot expand env vars without a shell.
healthcheck:
test: ["/usr/bin/python", "-c", "import json,urllib.request,sys; r=urllib.request.urlopen('http://localhost:8000/api/v1/health'); d=json.loads(r.read()); sys.exit(0 if d.get('data',{}).get('status')=='ok' else 1)"]
test: ["CMD", "/usr/bin/python", "-c", "import json,urllib.request,sys; r=urllib.request.urlopen('http://localhost:8000/api/v1/health'); d=json.loads(r.read()); sys.exit(0 if d.get('data',{}).get('status')=='ok' else 1)"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
start_period: 30s

web:
image: ghcr.io/aureliolo/synthorg-web:v0.2.0
Expand All @@ -51,15 +60,25 @@ services:
- ALL
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=16m
- /var/cache/nginx:size=32m
- /var/run:size=1m
- /tmp:noexec,nosuid,nodev,size=16m
- /var/cache/nginx:noexec,nosuid,nodev,size=32m
- /var/run:noexec,nosuid,nodev,size=1m
pids_limit: 64
restart: unless-stopped
deploy:
resources:
limits:
memory: 128M
memory: 256M
cpus: "0.5"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

networks:
default:
name: synthorg-net

volumes:
synthorg-data:
Loading
Loading