Packages
Also known as: dependencies dependency package library add install import third party kavach add kavach update kavach.lock lockfile git path registry version unsafe crate module reuse kavach_modules node_modules install scripts supply chain security token https ssh
● Intermediate4 minBefore this: ModulesSpec 10.12
A package is a library another project uses: name it in [dependencies], from a folder or a git repository, and use it like any module.
[package]
name = "shop"
version = "0.1.0"
edition = "2026"
[dependencies]
geometry = { path = "geometry" }use geometry
fn main() {
print(geometry.area(2.0, 3.5))
}/// The area of a `w` by `h` rectangle.
pub fn area(w: Float, h: Float) -> Float {
w * h
}The program ran and printed:
kavach run
7.0- The name in
[dependencies]is the module name:use geometryis the package'ssrc/lib.kav, anduse geometry.shapeswould be itssrc/shapes.kav. - Inside the package's own project, its
[package] namenames its modules the same way, soshapes.kavreaches the root's types withuse geometry.{Shape}whichever project is built.libnames no module. - Only its
pubitems can be used, as with any module. - From a git repository:
money = { git = "https://…", tag = "v1.0.0" }, orbranchorrevinstead oftag.kavach add geometry --path ../geometryandkavach add money --git <url>write these lines for you. - A fetched repository lands in
kavach_modules/money/in your project, as npm keepsnode_modules/. Never commit that folder —kavach newputs it in.gitignore. A path dependency is read where it is, not copied. kavach.lockrecords the exact commit of each git dependency. Commit it: every build then uses the same code, and the lock is enough to fetchkavach_modules/again.kavach updatefetches again and rewrites it.- A package whose code uses
unsafeis refused until your entry allows it:unsafe = truein its line. - Warnings in a dependency are its authors' to fix, so they are not shown. Errors are.
There is no package registry yet, so a version number such as stripe = "1.2" is an error that says so. The registry, with versions and signing, is an open question in the spec.
Code from outside is handled with care
Fetching a dependency only copies its files: none of its code runs, because Kavach packages have no install scripts — where npm runs a package's install scripts on your machine. And what a line may name is checked before git is asked:
money = { git = "https://github.com/example/money" } # fetched
money = { git = "http://github.com/example/money" } # error: unencrypted
money = { git = "https://me:token@github.com/example/money" } # error: the token would be committed- A git source must be
https://,ssh://,user@host:pathor a folder.http://,git://and other transports are refused: they are unencrypted or run commands. - A URL holding a password or token is refused, because
kavach.tomlandkavach.lockare committed. git finds the credentials for a private repository itself, from a credential helper or an SSH key. - A fetched package can't name a
pathoutside its own folder, and a name Windows keeps for a device (con,nul,com1…) is refused, so every project builds on every system.
Each package gets only the capabilities you grant
A dependency may use only the capabilities its entry lists in can. An entry without can grants none, so the package must be pure: it can't read files, reach the network or print. Here greeter prints, and its entry grants nothing:
/// Prints a greeting: it uses the terminal.
pub fn greet(name: String) uses Console {
print("hello, {name}")
}[package]
name = "shop"
version = "0.1.0"
edition = "2026"
[dependencies]
greeter = { path = "greeter" }The compiler refused the program:
error: `greet` in the package `greeter` uses Console, which the package isn't granted: add "Console" to `can` in its entry in kavach.toml, and in the entry of each package it comes through, if you trust them (spec 10.12)kavach add reads what a package uses, says so, and writes it into can, so the person adding it sees what it may do:
added `greeter`: it uses Console, granted in its `can` in kavach.toml
hello, Asha
[dependencies]
money = { git = "https://github.com/example/money", tag = "v1.0.0", can = ["Net"] }
geometry = { path = "../geometry" } # pure: no capabilities- The error names the function and the capability, so you can decide whether to trust the package with it.
- A dependency's own dependencies get at most what it was granted: a package granted only
Consolecan't passNeton to another. - An update that starts using a new capability fails to build until you grant it, so it can't quietly start reaching the network.
can may grant part of a capability, as uses names it (narrower file access). With can = ["Fs.read"], notes may read files but not write them:
use std.fs
/// Reads a note: it only reads files.
pub fn read(name: String) -> String ! FsError uses Fs.read {
try fs.read_text("notes/{name}.txt")
}
/// Saves a note: it writes files.
pub fn save(name: String, text: String) ! FsError uses Fs.write {
try fs.write_text("notes/{name}.txt", text)
}[package]
name = "shop"
version = "0.1.0"
edition = "2026"
[dependencies]
notes = { path = "notes", can = ["Fs.read"] }The compiler refused the program:
error: `save` in the package `notes` uses Fs.write, which the package isn't granted: add "Fs.write" to `can` in its entry in kavach.toml, and in the entry of each package it comes through, if you trust them (spec 10.12)