Add peo4c: neo4j-browser on PostgreSQL + Apache AGE

A single container image with:
- neo4j-browser built from source and served on :7474
- PostgreSQL with Apache AGE (based on apache/age)
- a Python bridge speaking Bolt 4.4/5.0-5.4 over TCP and WebSocket on
  :7687 that runs Cypher through AGE's cypher() function, emulates the
  Neo4j procedures neo4j-browser relies on, and maps databases to AGE
  graphs

Includes workarounds for AGE limitations (parameters are inlined as
literals, `id(x) IN [...]` and the browser's expand-node query are
rewritten) and an end-to-end smoke test using the Neo4j Python driver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Bart BroereandClaude Opus 5.5 committed 2026-10-14 10:46:20 +02:00
commit 47b8f5f746
15 files changed
+1850

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Prepares a freshly initialised cluster for peo4c.
set -euo pipefail
# The bridge connects over 127.0.0.1 with the credentials users log in with, so
# loopback TCP connections must check passwords. The postgres image trusts them
# by default; Unix-socket connections (psql inside the container) stay trusted.
sed -i -E 's/^(host[[:space:]]+[^[:space:]]+[[:space:]]+[^[:space:]]+[[:space:]]+(127\.0\.0\.1\/32|::1\/128)[[:space:]]+)trust/\1scram-sha-256/' \
"$PGDATA/pg_hba.conf"
# Create the default AGE graph that neo4j-browser opens on first connect.
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "${POSTGRES_DB:-$POSTGRES_USER}" \
-v graph="${PEO4C_DEFAULT_GRAPH:-neo4j}" <<'EOSQL'
LOAD 'age';
SET search_path = ag_catalog, "$user", public;
SELECT ag_catalog.create_graph(:'graph')
WHERE NOT EXISTS (SELECT 1 FROM ag_catalog.ag_graph WHERE name = :'graph');
EOSQL
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Runs PostgreSQL (through the stock postgres entrypoint) and the peo4c bridge
# side by side, and stops the container when either of them exits.
set -euo pipefail
if [ "${1:-}" != "postgres" ]; then
exec docker-entrypoint.sh "$@"
fi
export PEO4C_PG_DATABASE="${PEO4C_PG_DATABASE:-${POSTGRES_DB:-${POSTGRES_USER:-postgres}}}"
docker-entrypoint.sh "$@" &
pg_pid=$!
cd /opt/peo4c/bridge
gosu postgres python3 -m peo4c &
bridge_pid=$!
shutdown() {
kill -TERM "$bridge_pid" 2>/dev/null || true
kill -TERM "$pg_pid" 2>/dev/null || true
}
trap shutdown TERM INT
# Wait until one of the two processes exits, then stop the other one too.
set +e
wait -n "$pg_pid" "$bridge_pid"
status=$?
shutdown
wait "$pg_pid" 2>/dev/null
exit "$status"