---
title: "Vendor a module by hand"
description: "Bootstrap a module dependency with no repository to fetch from by copying its source tree into aontu_meta/vendor/ and letting aontu sync pin what it means."
source: "https://aontu.dev/how-to/vendor-by-hand/"
---

# Vendor a module by hand

Bootstrap a module dependency with no repository to fetch from by copying its source tree into aontu\_meta/vendor/ and letting aontu sync pin what it means.

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

`aontu sync` fetches a package from a repository, and a package that was never published to one cannot be fetched. The content-addressed user cache cannot be searched until a lockfile pins a hash, either, so the cold start for a package that lives only in a colleague’s checkout is hand-vendoring: put its source tree into `aontu_meta/vendor/` yourself, then let `sync` pin it. `cp -r` is the distribution protocol.

Start on the consumer side. The project declares the dependency in its `pkg.aon`:

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

and the entry file `main.aon` imports it:

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

With nothing received yet, `aontu pkg tidy`, the resolve step that fetches nothing, refuses and names the module:

```sh
$ aontu pkg tidy
verdict: missing
corp.example/schemas/service: not fetched (run: aontu sync)
$ echo $?
1
```

No lockfile is written; a partial lock would claim a closure that was never resolved. So do the fetch’s job by hand. The layout is `aontu_meta/vendor/<package-path>/` under your project, beside its `pkg.aon`: each `/`\-segment of the package path becomes a directory, and an uppercase letter is written `!` followed by its lowercase:

```plaintext
project/
  pkg.aon
  main.aon
  aontu_meta/
    vendor/
      corp.example/
        schemas/
          service/
            pkg.aon
            service.aon
```

The directory holds the package’s own source tree. Its `aontu_meta/vendor/corp.example/schemas/service/pkg.aon`:

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

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

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

Now `sync`, from the project root. It finds the hand-made tree at the version the project asked for, evaluates the module standalone, locks its canon-hash and the digest of its archive, and verifies both; evaluation then resolves the import:

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

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

The pin is what the hand-vendoring was for. Every later evaluation re-derives the vendored module’s canon-hash and compares it to the locked one, so a change to the module’s evaluated meaning is refused rather than silently used. Flip the vendored default in `aontu_meta/vendor/corp.example/schemas/service/service.aon`:

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

```sh
$ aontu main.aon
module integrity: corp.example/schemas/service expected aon1-oQs6Ng6XxP2FHQGTYescREGDrDPfLLW1Liq4OS8Gs2E got aon1-Bd4OQlOyzyJcXZvYbVcV7NZbMJGGxQH6GtNctkC26VA
$ echo $?
1
```

Both hashes are named: the meaning that was reviewed and the meaning the store now holds. Be precise about what that pin protects. It is a semantic pin, taken over the canonical form of the module’s entry document and its include closure; comments, whitespace, refactored spellings that canon to the same value, `pkg.aon` metadata, and files the entry never includes all keep the hash, deliberately. The bytes are the `archive` pin’s job, and the tooling checks that one first.

## In CI, verify: do not tidy

`tidy` rewrites the lockfile from whatever the store currently holds, so a job that tidies before evaluating makes the lock agree with a tampered store and then passes. `aontu pkg verify` asks the question without answering it by editing: against the still-tampered store, the bytes are checked before the meaning, so it is the archive pin that speaks:

```sh
$ aontu pkg verify
verdict: mismatch
corp.example/schemas/service: pinned archive sha256:db1c3797fbf11badbc4fb7c87c6a4c34fafe602735fb63dbdccc7de951f3d5e8 but the store holds sha256:d5e16bacd6172fd3f585fb8ec75d720c5653f277f301792bf0470edf19e2e6f2
$ echo $?
1
```

It recomputes every pin, compares against the committed lockfile, writes nothing, and exits `1` on any disagreement. Run it beside your tests ([validate in CI](https://aontu.dev/how-to/validate-in-ci) is the surrounding job), or run `aontu sync --frozen`, which refuses the same way and fetches what a locked version needs; run `sync` without the flag only when you intend to move a pin, and review its diff like code. Nothing to check is not a pass, either. Take a project that declares the dependency but never committed a lockfile: only its `pkg.aon`:

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

```sh
$ aontu pkg verify
verdict: unlocked
corp.example/schemas/service: not in the lockfile (run: aontu sync)
$ echo $?
1
```

An uncovered project is refused rather than verified over an empty set, and the line names the repair.

## A package with its own dependencies vendors flat

A vendored package carries its own `pkg.aon` and may declare its own `dep`. Its imports resolve from its own directory and from every enclosing project root, so its dependency goes in the same `aontu_meta/vendor/` tree, beside it: never nested inside it:

```plaintext
project/
  pkg.aon
  aontu_meta/
    vendor/
      corp.example/
        schemas/
          service/         # imports common
            pkg.aon
            service.aon
          common/          # flat beside it, not nested inside it
            pkg.aon
            common.aon
```

Declaring only the top of the closure is enough, because `sync` walks the rest. A consumer `pkg.aon`:

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

its entry `main.aon`:

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

a hand-vendored `aontu_meta/vendor/corp.example/schemas/service/pkg.aon` that declares a dependency of its own:

```aontu
pkg: { path:"corp.example/schemas/service" version:"1.0.0" main:"service.aon" }
dep: "corp.example/schemas/common": v: "1.0.0"
```

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

```aontu
@"corp.example/schemas/common"
name: string
port: *8080|integer
```

With `common` not yet vendored, `tidy` refuses the whole closure:

```sh
$ aontu pkg tidy
verdict: error
corp.example/schemas/service: does not evaluate on its own; nothing to pin
corp.example/schemas/common: not fetched (run: aontu sync)
$ echo $?
4
```

A module that does not evaluate on its own is refused rather than pinned, because every module that fails to evaluate hashes to the same string: a lockfile written from one would look like a pin and mean nothing. (`aontu hash` refuses the same file with the same wording.) Vendor the dependency flat beside its dependant: `aontu_meta/vendor/corp.example/schemas/common/pkg.aon`:

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

with its entry, a shared naming vocabulary, as `aontu_meta/vendor/corp.example/schemas/common/common.aon`:

```aontu
name: string & re("^[a-z][a-z0-9-]*$")
```

and the closure resolves, both packages pinned:

```sh
$ aontu sync
verdict: ok
corp.example/schemas/common 1.0.0 aon1-btDT9RfDGjP4uvd5osF3R3mRW5aIeDz49_JbJpVLDwU
corp.example/schemas/service 1.0.0 aon1-GublSGsGCwYBgyQBAZSk9imd7xfbeCYKY6qbud8okdc

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

One caution to carry away: the first copy is trusted axiomatically. There is no signed manifest to compare a hand-vendored tree against, so review what you vendor as if it were your own code; every copy after that is held to the first by the pins. A package that has been published carries its publisher’s proof instead: that is [publish a package](https://aontu.dev/how-to/publish-a-package). 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), and the live version of all of this (tamper, refactor-stable hashes, trust confinement, the publish gate) is [use-cases/11-shared-modules](https://aontu.dev/use-cases/11-shared-modules).
