Notebook (Jupyter) — Administrator Guide
Giving analysts Python used to mean installing local environments, handing out credentials by e-mail and losing track of where the data ends up. With DATTA's Notebook environment, each analyst gets a full Jupyter inside the platform itself — same identity, same look, ready access to the data sources — while you keep credentials and isolation under central control. This guide covers how to enable the environment, the credential security model and how to diagnose the most common problems.
Audience: administration and operations. For analyst-facing notebook usage, see the Notebook Guide and the
What the analyst gets
- A full Jupyter Lab (notebook + terminal), reachable at , with no local installation.
- An individual, isolated environment per user, created on demand by JupyterHub.
- Ready-made connectors to the platform's sources — Neo4j, OpenSearch, Trino, the platform cache, MinIO and Spark Connect — with the data credentials already injected into the environment.
- A single identity: there is no separate login. Whoever is authenticated in DATTA enters the Notebook automatically.
How the identity travels
The Notebook portal asks the platform for an HttpOnly session cookie (datta_jwt), scoped to the /jupyter path. The JupyterHub authenticator validates that token (HS256, HS384 or HS512), requires the ANALISTA or ADMIN role and creates the hub user from the normalized e-mail — @ and . become -. The endpoint that issues this cookie is described in the API reference.
Traffic to /jupyter/ goes straight to the public JupyterHub proxy on port 8000; it does not pass through the platform's entry proxy, which keeps an equivalent route only as an internal fallback. Mind the correct internal address: it is jupyterhub-proxy-public:8000 — there is no address named just jupyterhub. The JUPYTER_URL configuration variable and the default used by the Copilot both point to the public proxy.
How to enable it in production
1. Individual environment image
The jupyter-datta image is built from docker/jupyter-datta/ (base quay.io/jupyter/minimal-notebook, plus the Neo4j, OpenSearch, Trino, platform cache and MinIO drivers, Spark Connect and the DATTA theme). It is identified by the last commit that changed that folder (7-character short SHA), decoupled from the Java release SHA: a release that does not touch the Notebook neither rebuilds the image nor swaps it under environments already open, and git show <tag> shows exactly what is running.
You do not compute or pin that tag by hand. The installer and the release script derive the tag, build and publish the image only when it is not yet in the registry (the build takes ~10 min and 3 GB) and hand the tag to the chart (jupyter.singleuser.image.tag) on every deployment. To build or rebuild outside a release:
./scripts/datta-build-jupyter.sh # tag derived from git
./scripts/datta-build-jupyter.sh --force # rebuild even if it existsCalled from outside the server with the on-prem registry, the script builds on the server, from the commit published on GitHub — the registry only accepts connections from inside the cluster, and the commit must be published.
The image goes through a vulnerability scan before it is published; a HIGH/CRITICAL finding with a fix available blocks the push. Never use a floating latest.
The notebook's Spark is client only: the image ships the Spark Connect client 4.2.0, without the bundled Java distribution. Two practical consequences:
- the version must match the Spark Connect server (
k8s/jupyter/spark-connect.yml) — client and server speak the same protocol within a major line, so moving one without the other would break thesc://connection; - there is no local Spark inside the notebook: a session created with
master("local")no longer exists. The path isdatta_clients.spark, which connects to the server. This removes 2 GB of Java libraries that shipped with critical flaws impossible to fix on their own — they travel inside the Spark package, and it is the package that is gone.
With jupyter.prepull.enabled: true (the installation default), every machine in the cluster downloads the image right after deployment. Without it, the first Notebook opening on each machine pays the whole download inside the environment start timeout — the screen sits in "Starting…" for minutes or fails.
2. Installation configuration
values-onprem-local.yaml ships jupyter.enabled: true. The credentials (jupyter.serviceToken, the MinIO keys and the remaining secrets) come from the .env file — or from the current release, as a fallback — and are injected by the release script.
3. Deployment
Run ./scripts/datta-release.sh (uniform release). It brings up the hub, the proxy, the internal addresses, the dedicated jupyter-user-secrets store and the /jupyter/ routing rule.
Credential security model
The Notebook environment runs arbitrary analyst code. That is why it receives credentials from a dedicated store (jupyter-user-secrets), never the platform's entire secret store (datta-secrets).
The dedicated store holds only the "Data + MinIO" scope, and every value belongs to a dedicated least-privilege user (2026-09-04 audit):
NEO4J_PASSWORD— for thejupyter_readeruser (readerrole: read-only on every database), created by the Neo4j init job. Never theneo4jsuperuser password.MINIO_ACCESS_KEY=jupyterandMINIO_SECRET_KEY— a user bound to thedatta-jupyterpolicy: read on the warehouse and documents buckets, read and write only on the staging bucket. Never the root keys.- OpenSearch: no credential while the security plugin is disabled; once it is enabled, a read-only user goes here as
OPENSEARCH_PASSWORD.
Both passwords are generated and preserved in the platform store (JUPYTER_NEO4J_PASSWORD, JUPYTER_MINIO_PASSWORD) and reach the notebook environment by reference to those two specific keys, never to the whole store. The dedicated store now holds only the MinIO user name.
Why this matters: the platform store carries JWT_SECRET, DATTA_VAULT_MASTER_KEY, GEMINI_API_KEY, DATTA_INTERNAL_TOKEN, ADMIN_DEFAULT_PASSWORD and others. An analyst could read any of them with an os.environ from inside a cell — exposing the whole store would leak the platform's secrets.
Two decisions complete the isolation:
- A narrow store injected as a block, not key by key. The environment spawner cannot merge environment variable lists: a per-key reference would wipe the variable list the spawner itself defines. Hence a narrow store, injected whole.
- No infrastructure token. The individual environment starts without automatically mounting the cluster's service-account token — from inside the notebook there is no infrastructure administration credential.
Copilot creates notebooks (optional feature)
The chat Copilot can create ready-made .dattanb notebooks in the user's environment, using Jupyter's Contents API. This requires a JupyterHub service token, registered in the hub with the access:servers scope — a DATTA token is not a valid JupyterHub token.
- The token comes from
JUPYTER_SERVICE_TOKEN, generated bysetup-datta.shand persisted in.env. - Without it, the hub does not register the service and notebook creation by the Copilot is disabled with a friendly notice — everything else in the Notebook works normally.
- The release script preserves
jupyter.serviceTokenacross releases, so the enablement is not lost on every deployment.
Quick diagnosis
| Symptom | Likely cause | Action |
|---|---|---|
| "Notebook indisponível" (error state, with the HTTP code when available) | the hub did not answer — it may be restarting or have jupyter.enabled=false; from the browser the two causes are indistinguishable | Wait a few seconds and reload the page (F5); if it persists, confirm jupyter.enabled: true in the installation configuration and check the hub's health from the platform console |
| "Checking…" that never ends | Already fixed: the screen had exits without a state (missing session, profile without access, cookie refused, silent hub) and no timeouts | The screen is a state machine: every check has a timeout and every exit paints the bar (unavailable, no session, no permission, stopped, starting with a percentage, active, error with "Try again"); force an asset reload in the browser |
| Empty iframe / "refused to connect" | frame-ancestors policy from the hub or from the individual environment | Already handled: the hub and the spawner arguments set frame-ancestors 'self' |
Permission error on /home/jovyan | The home directory volume is created owned by root | Already handled: an initialization step adjusts ownership to the notebook user, and it ships with the installation |
| Copilot does not create notebooks | JUPYTER_SERVICE_TOKEN missing | Set it in .env and redeploy — optional feature, the Notebook keeps working without it |
| Notebook cannot connect to Neo4j/OpenSearch/MinIO | Credential missing from the dedicated store | Check that jupyter-user-secrets holds the four data keys |
| "Starting…" for minutes on the first opening | The image (~3 GB) was not on the machine yet | Enable jupyter.prepull.enabled (installation default) — warming happens right after deployment, not on the first click |
| Environment does not start and the hub logs image not found | The image tag handed to the chart does not exist in the registry | Run ./scripts/datta-build-jupyter.sh (builds and publishes the git-derived tag) and redeploy |
To tell at which gate access stops (hub, cookie issuance, cookie accepted by the hub, environment creation), follow the Notebook — E0 diagnosis runbook, which lists the four calls in order.
Learn more
- Notebook Guide — analyst-facing notebook usage. creation.
- Roles and permissions — where the
ANALISTAandADMINroles come from.