* fix(explorer): resolve ontology ownership on the backend Which registered ontology owns an entity was decided twice: the backend applies nested-namespace boundaries, while the Ontology Editor did a bare prefix match. The two had already drifted, so a deep link to an entity in an unregistered nested namespace selected the parent ontology whose /graph response excludes that entity, and the selection silently failed. /api/ontology/entity now returns owning_ontology, resolved with the same rule the graph endpoint filters by, and the editor prefers it. The frontend namespace guess stays as the fallback for a missing verdict, documented as non-authoritative. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(explorer): keep the backend's no-owner verdict authoritative Review follow-up: loadOntologyEntityOwner collapsed the backend's explicit owning_ontology: null into undefined, re-activating the namespace prefix guess for exactly the unregistered-nested-namespace case this PR exists to fix. The owner verdict is now three-state (owner / authoritative none / unavailable) and resolveEditorOntology in the model suppresses inference on an authoritative none; only an unavailable verdict may fall back. Model tests pin all three states. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(explorer): trust the backend to send owning_ontology The Explorer bundle ships in the same wheel as the route that emits this field, so the legacy-response branch could never run. Dropping it lets the type say what the wire actually carries, leaving undefined to mean only what it should: the request failed. Note why the endpoint derives its ontology-URI set inline rather than calling _known_ontology_uris, so the next reader does not consolidate a graph scan back in. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix(explorer): stop the registry default overriding a no-owner verdict resolveEditorOntology returned string | undefined, so the caller wrote `resolved || entries[0]?.uri` and an authoritative "nothing owns this entity" fell straight through to an arbitrary registry entry. When that entry happened to be the parent, the deep link opened the parent whose graph excludes the entity — the bug the verdict exists to prevent. Only the ordering of the registry in the earlier test hid it. It now returns a union: unowned and unresolved both mean "no ontology to open" but the editor treats them oppositely, so collapsing them with || is a type error rather than a silent regression. An unowned entity is reported on the canvas instead of quietly opening the wrong ontology. Also: - /entity resolves ownership through _known_ontology_uris, the same helper /graph uses, instead of deriving it from a get_nodes scan capped at 999,999. Past that cap the set was silently truncated and the two endpoints could disagree about who owns a node. - _resolve_owning_ontology does one pass over the candidates rather than one pass per candidate, each rescanning the whole set: 516us -> 9us at 50 ontologies, 125ms -> 138us at 800, same answers throughout. A test pins it against _node_belongs_to_ontology so the hand-rolled version cannot drift from the membership rule it has to mirror. - An explicit scheme_uri is honoured even when the registry does not list it, on both sides. Discarding it and guessing by namespace answered a question nobody asked; an unregistered owner now surfaces as an explicit error from /graph instead. - A missing owning_ontology field reads as "no verdict", not as the authoritative "nothing owns this". That claim now suppresses selection outright, so it must not be inferred from an absent field. Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Sameer Kadam <sskadam6305@gmail.com> Co-authored-by: Sameer Kadam <sameerkadam@Mac.lan>
Semantica Knowledge Explorer
A browser-based graph workbench for the Semantica platform. Pan and zoom live graphs, scrub the timeline, trace every decision's causal chain, resolve duplicates, and author your ontology visually. Built on React 19 + Sigma.js.
Requirements
| Dependency | Minimum version |
|---|---|
| Python | 3.8+ |
| Node.js | 18.x or higher (20.x recommended) |
| npm | 9.x or higher |
python --version
node --version
npm --version
Two ways to run the Explorer
Option A — pip install (recommended for users)
Install the package with the explorer extras. The pre-built frontend bundle is included in the wheel so no Node.js is required.
pip install "semantica[explorer]"
Launch the dashboard by pointing it at any graph JSON file:
semantica-explorer --graph my_graph.json
The server starts at http://127.0.0.1:8000 and opens the dashboard in your default browser automatically.
CLI flags:
| Flag | Default | Description |
|---|---|---|
--graph / -g |
(required) | Path to a ContextGraph JSON file |
--port / -p |
8000 |
Port to bind the server to |
--host |
127.0.0.1 |
Host to bind (use 127.0.0.1 for local-only; see security note below) |
--no-browser |
off | Skip opening the browser automatically |
Examples:
# Default — opens at http://127.0.0.1:8000
semantica-explorer --graph my_graph.json
# Custom port
semantica-explorer --graph my_graph.json --port 8080
# Suppress auto-open
semantica-explorer --graph my_graph.json --no-browser
# Equivalent using python -m
python -m semantica.explorer --graph my_graph.json
Security note: Since v0.6.5 the Explorer API requires an API key on protected routes. Set the
SEMANTICA_API_KEYenvironment variable and send it as theX-API-Keyheader; without a configured key, protected routes fail closed with503rather than serving anonymously. To opt into unauthenticated access for local development only, setSEMANTICA_ALLOW_ANONYMOUS=trueexplicitly. (/api/healthand/api/infoare intentionally unauthenticated.)The default
--host 127.0.0.1binds to localhost only, so it is not reachable from other machines on your network. If you bind to0.0.0.0, all graph data is readable and writable by any host that can reach the port (subject to API-key auth). The CLI prints a warning when binding to a non-loopback host in anonymous mode or whenSEMANTICA_API_KEYis unset.
Option B — run from source (for contributors / frontend development)
This mode runs the React dev server with hot module replacement, so frontend changes appear in the browser instantly without rebuilding.
Step 1 — Clone the repo
git clone https://github.com/semantica-agi/semantica.git
cd semantica
Step 2 — Install the Python package
pip install -e ".[explorer]"
Step 3 — Install frontend dependencies
cd explorer
npm ci
Step 4 — Start the Python backend
Open a terminal in the repo root:
semantica-explorer --graph path/to/my_graph.json --no-browser
This starts the API on http://127.0.0.1:8000. Keep this terminal open.
Step 5 — Start the frontend dev server
Open a second terminal in explorer/:
npm run dev
Vite starts on http://localhost:5173. Open that URL in your browser. All /api and /ws requests are automatically proxied to the Python backend at http://127.0.0.1:8000.
Building the production bundle
If you need to serve the UI from the Python server directly (without the Vite dev server):
cd explorer
npm ci
npm run build
This writes the compiled assets to ../semantica/static/. The Python server then serves the full dashboard at http://127.0.0.1:8000 — no separate Vite process needed.
Workspaces
| Workspace | What you can do |
|---|---|
| Knowledge Graph | Live Sigma.js canvas · ForceAtlas2 layout · Ego Mode · semantic distance heatmap · path highlighting |
| Timeline | Temporal event scrubber — watch the graph evolve across time |
| Decisions | Browse causal chains behind every recorded decision with outcome badges and confidence scores |
| Registry | Live audit log of every graph mutation (add-node, add-edge, delete, update) |
| Entity Resolution | Review and merge duplicate entities with blocking + semantic dedup |
| KG Overview | Aggregate stats, community breakdown, centrality heatmap |
| Ontology Hub | SHACL Studio · visual drag-and-drop editor · cross-ontology alignments · SKOS browser |
| Lineage | W3C PROV-O provenance visualization for any entity |
Environment variables
| Variable | Default | Description |
|---|---|---|
EXPLORER_CORS_ORIGINS |
http://localhost:5173,http://127.0.0.1:5173 |
Comma-separated list of allowed CORS origins |
EXPLORER_CORS_CREDENTIALS |
false |
Set to true to allow credentialed cross-origin requests (only needed behind an authenticating reverse proxy) |
SEMANTICA_API_KEY |
(unset) | API key required on protected routes since v0.6.5; send it as the X-API-Key header. When unset, protected routes fail closed with 503. |
SEMANTICA_ALLOW_ANONYMOUS |
false |
Set to true to opt into unauthenticated access (local development only). |
Available scripts
Run these from inside the explorer/ directory:
# Start the dev server with hot module replacement
npm run dev
# Type-check and build the production bundle into ../semantica/static/
npm run build
# Preview the production build locally
npm run preview
# Run ESLint over all source files
npm run lint
# Run the graph store multi-edge unit tests
npm run test:graph-store
# Run the graph workspace display tests
npm run test:graph-workspace
API & WebSocket proxy (dev mode only)
During development, Vite forwards requests automatically — no CORS configuration needed:
| Pattern | Forwarded to |
|---|---|
/api/* |
http://127.0.0.1:8000/api/* |
/ws/* |
ws://127.0.0.1:8000/ws/* |
To run the backend on a different port, update server.proxy in vite.config.ts.
Project structure
explorer/
├── src/
│ ├── App.tsx # Root layout, tab routing, workspace wiring
│ ├── index.css # Global resets, fonts, keyframe animations
│ ├── store/
│ │ ├── graphStore.ts # In-memory graph state
│ │ └── registryStore.ts # Pub/sub audit registry
│ └── workspaces/
│ ├── GraphWorkspace/ # Sigma.js canvas + inspector + behaviors
│ ├── DecisionWorkspace/ # Causal flow diagram + decision list
│ ├── DiffMergeWorkspace/ # Graph diff and merge view
│ ├── EnrichWorkspace/ # Entity resolution + registry tabs
│ ├── ImportExportWorkspace/ # Import CSV/JSON, export graph
│ ├── LineageWorkspace/ # W3C PROV-O lineage diagram
│ ├── ManageWorkspace/ # KG Overview + Ontology Summary
│ ├── OntologyWorkspace/ # SHACL Studio, visual editor, SKOS browser
│ ├── SparqlWorkspace/ # In-browser SPARQL query editor
│ └── VocabularyWorkspace/ # SKOS vocabulary manager
├── index.html
├── vite.config.ts # Dev proxy → 127.0.0.1:8000, build → ../semantica/static
└── package.json
Troubleshooting
Dashboard shows a blank white page or "UI not available" message
The frontend bundle is missing from the server's static directory. Fix options:
- If you installed via pip:
pip install --upgrade "semantica[explorer]"— the wheel includes the pre-built bundle. - If you installed from source: run
cd explorer && npm ci && npm run buildfrom the repo root, then restart the server. - In dev mode: use the Vite dev server at
http://localhost:5173instead of the backend URL.
Blank graph / no data loads in the browser
- Confirm the Python backend is running and check the terminal for errors.
- Open browser DevTools → Network tab and look for failed
/api/graphrequests. - If the backend is on a different port, update
server.proxyinvite.config.ts.
npm ci fails or reports missing lockfile
The package-lock.json must be present. Run npm install once to generate it, commit it, then use npm ci going forward.
npm run dev fails with Node version error
Vite 6 requires Node 18 or higher. Run node --version to check. If you're on Node 16, upgrade via nvm or the official Node.js installer.
Port 5173 already in use
Vite automatically tries the next available port and prints the actual URL in the terminal. Use the URL shown in the output.
WebSocket not connecting (real-time mutations not appearing)
- Confirm the backend exposes the
/ws/graph-updatesWebSocket endpoint. - Check DevTools → Network → WS tab for the connection status and error code.
- Ensure the backend version matches the frontend — mixing major versions can cause protocol mismatches.
- Authentication:
/ws/graph-updatesenforces the same API key as the REST routes. Browsers cannot set custom headers on a WebSocket handshake, so pass the key as a query parameter instead:Non-browser clients (native apps, scripts) may send it as thews://127.0.0.1:8000/ws/graph-updates?api_key=<your-key>X-API-Keyheader. A missing or incorrect key results in close code4401; ifSEMANTICA_API_KEYis unset andSEMANTICA_ALLOW_ANONYMOUSis nottrue, the connection is also rejected. Note that API keys in URLs appear in server logs — prefer the header for non-browser clients.
Tech stack
- React 19 + TypeScript (strict mode)
- Vite 6 with
babel-plugin-react-compiler - Sigma.js 3 + Graphology — graph rendering and in-memory graph model
- ForceAtlas2 — physics-based layout
- @tanstack/react-query — async data fetching for ontology and vocab tabs
- vis-timeline — temporal event visualization
- @xyflow/react — lineage diagram rendering
- Monaco Editor — in-browser SPARQL / SHACL editor
- lucide-react — icon set
Contributing
See the root CONTRIBUTING.md and open issues on the main Semantica repository.