@rtsantos3/trellis-app
v0.16.0-beta.5f
Published
Knowledge graph framework for Jupyter notebooks — organize, link, and visualize data science workflows with a force-directed graph GUI, CLI, and REST API
Maintainers
Readme
Trellis — Knowledge Graph for Jupyter Notebooks
Organize, link, and visualize your data science work. Trellis turns a project directory into an interconnected knowledge graph — notebooks, datasets, findings, and concepts — with a force-directed graph GUI, tiled workspace, embedded JupyterLab, and a full CLI + REST API.
Think Obsidian meets Hex, built for data science and research workflows.
Latest: 0.16.0-beta.5f — current beta release.
How It Works
Trellis wraps your existing project directory — notebooks, scripts, datasets, and outputs — in a local SQLite-backed knowledge graph. You run trellis init to scaffold a .trellis/ metadata store, then use the CLI or GUI to register nodes (notebooks, datasets, findings, concepts, etc.) and link them with typed, weighted edges. A built-in FastAPI server exposes the graph over REST and launches a managed JupyterLab subprocess, so the browser-based GUI gives you a force-directed graph view, a tiled workspace, and an embedded notebook editor in one window. File watchers detect new artifacts as notebooks run, and a mutation log tracks every change with actor attribution — making Trellis suitable for both interactive use and agent-driven pipelines.
Features
Core (Python)
- Knowledge graph — nodes (project, notebook, dataset, finding, concept, hypothesis, method, reference, artifact, custom) and typed edges (supports, contradicts, extends, depends_on, references, derived_from, parent_of, related_to, custom)
- SQLite storage — local-first SQLAlchemy store with parent hierarchy, slug uniqueness, and subgraph traversal
- CLI —
trellis init,add,link,annotate,annotation,list,get,find,grep,subgraph,path,history,export,import,update,rm/restore/purge,kernel register,serve - Dataset access —
trellis.load('slug')→ pandas DataFrame; auto-discovers.csv/.parquetfiles fromdata/directory - Artifact detection — file-watcher-based artifact capture with auto-linking to parent notebooks
API (FastAPI)
- Full REST API for nodes, edges, annotations, search, graph traversal
- Managed Jupyter Notebook 7 subprocess integration
- Notebook content serving with allowlisted roots and size cap
- Dataset discovery and artifact scan endpoints
- API key authentication (
TRELLIS_API_KEY) with reverse-proxy support (TRELLIS_TRUST_PROXY)
GUI (React + Vite)
- Tiled workspace — draggable/resizable panels with persisted layout
- Graph panel — force-directed knowledge graph with node type icons, edge labels, zoom/pan, search, and command palette
- Detail panel — Details, History, Metadata, Git Timeline, and Artifacts tabs; git commit history with diff previews for file-backed nodes
- Files panel — workspace file browser with system viewer integration
- Notebook panel — embedded JupyterLab iframe per notebook node (
.ipynb,.Rmd,.qmd,.py,.r,.sh,.md) - Force layout controls — gravity, repulsion, link distance, and halo radius sliders
Installation
Requirements
- Node.js 18+
- Python 3.10+ (auto-detected; used as the backend runtime)
Install
npm install -g @rtsantos3/trellis-appThe installer auto-detects your Python 3.10+ environment and bootstraps the Python backend on first run. If you use conda:
conda create -n trellis python=3.12
conda activate trellis
npm install -g @rtsantos3/trellis-appTo update Trellis, reinstall the package:
npm install -g @rtsantos3/trellis-appThe NPM latest dist-tag tracks the newest tagged Trellis release. The launcher re-syncs the bundled Python backend when the installed Python package version does not match the NPM package.
Quickstart
# initialize a project
trellis init
# optional: register a Jupyter kernel for notebook editing
python -m pip install ipykernel
python -m ipykernel install --user --name trellis --display-name "Python (trellis)"
# start the server
trellis serveThen open http://127.0.0.1:8050 and select a node in the graph or tree to view details, annotations, history, and notebooks.
Running
# 1) Start backend + static frontend
trellis serve
# 2) Open UI
# http://127.0.0.1:8050
# Debug/reload mode
trellis serve --debug
# Disable managed JupyterLab startup
trellis serve --no-jupyter
# Custom JupyterLab preferred port
trellis serve --jupyter-port 8899
# Custom API/UI port
trellis serve --port 8080
# Non-loopback bind requires explicit opt-in
trellis serve --host 0.0.0.0 --insecure-expose
API Authentication
By default, Trellis runs on 127.0.0.1 (localhost) with no authentication required. If you need to expose Trellis over a network, set an API key:
# 1. Export your key (use any secure random string)
export TRELLIS_API_KEY="$(openssl rand -hex 32)"
# 2. Start with network binding
trellis serve --host 0.0.0.0 --insecure-exposeClients authenticate with either header:
curl -H "Authorization: Bearer $TRELLIS_API_KEY" http://<host>:8050/api/graph
# or
curl -H "X-API-Key: $TRELLIS_API_KEY" http://<host>:8050/api/graphIf you're behind a reverse proxy, also set TRELLIS_TRUST_PROXY=true so auth is enforced regardless of the forwarded client address.
Environment variables
| Variable | Default | Description |
|----------|---------|-------------|
| TRELLIS_API_KEY | unset | API token; required when binding to non-loopback interfaces |
| TRELLIS_TRUST_PROXY | false | Enforce auth behind a reverse proxy |
| TRELLIS_CORS_ORIGINS | localhost origins | Comma-separated allowed browser origins |
| TRELLIS_NOTEBOOK_ROOTS | cwd | Comma-separated allowlisted roots for notebook file access |
| TRELLIS_NOTEBOOK_MAX_BYTES | 10485760 | Max notebook file size (10 MiB) |
Node Types
| Type | Description |
|------|-------------|
| project | Top-level container for a body of work |
| notebook | A computational notebook or script (.ipynb, .Rmd, .qmd, .py, .r, .sh, .md) |
| dataset | A data source — CSV, Parquet, database table, etc. |
| finding | A discrete result or observation produced by analysis |
| concept | A domain idea or theme that connects related work |
| hypothesis | A testable claim under investigation |
| method | An analytical technique or pipeline step |
| reference | A canonical external source node; legacy paper and url inputs/records remain accepted as compatibility aliases, but new usage should prefer reference |
| artifact | A generated output — plot, model file, export, etc. |
| custom | A user-defined node type for cases that do not fit the built-in semantic classes |
Edge Types
| Type | Description |
|------|-------------|
| supports | Source provides evidence for target |
| contradicts | Source provides evidence against target |
| extends | Source builds on or refines target |
| depends_on | Source requires target to function |
| references | Source cites or links to target |
| derived_from | Source was produced from target |
| parent_of | Source is the hierarchical parent of target |
| related_to | General association (weak-tier) |
| custom | User-defined relationship type |
CLI
trellis init
# create/link/annotate
trellis add project "My project"
trellis add notebook "My notebook" --parent my-project --file analysis.ipynb --tags baseline,eda
trellis add dataset "My dataset" --parent my-project
trellis link my-notebook my-dataset references
trellis annotate my-notebook "Key finding: seasonal drift" --tags review
# inspect
trellis list
trellis list --include-deleted
trellis get my-notebook --json
trellis find --tag baseline --text drift --limit 20 --json
trellis grep "seasonal drift" --scope node --scope annotation --json
trellis subgraph my-project --mode edges --depth 2 --json
trellis subgraph my-project --mode edges --depth 2 --summary
trellis path my-notebook my-dataset --summary
trellis info my-notebook
trellis show
# update (partial patch; only provided fields are changed)
trellis update my-notebook --title "My notebook v2" --tags baseline,production
trellis update my-notebook --attribute description --text "Updated summary"
trellis add reference "Turney 2010" --parent my-project --metadata '{"schema":"reference-v1","title":"Turney 2010","doi":"10.1000/example"}'
trellis update turney-2010 --citation "Turney, 2010" --metadata '{"url":"https://example.com/paper"}'
trellis update --uuid <node-uuid> --clear-description --clear-file --clear-tags
# lifecycle
trellis rm my-notebook --force # soft-delete to trash
trellis restore my-notebook # restore from trash
trellis purge my-notebook --force # permanent delete
trellis restore --uuid <node-uuid> # disambiguate duplicate slugs
trellis purge --uuid <node-uuid> --force
# edge/annotation maintenance
trellis link-update --edge-id <edge-uuid> --label "confidence:high"
trellis unlink --edge-id <edge-uuid>
trellis annotation add my-notebook "Check residuals" --tags review
trellis annotation list my-notebook --json
trellis annotation rm <annotation-uuid>
# history (mutation log)
trellis history my-notebook
trellis history --actor-id my-agent --operation create,update --since 2025-01-01 --json
# explicit local-first checkpoint/sync flow
trellis export --path .trellis/outgoing/checkpoint.jsonl
trellis import --path .trellis/outgoing/checkpoint.jsonl
# kernel registration for Jupyter
trellis kernel register trellis-demo --display-name "Python (trellis-demo)"
trellis kernel register my-env --python /path/to/python --sys-prefix
# serve GUI/API
trellis serve [--port PORT] [--host HOST] [--debug] [--insecure-expose]Notes:
- SQLite is the live local source of truth during editing.
export/importare explicit checkpoint/sync operations.- Non-
project/non-conceptnode types require a parent (--parent <slug>or--parent-uuid <uuid>). referenceis the canonical external-source node type;paperandurlremain legacy compatibility aliases.- Reference metadata writes go through
--citation,--abstract, and--metadata; direct CLI--urimutation is removed.
Graph Query Commands
Use these commands to pull bounded context for humans or agents without dumping the whole ledger:
# global text search over nodes, edges, and annotations
trellis grep "quantity" --json
trellis grep "quantity" --scope node --scope annotation --json
# retrieve context around one node
trellis subgraph phase-3b --mode hierarchy --depth 2 --json
trellis subgraph phase-3b --mode edges --depth 2 --direction both --json
trellis subgraph phase-3b --mode edges --relationship references --summary
# pipe a bounded node set into grep
trellis subgraph phase-3b --mode edges --depth 2 --ids \
| trellis grep quantity --stdin-ids --json
# export an importable bounded context
trellis subgraph export phase-3b --mode edges --depth 2 --path context.jsonl
# ask how two nodes are connected
trellis path phase-3b-transformer-training phase-3c-phylogenetic-supervision --summary
trellis path phase-1 phase-2 phase-3 --json
trellis path --uuid <id-a> --uuid <id-b> --uuid <id-c> --slugs
trellis path source-node target-node --relationship references --jsontrellis subgraph answers "what context is around this node"; trellis path answers "how are these two nodes connected." subgraph --mode hierarchy follows containment, while subgraph --mode edges follows persisted graph edges. path uses unweighted shortest-path search over persisted edges; 3+ slugs or repeated --uuid values are treated as ordered waypoints.
Agent Reference
Trellis is agent-agnostic by design — any LLM or agent framework that can invoke shell commands can use Trellis directly. No plugins, no MCP server, no custom integrations required. trellis init generates a .trellis/README.md with the common operational CLI reference for agent consumption.
Contract:
- Read: graph state (nodes/edges/annotations), mutation history, datasets, artifacts.
- Write: annotations first, then findings/links when confidence is high.
- Always include provenance via
--actor-id.
Example agent workflow:
# read current graph state
trellis list --json
trellis find --tag review --json
# annotate a node with provenance
trellis annotate my-notebook "Residuals show seasonal drift" --tags review --actor-id agent-run-1
# create a finding when confidence is high
trellis add finding "Seasonal drift in Q3" --parent my-project --actor-id agent-run-1
trellis link seasonal-drift-in-q3 my-notebook derived_from --actor-id agent-run-1
# check mutation history
trellis history --actor-id agent-run-1 --jsonNotebook Runtime Notes
- Trellis manages one Notebook runtime per
trellis serveprocess. - Notebook node
file_pathmust be project/workspace-relative (or an absolute path that resolves under the same workspace root). - Kernel/environment selection is handled by Jupyter kernelspec metadata and installed kernels visible to the running Trellis environment.
- You do not assign/create environments inside Trellis; start Trellis from the environment that has the kernels you want available.
- If managed Jupyter startup fails, Trellis API/UI still start; notebook iframe execution is unavailable until restart with a valid runtime.
Kernel Management (Python / Conda)
Register each environment you want as a Jupyter kernel:
conda activate my-env
python -m pip install ipykernel
python -m ipykernel install --user --name my-env --display-name "Python (my-env)"Then start Trellis from your active environment:
conda activate trellis
trellis serveIn the notebook iframe, switch kernels via Kernel -> Change kernel.
Check visible kernels:
jupyter kernelspec listKernel Management (R / renv)
Install and register IRkernel from the R environment you want:
R -e "install.packages('IRkernel', repos='https://cloud.r-project.org')"
R -e "IRkernel::installspec(name='ir-main', displayname='R (main)')"For project renv, keep renv activation files in project root (.Rprofile, renv/activate.R) so notebooks use that project library context.
For .Rmd, set notebook metadata kernelspec.name to the registered R kernel, or switch it from the Jupyter kernel menu at runtime.
License
MIT + Commons Clause. See LICENSE for details.
Free for personal, academic, and internal use. You may not sell Trellis or offer it as a paid hosted service.
