---
title: "Vendor a dependency closure for an offline build"
description: "Lock a dependency closure with aontu sync and commit aontu_meta/vendor/ so a build resolves every import with no network at all."
source: "https://aontu.dev/how-to/vendor-a-dependency-closure/"
---

# Vendor a dependency closure for an offline build

Lock a dependency closure with aontu sync and commit aontu\_meta/vendor/ so a build resolves every import with no network at all.

Rendered from [`docs/how-to/vendor-a-dependency-closure.md`](https://github.com/aontu-lang/aontu/blob/main/docs/how-to/vendor-a-dependency-closure.md) in the engine repository, where a correction belongs, and where the test suite executes every example on this page.

An `@"…"` import whose first segment carries a dot is a module import rather than a file path, and modules resolve from local stores only: evaluation never reaches the network. The stores are `aontu_meta/vendor/` in your project and a user cache keyed by canon-hash. `aontu sync` is the verb that fills both; a synced project evaluates with neither a cache nor a network, which is what this guide is for.

Declare the dependency in the project’s `pkg.aon`:

```aontu
pkg: { path:"corp.example/app" main:"main.aon" }
dep: "corp.example/schemas/service": v: "1.4.2"
```

and import it from the entry file, `main.aon`:

```aontu
svc: @"corp.example/schemas/service"
svc: name: "auth"
```

The package itself is an ordinary source tree with its own `pkg.aon` and entry file. Here it already sits in the vendor tree, copied by hand from the platform team’s repository, which is its own guide: [vendor a module by hand](https://aontu.dev/how-to/vendor-by-hand). Its `aontu_meta/vendor/corp.example/schemas/service/pkg.aon`:

```aontu
pkg: { path:"corp.example/schemas/service" version:"1.4.2" main:"service.aon" }
```

and its entry, `aontu_meta/vendor/corp.example/schemas/service/service.aon`:

```aontu
name: string
port: *8080|integer
```

`sync` resolves the closure, fetches nothing it already holds, writes the lockfile and verifies every pin; evaluation then needs nothing outside the project directory:

```sh
$ aontu sync
verdict: ok
corp.example/schemas/service 1.4.2 aon1-oQs6Ng6XxP2FHQGTYescREGDrDPfLLW1Liq4OS8Gs2E

$ aontu main.aon
{
  "svc": {
    "name": "auth",
    "port": 8080
  }
}
```

The default filled and the schema held, all from the vendored tree. The lockfile `sync` wrote is one canonical, diffable, JSON-parseable line:

```aon
# pkg-lock.aon (generated by `aontu sync`; do not edit)
{"lock":{"corp.example/schemas/service":{"archive":"sha256:f0e8…","canon":"aon1-oQs6Ng6XxP2FHQGTYescREGDrDPfLLW1Liq4OS8Gs2E","v":"1.4.2"}}}
```

The `canon` pin is the module’s meaning, recomputed and compared on every later evaluation ([vendor by hand](https://aontu.dev/how-to/vendor-by-hand) shows what that catches); `archive` is the digest of the tree’s canonical archive, which `aontu pkg verify` compares before it evaluates anything. A package acquired from a repository carries a third pin, `manifest`, the digest of what its publisher signed. Commit `aontu_meta/pkg-lock.aon` and `aontu_meta/vendor/` together: that pair is the offline build, the tree to bake into a container image, an air-gapped checkout, or an agent sandbox.

## In CI, freeze

`sync` is idempotent, so running it in CI is safe; what CI must not do is let it rewrite the lockfile without anyone noticing. `--frozen` resolves and fetches what a locked version needs, and refuses when the lockfile would change. Add a dependency to `pkg.aon` without syncing:

```aontu
pkg: { path:"corp.example/app" main:"main.aon" }

dep: "corp.example/schemas/service": v: "1.4.2"
dep: "corp.example/schemas/common": v: "1.0.0"
```

```sh
$ aontu sync --frozen
verdict: frozen
lockfile would change: corp.example/schemas/common: unlocked -> 1.0.0
$ echo $?
1
```

Nothing was fetched and nothing was written: the refusal came before the first request. A pin that would move says `repinned`, and a dependency that left the package file says `dropped`. Run `sync` without the flag where the lockfile is meant to change, review its diff like code, and let CI hold the result.

`aontu pkg verify` is the narrower gate: it checks every pin against the stores and reaches for nothing, which is what to run beside the tests when the project is already synced ([validate in CI](https://aontu.dev/how-to/validate-in-ci) is the surrounding job). The verbs’ full contract is [`aontu sync`](https://aontu.dev/docs/reference-api#aontu-sync) and [`aontu pkg`](https://aontu.dev/docs/reference-api#aontu-pkg); the module resolution rules are in the [language reference](https://aontu.dev/docs/reference-language#modules). Publishing the package a consumer syncs is [publish a package](https://aontu.dev/how-to/publish-a-package), and the live version (cold start through tamper, confinement, a local repository and the publish gate) is [use-cases/11-shared-modules](https://aontu.dev/use-cases/11-shared-modules).
