ADR-0007 — Apache AGE as Property Graph Layer on PostgreSQL¶
Status: Accepted
Date: 2026-05-24
Author: João Luís Brazão
Deciders: João Luís Brazão
Vikunja: #838
Context¶
The Down a Rabbit Hole second brain holds 15,000+ knowledge nodes with rich relational structure: nodes link to other nodes (co-citation, similarity, contradiction), nodes mention entities (people, organisations, concepts), and entities relate to each other (LEADS, MEMBER_OF, PARTY_TO, etc.).
The existing pgvector layer enables semantic similarity search (RAG) but cannot answer graph traversal questions such as: - "What nodes are within 2 hops of this concept?" - "Which entities appear together most often?" - "Find all paths between Ukraine War and Financial Fragmentation."
An additional graph capability was needed without introducing a separate database system (Neo4j, ArangoDB) that would break the sovereignty-first and single-datastore principles established in ADR-001 and ADR-003.
Decision¶
Add Apache AGE 1.5.0 (A Graph Extension) to the existing PostgreSQL 16
instance alongside pgvector 0.8.2. AGE implements the openCypher query
language natively inside PostgreSQL, so graph queries run as standard SQL
with SELECT * FROM cypher(...) syntax.
The custom Docker image localhost/infra-postgres-age:16-age1.5.0 is built
from pgvector/pgvector:pg16 (to preserve the pgvector base) with AGE
compiled from source and shared_preload_libraries = 'age' injected into
postgresql.conf.sample.
The named graph down_rabbit_hole_graph holds three vertex labels:
- KnowledgeNode — mirrors public.nodes
- Entity — mirrors entity types from ontology_entity_types
- Topic — taxonomy terms acting as concept hubs
And three edge labels:
- LINKS_TO — from node_links table
- MENTIONS — from node_entity_mentions table
- TAGGED_WITH — node → taxonomy term associations
The brain.graph-rebuild skill populates the graph from the relational
tables. It runs on-demand and via the weekly cron (Sunday 04:30 UTC).
Alternatives Considered¶
| Option | Reason Rejected |
|---|---|
| Neo4j | Separate process, breaks single-datastore principle (ADR-001) |
| ArangoDB | Same — separate process + different query language |
| PostgreSQL recursive CTEs | Can traverse but no property graph model, no Cypher |
| PostGIS graph extension | Geographic focus, not semantic graph |
| Apache AGE (chosen) | Runs inside PostgreSQL; openCypher; MIT licence; active community |
Consequences¶
Positive:
- Zero new infrastructure — AGE lives inside the existing infra-postgres container
- Graph queries use standard PostgreSQL connections (same DSN, same auth)
- openCypher is industry-standard (Neo4j-compatible syntax)
- Enables future DARH graph traversal skills without external dependencies
Negative / Risks:
- AGE is a PostgreSQL extension, not a native graph DB — performance ceiling
exists for very large graphs (>10M edges); acceptable for current scale
- Must rebuild infra-postgres image if AGE needs upgrading (no ALTER EXTENSION)
- AGE requires CREATE EXTENSION age per database; new databases need
explicit migration step
- Existing volumes work unchanged — AGE does not modify base PostgreSQL storage
Migration Notes¶
- Compose backup:
/opt/infrastructure/docker-compose.yml.bak-pre-age-20260524T200101Z - DB dump pre-migration:
/opt/backups/pre-age-20260524-193501.sql.gz(771 MB) - SQL migration:
/opt/infrastructure/sql-migrations/20260524_subsystem1.sql - Rollback: restore compose + dump; no data loss since AGE data is derived
from relational tables and can be rebuilt by
brain.graph-rebuild
Related ADRs¶
- ADR-001: PostgreSQL + pgvector as single datastore → AGE extends this
- ADR-003: Sovereignty boundary → AGE maintains it (same VPS, same container)
- ADR-005: Skills Gateway →
brain.graph-rebuildskill populates graph