feat: Docker/Podman containerized setup with GHCR publishing (closes #1) - #2743
feat: Docker/Podman containerized setup with GHCR publishing (closes #1)#2743slmingol wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. 7 change(s) tested, no difference found (not proven).
Graphify review — findings
This PR adds a new GitHub Actions workflow (.github/workflows/docker.yml) that builds multi-arch (amd64/arm64) Docker images on native runners and pushes them to GHCR as a merged manifest. It also rewrites the ARCHITECTURE.md pipeline/module documentation to describe a simplified, single-function-per-stage API (e.g. build_graph, analyze, render_report, export, start_server) and trims older content. The changed-symbols list further suggests accompanying edits across many test files, extractors, skill/tooling fragments, and the changelog. Note: the diff was truncated, so the full surface area (especially the many listed test and extractor symbols) isn't visible here.
No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 6784 functions depend on the 5529 functions this change touches.
Health — this change adds coupling hotspots:
- worse:
_find_node()— 13 callers, 3 callees - worse:
_emit_rescued_import()— 3 callers, 4 callees
Verification — 6784 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 6760 function(s) in the blast radius were not formally verified this run
Formal verification
No difference found (not proven): No behavior difference found in resolve\_seed (not a proof).
The verifier ran both versions of resolve\_seed on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_query\_subgraph\_tokens (not a proof).
The verifier ran both versions of \_query\_subgraph\_tokens on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify \_abs\_identity.
The verifier did not have enough to check \_abs\_identity, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous
Could not verify: Could not verify build.
The verifier did not have enough to check build, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify build\_from\_json.
The verifier did not have enough to check build\_from\_json, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify build\_merge.
The verifier did not have enough to check build\_merge, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `graph_path` is annotated `str | Path | None` — outside the synthesizable primitive/collection set
Could not verify: Could not verify graph\_has\_legacy\_ids.
The verifier did not have enough to check graph\_has\_legacy\_ids, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `str | Path | None` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_infer\_merge\_root.
The verifier did not have enough to check \_infer\_merge\_root, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_load\_existing\_graph.
The verifier did not have enough to check \_load\_existing\_graph, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify merge\_raw\_extraction.
The verifier did not have enough to check merge\_raw\_extraction, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `graph_path` is annotated `str | Path` — outside the synthesizable primitive/collection set
No difference found (not proven): No behavior difference found in \_norm\_source\_file (not a proof).
The verifier ran both versions of \_norm\_source\_file on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_normalize\_hyperedge\_members (not a proof).
The verifier ran both versions of \_normalize\_hyperedge\_members on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify prefix\_graph\_for\_global.
The verifier did not have enough to check prefix\_graph\_for\_global, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 115 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous
No difference found (not proven): No behavior difference found in \_semantic\_id\_remap (not a proof).
The verifier ran both versions of \_semantic\_id\_remap on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify cached\_word\_count.
The verifier did not have enough to check cached\_word\_count, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify check\_semantic\_cache.
The verifier did not have enough to check check\_semantic\_cache, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify file\_hash.
The verifier did not have enough to check file\_hash, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify load\_cached.
The verifier did not have enough to check load\_cached, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_relativize\_source\_files\_in.
The verifier did not have enough to check \_relativize\_source\_files\_in, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify save\_cached.
The verifier did not have enough to check save\_cached, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
No difference found (not proven): No behavior difference found in generate\_call\_table\_rows (not a proof).
The verifier ran both versions of generate\_call\_table\_rows on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_node\_link\_payload (not a proof).
The verifier ran both versions of \_node\_link\_payload on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify write\_callflow\_html.
The verifier did not have enough to check write\_callflow\_html, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `project` is annotated `str | Path | None` — outside the synthesizable primitive/collection set
Could not verify: Could not verify dispatch\_command.
The verifier did not have enough to check dispatch\_command, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)
Could not verify: Could not verify \_stamped\_manifest\_files.
The verifier did not have enough to check \_stamped\_manifest\_files, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
· 2 more finding(s) on lines outside this diff (see the check run).
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. 7 change(s) tested, no difference found (not proven).
Graphify review — findings
This PR appears to focus on Docker packaging, documentation, and a range of extractor/pipeline behavior changes across the graphify library. - Adds a new GitHub Actions workflow (.github/workflows/docker.yml) that builds native linux/amd64 and linux/arm64 images, pushes per-platform digests to GHCR, and merges them into a single multi-arch manifest with tagging logic. - Rewrites ARCHITECTURE.md to describe a simplified single-function-per-stage pipeline and updated module signatures, and touches CHANGELOG.md (including version/changelog entries). - Changes span many library modules (watch, install, cache, extract, callflow HTML, security, dedup, resolution, skill definitions) plus their corresponding test files, indicating adjustments to extraction/resolution behavior, skill generation, and related test expectations. Surface area is broad, covering CI, docs, core extractor logic, and tests; a reviewer should verify the doc/module-signature claims against the actual code since the rewritten architecture doc diverges from some previously documented signatures.
No blocking issues surfaced. 1 lower-confidence candidate did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 6784 functions depend on the 5529 functions this change touches.
Health — this change adds coupling hotspots:
- worse:
_find_node()— 13 callers, 3 callees - worse:
_emit_rescued_import()— 3 callers, 4 callees
Verification — 6784 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 6760 function(s) in the blast radius were not formally verified this run
Formal verification
No difference found (not proven): No behavior difference found in resolve\_seed (not a proof).
The verifier ran both versions of resolve\_seed on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_query\_subgraph\_tokens (not a proof).
The verifier ran both versions of \_query\_subgraph\_tokens on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify \_abs\_identity.
The verifier did not have enough to check \_abs\_identity, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous
Could not verify: Could not verify build.
The verifier did not have enough to check build, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify build\_from\_json.
The verifier did not have enough to check build\_from\_json, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify build\_merge.
The verifier did not have enough to check build\_merge, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `graph_path` is annotated `str | Path | None` — outside the synthesizable primitive/collection set
Could not verify: Could not verify graph\_has\_legacy\_ids.
The verifier did not have enough to check graph\_has\_legacy\_ids, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `str | Path | None` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_infer\_merge\_root.
The verifier did not have enough to check \_infer\_merge\_root, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_load\_existing\_graph.
The verifier did not have enough to check \_load\_existing\_graph, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `graph_path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify merge\_raw\_extraction.
The verifier did not have enough to check merge\_raw\_extraction, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `graph_path` is annotated `str | Path` — outside the synthesizable primitive/collection set
No difference found (not proven): No behavior difference found in \_norm\_source\_file (not a proof).
The verifier ran both versions of \_norm\_source\_file on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_normalize\_hyperedge\_members (not a proof).
The verifier ran both versions of \_normalize\_hyperedge\_members on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify prefix\_graph\_for\_global.
The verifier did not have enough to check prefix\_graph\_for\_global, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 115 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous
No difference found (not proven): No behavior difference found in \_semantic\_id\_remap (not a proof).
The verifier ran both versions of \_semantic\_id\_remap on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify cached\_word\_count.
The verifier did not have enough to check cached\_word\_count, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify check\_semantic\_cache.
The verifier did not have enough to check check\_semantic\_cache, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify file\_hash.
The verifier did not have enough to check file\_hash, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify load\_cached.
The verifier did not have enough to check load\_cached, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_relativize\_source\_files\_in.
The verifier did not have enough to check \_relativize\_source\_files\_in, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify save\_cached.
The verifier did not have enough to check save\_cached, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
No difference found (not proven): No behavior difference found in generate\_call\_table\_rows (not a proof).
The verifier ran both versions of generate\_call\_table\_rows on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_node\_link\_payload (not a proof).
The verifier ran both versions of \_node\_link\_payload on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify write\_callflow\_html.
The verifier did not have enough to check write\_callflow\_html, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `project` is annotated `str | Path | None` — outside the synthesizable primitive/collection set
Could not verify: Could not verify dispatch\_command.
The verifier did not have enough to check dispatch\_command, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)
Could not verify: Could not verify \_stamped\_manifest\_files.
The verifier did not have enough to check \_stamped\_manifest\_files, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
· 2 more finding(s) on lines outside this diff (see the check run).
#1) - docker-compose.yaml: two services — graphify (CLI indexing, profile:cli) and mcp (HTTP MCP server); prefers ghcr.io/graphify-labs/graphify:latest when present, falls back to local build; GRAPHIFY_API_KEY/PORT/IMAGE env vars; SELinux :Z guidance; TCP socket healthcheck (avoids SSE 406 false negatives); user:0 for podman rootless bind-mount compat - Makefile: auto-detects docker vs podman (RUNTIME override); targets: pull, build, up, down, index [SRC=...], logs; up/index try GHCR pull first, fall back to local build; mkdir -p graphify-out guard - .github/workflows/docker.yml: native runners (ubuntu-latest for amd64, ubuntu-24.04-arm for arm64) — avoids broken QEMU cross-compiled C extensions; pushes :latest, semver, and :sha- tags; GHA layer cache Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Expand the "Shared HTTP server" section with a new "Running in a container" subsection covering: GHCR pull (no local Python install), make quick start, docker/podman compose examples, bare docker/podman commands, Podman rootless and SELinux notes, GRAPHIFY_IMAGE override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Change entrypoint from python -m graphify.serve to graphify CLI so the image serves both indexing (graphify extract) and the MCP server (graphify-mcp via --entrypoint); CMD defaults to --help - Pin mcp<2.0.0: mcp 2.0.0 removed mcp.types.AnyUrl which graphify.serve imports for the HTTP transport (ImportError at startup) - Pin cryptography<42: cryptography 42+ ships a Rust _openssl binding that crashes with SIGILL (exit 132) on ARM64 container runtimes (podman on Apple Silicon); 41.x is cffi-based and unaffected Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.
Graphify review — findings
This PR adds container-based distribution for graphify. It introduces a GitHub Actions workflow that builds and pushes multi-arch (amd64/arm64) Docker images to GHCR on native runners, a Makefile and docker-compose.yaml for running builds/indexing/the MCP server via Docker or Podman, and modifies the Dockerfile (new entrypoint, pinned mcp<2.0.0 and cryptography deps, general-purpose usage). The README is updated with a new "Running in a container" section documenting the make, compose, and bare docker/podman workflows.
Worth a look
- Docker image entrypoint no longer starts the MCP server —
Dockerfile:44· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
- Docker CMD changed from server launch to --help breaks existing run invocations —
Dockerfile:44· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review Execution auto-disposal is off for this run; enable it (with sandbox isolation) to have Graphify try to confirm or refute this automatically.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 27 functions depend on the 27 functions this change touches.
Health — grade A; no new coupling hotspots.
Verification — 27 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 27 function(s) in the blast radius were not formally verified this run
Summary
.github/workflows/docker.yml— buildslinux/amd64+linux/arm64images via native runners (no QEMU) on push tov8and on release; pushes toghcr.io/graphify-labs/graphifywith:latest, semver, and:sha-tags; uses GHA layer cache for fast incremental rebuildsdocker-compose.yaml— two services:graphify(CLI indexing,profile:cli) andmcp(HTTP MCP server); prefers the GHCR image when present, falls back to localbuild:; supportsGRAPHIFY_API_KEY,GRAPHIFY_PORT,GRAPHIFY_IMAGEenv vars; SELinux:Zguidance in comments; TCP socket healthcheck (avoids SSE framing false negatives)Makefile— auto-detectsdockervspodman; targets:pull,build,up,down,index [SRC=...],logs;upandindexauto-pull from GHCR, fall back to local buildDockerfile— generalize entrypoint frompython -m graphify.servetographifyCLI;graphify-mcpstill reachable via--entrypoint; pinmcp<2.0.0(2.0.0 removedmcp.types.AnyUrlused bygraphify.serve) andcryptography<42(42+ Rust_opensslbinding crashes with SIGILL on ARM64 container runtimes)README.md— expand container section with GHCR pull,makequick start, compose and bare docker/podman examples, Podman/SELinux notesTest plan
podman buildsucceeds locally (tested on Apple Silicon)podman compose configparsesdocker-compose.yamlwithout errorspodman run --rm graphify --helpandpodman run --rm --entrypoint graphify-mcp graphify --helpboth workdocker.yml) ran green on push: build + pushlinux/amd64+linux/arm64in 3m 48smake pull && make index && GRAPHIFY_API_KEY=x make up— end-to-end smoke test (graphify extract /src --code-only --output /data+ MCP server starts on:8080)Notes for maintainers
mcp<2.0.0pin is a workaround —graphify.serveimportsfrom mcp.types import AnyUrlwhich was removed in mcp 2.0.0. The HTTP transport needs updating for mcp 2.x compatibility.cryptography<42pin avoids the Rust_opensslbinding (introduced in 42) that crashes with SIGILL in ARM64 container runtimes (podman on Apple Silicon). Should be revisited when upstream fixes the runtime compatibility.🤖 Generated with Claude Code