Nuxeo Integration Guide¶
This guide covers Nuxeo-specific configuration for the Flexible GraphRAG system: the three authentication methods, path conventions, and optional real-time change sync via Kafka.
About Nuxeo¶
Nuxeo (owned by Hyland) is an open-source content services platform. Flexible GraphRAG connects to a Nuxeo repository as a data source — enumerating documents under a path (or by node id), downloading each document's content, and ingesting it into the knowledge graph / vector / search stores. Like Alfresco, it supports optional incremental auto-sync so that changes in Nuxeo flow into the graph in real time.
Requirements¶
- A running Nuxeo server (LTS 2023+ recommended). A local option is aborroy/nuxeo-deployment (PostgreSQL-backed Nuxeo built from public source).
- The Nuxeo Python client — already declared in
pyproject.toml, so a normal install pulls it in. To add it to an existing venv:Do not install
nuxeo[oauth2]. That extra isauthlib+jwt, and itsjwtis GehirnInc python-jwt, which claims the same top-leveljwtmodule as PyJWT and overwrites it — breakingpython-arango(and solangchain-arangodband the ArangoDB graph store),authlibitself, and Langflow. The two cannot coexist and no version pin resolves it.Flexible GraphRAG installs the extra's two halves directly instead —
authlibandpyjwt[crypto]— andsources/nuxeo.pyimplements the three symbolsnuxeo.auth.oauth2needs (JWT,jwk_from_dict,JWTDecodeError) on top of PyJWT. All three auth methods, including client-side access-token validation, work normally.
Configuration¶
Set these in your .env (used by the REST API and MCP server; the UIs have their own form):
# Nuxeo Configuration
# Base repository URL (NO /api/v1 suffix — the client appends it)
NUXEO_URL=http://localhost:8081/nuxeo
# Auth method: basic | oauth2 | token (default: basic)
NUXEO_AUTH_METHOD=basic
# Path to ingest (folder or single document)
NUXEO_PATH=/default-domain/workspaces/GraphRAG
In the UI, choose Nuxeo Repository as the data source, pick the Authentication method, and fill in the fields for that method plus the path.
Path Conventions¶
Nuxeo paths are domain-rooted and use the document's internal name (ecm:name), which is
lowercased/normalized (and truncated ~24 chars) from the title at creation. Always copy the path
from the Nuxeo browse URL — everything after #!/browse is the exact path:
- A folder path ingests all supported documents under it (recursive optional).
- A single-document path ingests just that document.
- File vs Note: importing a
.txt/.mdtypically creates a Note (text inline innote:note), while uploaded PDFs/Office files are File documents (binary blob infile:content). Both ingest. Note that only Notes are text-editable inline in the Web UI; to "modify" a File you upload a new version of its blob.
Authentication Methods¶
1. Basic (username / password)¶
The simplest option — good for local testing (Administrator / Administrator).
2. Token (X-Authentication-Token)¶
A persistent Nuxeo auth token, independent of web-UI sessions (survives UI timeout; valid until revoked). Mint one with basic auth:
curl -u Administrator:Administrator \
"http://localhost:8081/nuxeo/authentication/token?applicationName=flexible-graphrag&deviceId=my-device&permission=ReadWrite"
Then:
3. OAuth2 (Bearer)¶
Uses the authorization-code + PKCE flow. You obtain a token out-of-band and provide it; the client auto-refreshes when a refresh token is present.
Step 1 — register an OAuth2 client in Nuxeo. In the Admin Center → Cloud Services →
Consumers, add a client with a Client ID, an optional secret, and a redirect URI
(e.g. http://localhost:8888/callback).
This is a manual, per-deployment step that does not survive recreating the Nuxeo containers — the client lives in Nuxeo's
oauth2Clientsdirectory, not in this repo. After a fresh deployment onlynuxeo-driveandnuxeo-mobileexist, and/oauth2/authorizereturns HTTP 400 until you re-register.
If the Web UI screen doesn't persist it, you can register it via the directory API:
curl -u Administrator:Administrator -H "Content-Type: application/json" \
-d '{"entity-type":"directoryEntry","directoryName":"oauth2Clients",
"properties":{"clientId":"flexible-graphrag","name":"flexible-graphrag",
"redirectURIs":"http://localhost:8888/callback",
"autoGrant":true,"enabled":true}}' \
http://localhost:8081/nuxeo/api/v1/directory/oauth2Clients
Step 2 — obtain a token via the PKCE flow (authorization URL → browser approve → exchange the
redirect for a token). Default endpoints are <url>/oauth2/authorize and <url>/oauth2/token.
Step 3 — configure:
NUXEO_AUTH_METHOD=oauth2
NUXEO_OAUTH2_CLIENT_ID=flexible-graphrag
# NUXEO_OAUTH2_CLIENT_SECRET= # only if you set one
NUXEO_OAUTH2_ACCESS_TOKEN=<access-token>
NUXEO_OAUTH2_REFRESH_TOKEN=<refresh-token>
# NUXEO_OAUTH2_TOKEN_ENDPOINT= # optional; defaults to <url>/oauth2/token
In the UI's OAuth2 fields, provide the Client ID (and secret if set) plus the access/refresh tokens.
Real-Time Change Sync (optional, via Kafka)¶
With auto-sync enabled, Nuxeo document changes (create / modify / delete) flow into the graph in real time. This works by consuming Nuxeo's audit stream from Kafka. It requires Kafka enabled on the Nuxeo server.
Working reference (unofficial/temporary): a copy of a compose +
kafka.confwe got working lives inexamples/nuxeo-deployment/. It is built on top of Angel Borroy's content-lake POC deployment (github.com/aborroy/nuxeo-deployment, Apache-2.0) — a proof-of-concept, not an official Nuxeo image, and not standalone-runnable. The generic steps below apply to any Nuxeo deployment.
1. Nuxeo server — enable Kafka (compose-only, no image rebuild)¶
Nuxeo appends any *.conf dropped in /etc/nuxeo/conf.d/ to nuxeo.conf at startup. Create a
kafka.conf:
kafka.enabled=true
kafka.bootstrap.servers=kafka:29092
kafka.topicPrefix=nuxeo-
kafka.default.replication.factor=1
nuxeo.stream.audit.enabled=true
Gotcha: the entrypoint only regenerates
nuxeo.conf(and re-appliesconf.d) when/etc/nuxeo/nuxeo.confis absent. If/etc/nuxeois a persisted volume, config freezes at first-run and yourkafka.confis ignored. Keep/etc/nuxeoephemeral (don't mount a volume on it) soconf.dapplies on every start.
2. A single-node Kafka broker (generic docker-compose)¶
services:
kafka:
image: apache/kafka:3.8.1
hostname: kafka
ports:
- "9092:9092" # host listener for the Python consumer
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_LISTENERS: PLAINTEXT://kafka:29092,CONTROLLER://kafka:29093,PLAINTEXT_HOST://0.0.0.0:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT,CONTROLLER:PLAINTEXT
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:29093
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_LOG_DIRS: /tmp/kraft-combined-logs
CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk
Notes:
- Dual listener so both sides reach the broker: the Nuxeo container uses the internal
kafka:29092; the host-side Python consumer uses localhost:9092. (A containerized Nuxeo
can't reach a sibling container via localhost.)
- Don't mount a named volume on the Kafka log dir — the apache/kafka image runs as uid 1000 and a
root-owned volume breaks KRaft formatting. For a dev broker, persistence isn't needed.
- Optional: add a provectuslabs/kafka-ui service to browse topics (point it at kafka:29092).
3. Flexible GraphRAG — point the consumer at the broker¶
The consumer defaults to localhost:9092. Override if needed:
Then in the UI, check Enable auto change sync when you ingest the Nuxeo source. Create / edit /
delete a document in the monitored path and it auto-ingests within a second or two (watch the
backend log for NUXEO EVENT: CREATE / UPDATE / DELETE).
How it works: Nuxeo publishes audit entries to the nuxeo-audit-audit topic; the detector decodes
the embedded JSON, maps documentCreated / documentModified / documentRemoved to add / modify /
delete, resolves the live document, and ingests it. Version snapshots (versionCreated,
documentCheckedIn) are ignored, and each save's event burst is de-duplicated by the document's
dc:modified timestamp.
Python Library Information¶
- Nuxeo client:
nuxeo(plain, not the[oauth2]extra — see the install note above) — basic / token / JWT / OAuth2 auth, NXQL query, blob download. - OAuth2 support:
authlibpluspyjwt[crypto], which together replace the[oauth2]extra without thejwtmodule collision. - Kafka client (for real-time sync):
kafka-python-ng. - Both are declared in
flexible-graphrag/pyproject.toml, so a standard install includes them.
File Structure (Nuxeo-specific)¶
sources/nuxeo.py— Nuxeo data source connector (File + Note documents).incremental_updates/detectors/nuxeo_audit.py— Kafka audit consumer + JSON decoder.incremental_updates/detectors/nuxeo_detector.py— real-time change detector.
Notes¶
- Nuxeo's default versioning policy snapshots a new version (0.1 → 0.2 → …) on each update. This is expected and does not affect ingestion — version events are filtered out.
- Auth tokens (method 2) persist until explicitly revoked; a web-UI session timeout does not affect them.
- OAuth2 access tokens are short-lived; supply the refresh token so the client can renew.