cd graph_db
docker compose up -d- Browser UI: http://localhost:7474
- Bolt (Python driver): bolt://localhost:7687
Configured via root .env file:
NEO4J_URI- Bolt connection URI (default:bolt://localhost:7687)NEO4J_USER- Username (default:neo4j)NEO4J_PASSWORD- Your password
This file covers running Neo4j. What the graph CONTAINS - every node label, its properties, and the relationships between them - is declared once, in graph_db/schema_sections.md.
# the whole schema, as the agent is served it
python3 -c "from graph_db.schema_render import render_schema; print(render_schema())"
# one label
python3 -c "from graph_db.schema_render import render_label; print(render_label('Subdomain'))"Constraints and indexes are generated from
graph_db/schema_keys.py and applied by
init_schema on every container spawn, so a fresh database needs no manual DDL.
After editing the schema, re-seed the catalog:
python3 tooling/scripts/seed_schema_catalog.pyGRAPH.SCHEMA.md explains WHY the graph is shaped this way -
the tenancy model, the Muted label, Scan Timeline. It deliberately lists no
labels.
The graph is automatically populated after each recon scan phase completes. Graph updates are controlled by the UPDATE_GRAPH_DB setting in the project configuration. GitHub Secret Hunt results are also ingested into the graph after scan completion.
# Start Neo4j
cd graph_db && docker compose up -d
# Stop Neo4j
docker compose down
# Stop and remove all data (fresh start)
docker compose down -v
# View logs
docker compose logs -f
# View last 50 lines of logs
docker compose logs --tail 50
# Check container status
docker compose ps
# Restart Neo4j
docker compose restart
# Enter container shell
docker exec -it redamon-neo4j bashRun these in the Neo4j Browser at http://localhost:7474
Queries sent through the AI agent, the MCP server or a saved graph view are
tenant-scoped before they run, and they refuse variable-length paths and namespaced
function calls (graph_db/tenant_filter.py,
injectProjectFilter.ts;
rules in the MCP Server wiki page). To read an
attack chain's steps there, match (s:ChainStep {chain_id: $chain_id}): HAS_STEP
links only the first step, and a NEXT_STEP* walk is refused.
-- Show all nodes and relationships
MATCH (n) OPTIONAL MATCH (n)-[r]->(m) RETURN n, r, m
-- Show all nodes (browser auto-draws relationships)
MATCH (n) RETURN n
-- Count all nodes by type
MATCH (n) RETURN labels(n) AS type, count(n) AS count-- Show all nodes and relationships for a project
MATCH (n {project_id: "first_test"})
OPTIONAL MATCH (n)-[r]->(m)
RETURN n, r, m
-- Filter by both user_id and project_id
MATCH (n {user_id: "samgiam", project_id: "first_test"})
OPTIONAL MATCH (n)-[r]->(m)
RETURN n, r, m-- Delete all nodes and relationships (clear database)
MATCH (n) DETACH DELETE n
-- Delete all data for a specific project
MATCH (n {project_id: "first_test"})
DETACH DELETE n
-- Delete by user_id and project_id
MATCH (n {user_id: "samgiam", project_id: "first_test"})
DETACH DELETE n