docs / guide / The Cajeta Guide
Chapters — 24. Notebooks
- Introduction
- 0. Introduction
- 1. Installation
- 2. Kick the tires
- 3. Your first project
- 4. Running your application
- 5. Debugging
- 6. Keywords
- 7. Comments
- 8. Native types
- 9. Type kinds
- 10. Allocation
- 11. Ownership & borrowing
- 12. Control flow
- 13. Strings & formatting
- 14. Templates & wildcards
- 15. Lambdas & captures
- 16. Operator overloading
- 17. Inheritance
- 18. Annotations & synthesis
- 19. DI & aspects
- 20. Error handling
- 21. Reflection
- 22. Differentiation
- 23. Code coverage
- 23. Profiling
- 24. Notebooks
- 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
cajeta.session.Packages— the API reference- 03 — Your first project — manifests and dependencies
- 05 — Debugging — the kernel’s place in the tooling