docs / guide / The Cajeta Guide

Chapters — 24. Notebooks
  1. Introduction
  2. 0. Introduction
  3. 1. Installation
  4. 2. Kick the tires
  5. 3. Your first project
  6. 4. Running your application
  7. 5. Debugging
  8. 6. Keywords
  9. 7. Comments
  10. 8. Native types
  11. 9. Type kinds
  12. 10. Allocation
  13. 11. Ownership & borrowing
  14. 12. Control flow
  15. 13. Strings & formatting
  16. 14. Templates & wildcards
  17. 15. Lambdas & captures
  18. 16. Operator overloading
  19. 17. Inheritance
  20. 18. Annotations & synthesis
  21. 19. DI & aspects
  22. 20. Error handling
  23. 21. Reflection
  24. 22. Differentiation
  25. 23. Code coverage
  26. 23. Profiling
  27. 24. Notebooks
  28. 25. Writing XPU Kernels

24 — Notebooks

Cajeta runs as a Jupyter kernel. cajeta kernel speaks the v5.3 protocol over ZeroMQ, and a notebook cell is a script unit: top-level code, compiled and JIT’d into a session that accumulates. Bindings made in one cell are there in the next, Out[N] renders the cell’s value, and a traceback names the cell it came from.

Install the kernelspec once:

cajeta init --kernel

Then start Jupyter Lab as usual and pick the Cajeta kernel. The kernel adopts the project it was launched in, so run it from your notebook’s directory and that project’s dependencies are on the classpath from the first cell.

To scaffold a project shaped for this:

cajeta init notebook my-analysis

Installing a library mid-session

A notebook is an exploratory place, and needing a restart to add a dependency breaks the session you were exploring in. cajeta.session.Packages acquires a library into the running kernel:

// cell 1
import cajeta.session.Packages;
Packages.install("dev.cajeta.ml", "0.10.*");
// cell 2 — the import resolves now
import dev.cajeta.ml.Frame;
Frame f = Frame.fromCsv("sales.csv");

install returns the resolved version as a String, and prints its phases as it goes so a network fetch is never a silent stall:

  resolving dev.cajeta.ml 0.10.*
  fetching dev.cajeta.ml 0.10.4 from central
  verifying dev.cajeta.ml 0.10.4
  splicing dev.cajeta.ml 0.10.4

Acquisition and binding are separate

install puts an archive on the session’s classpath. import names packages from archives already acquired. They are the same two layers a compiled project has, and install never imports anything itself.

The practical consequence is the one rule to remember: a cell cannot import what it installs. The cell is compiled before its code runs, so the import is resolved before the install has happened. Put the import in the next cell. The diagnostic says so if you forget.

Re-running is safe

Installing a library already loaded at a satisfying version is a no-op that returns the loaded version, so running a notebook top to bottom a second time does not re-fetch anything.

Installs are additive. A session cannot unload or replace an archive it has already loaded — JIT’d code from it may be live — so moving to a version the loaded one does not satisfy needs a restart, and the error says as much.


Keeping the dependency

By default an install lasts as long as the session. The manifest remains the reproducible record, so a notebook you intend to share should record what it needs:

import cajeta.session.Packages;
Packages.installAndSave("dev.cajeta.ml", "0.10.*");

That installs exactly as install does, and also writes the dependency into the governing project’s cajeta.json. Your comments and formatting survive — it is the same editor cajeta add uses. After that the library loads at session start and the notebook needs no install cell at all.

It is a separate method rather than a flag on install so that reading the call tells you whether a file was written.


Verification

Installing code into a running session is a supply-chain surface, so an archive is checked before it is spliced.

The repository must serve release metadata signed through its root: the root ships with the toolchain, so this works with no setup against the public repository. The metadata names the archive’s sha256 and the organization that owns it. A mismatch discards the bytes and fails the install; nothing is half-installed. This is the same check cajeta build runs.

When the repository also publishes an ed25519 signature for the archive, it is verified against that organization’s key document, or, where the repository serves none, against the keys your machine trusts, never a key shipped alongside the artifact. Manage those with cajeta trust:

cajeta trust add acme-releases ./acme-releases.pem
cajeta trust list

A signature that does not match a trusted key rejects the install. So does a signed archive on a machine with no trusted keys at all: unverifiable is not the same as fine.

A local or vendored repository that signs nothing is refused until you relax that one repository in cajeta.json:

{
    "settings": {
        "repositories": [
            { "name": "vendor", "type": "filesystem", "path": "./vendor",
              "verification": "checksum" }
        ]
    }
}

The relaxation covers only that repository; the public one stays verified. The older "require-signatures": true setting is retired and has no effect.

Note what the relaxation gives up. A checksum only says these are the bytes the repository meant to serve, so whoever controls that repository controls what you install. Signed metadata says the repository’s root stands behind them, and a mirror cannot forge it.


When an install fails

Every rejection leaves the kernel serving. You lose the install and nothing else — the session keeps its bindings and the next cell runs. That is deliberate: a notebook session can represent hours of work, and a bad version constraint is not a reason to lose it.

Outside a session — cajeta run, a compiled program — Packages.install throws a recoverable PackageInstallException saying there is no live session to install into. Declare the dependency in cajeta.json instead.


See also

Source: docs/guide/24-notebooks.md · 4 min · 793 words