# The asset library

The asset library is where models you generate, import or reuse are kept, so the next project can
find them again instead of making them twice. It has two parts:

- **Your cache**: the assets on this machine. It always works and needs no set-up.
- **A shared library** (optional): assets several people or machines publish to and search
  together. It is a folder, a pair of cloud buckets, or the hosted services.

Nothing needs to be set up to use the library. A machine without a shared library is
**local-only**: search, generate and import work on your cache, and nothing is reported as an
error. Only the verbs that need a shared library (fetch, publish) are refused.

The library is driven by the asset package's command line, one verb at a time, with JSON on stdin
and one JSON answer on stdout. Agents use most of them through the `library.*` tools; `status`, `retire` and `info` are CLI-only.

```
node --experimental-strip-types --no-warnings packages/assets/bin/library.ts info < /dev/null
echo '{"text": "wooden crate"}' | node --experimental-strip-types --no-warnings packages/assets/bin/library.ts search
```

## Which library am I on?

`info` answers that in its `shared` part. Paths are shown; what is inside the files never is.

```json
"shared": {
  "backend": "folder",
  "configured": false,
  "ready": true,
  "mode": "local-only",
  "status": "local-only: no shared library configured (set ARGUS_LIBRARY_DIR for a shared folder); search and generate use this machine's cache"
}
```

| `mode` | What this machine can do |
|---|---|
| `local-only` | Search, generate and import on your cache. Publishing and fetching shared assets are refused. |
| `shared-read` | Also search and fetch shared assets. Every read is checked against the library's key. |
| `shared-write` | Also publish, retire and rate. |

`ready: false` means a shared library is named but cannot be opened (a file is missing or open to
other users, or a variable is misspelt). `problem` says what is wrong and names the file or the
variable to fix. Until it is fixed the machine works as `local-only`.

`info` makes no network request. It checks the files only, so `ready: true` does not promise the
library is reachable right now.

## Try it offline: the demo library

The repository includes a small shared library in `assets/library-demo/` (in a source checkout; installed builds do
not carry it): original assets made by the modelling guide's example scripts, CC0. Point the library at it to try shared search and fetch with no account, no key and no
network:

```
export ARGUS_LIBRARY_DIR="$PWD/assets/library-demo"
echo '{"text": "crate", "scope": "shared"}' | node --experimental-strip-types --no-warnings packages/assets/bin/library.ts search
echo '{"id": "<an id from the search>"}' | node --experimental-strip-types --no-warnings packages/assets/bin/library.ts fetch
```

`info` then says `mode: shared-read`. The demo holds only the library's public key, so it is read-only: a fetch
copies the asset into your cache and writes nothing into the demo folder, and publish, retire, rate and report
are refused (`share-not-supported`). To make your own shared folder, point `ARGUS_LIBRARY_DIR` at an empty folder instead:
your first publish creates its key.

The demo is rebuilt from the example scripts with `packages/assets/scripts/build-library-demo.ts` (it needs
Blender and the asset gate, and calls nothing paid). The repository keeps only the crate, about 1 MB, to stay small.
Built in full, the script makes three assets: the crate, a cart and a tree. Each build makes a new library key, so a rebuild replaces the whole folder.

```
cd packages/assets
node --experimental-strip-types --no-warnings scripts/build-library-demo.ts --out ../../assets/library-demo --only crate
```

## Setting up a shared library

The shared library is chosen with `ARGUS_LIBRARY`:

| `ARGUS_LIBRARY` | Where the library is | What it needs |
|---|---|---|
| unset or `folder` | a folder | `ARGUS_LIBRARY_DIR`: the folder. Unset means no shared library (local-only). |
| `r2` | two S3-compatible cloud buckets, internal and public | `~/.config/argus/r2-library.json` (account, access key, the two bucket names). To write, also `~/.config/argus/library-signing.json`. To only read, the library's public key in the config's `libraryPublicKey` or in `ARGUS_LIBRARY_PUBLIC_KEY`. |
| `services` | the hosted services | `ARGUS_SERVICES_URL` and `ARGUS_LIBRARY_PUBLIC_KEY`, then sign in. |

The two `r2` files hold secrets. Keep them mode 600 in a folder only you can open; a file other
users can read is refused, not read. `ARGUS_R2_CONFIG` and `ARGUS_LIBRARY_SIGNING_KEY` point at
other paths.

Any other `ARGUS_LIBRARY` value is refused and never guessed: a misspelt name never sends your
assets to a folder you did not mean.

## Searching

`search` looks in your cache and the shared library together, ranks the results on this machine,
and says why each one matched. Your query never leaves the machine: the shared index is downloaded,
checked against the library's key, and searched locally.

```json
{ "text": "barrel", "class": "prop", "facets": { "maxTriangles": 5000 }, "limit": 10 }
```

`scope` picks where to look:

- `all` (the default): your cache, and the shared library when there is one.
- `mine`: your cache only.
- `shared`: the shared library only. It is refused (`share-no-library`) when there is none.

With `scope: "all"`, a shared library that is not set up, cannot be opened or cannot be reached
does not stop the search. You get the hits from your cache and a `note` saying the shared library
was not searched, and why:

```json
{ "ok": true, "hits": [ ... ], "note": "the shared library is unavailable (...); searched your cache only" }
```

A shared library that answers with something that does not verify (a bad signature, an index
older than one already seen) is different: the search is refused with `share-bad-signature` or
`share-rollback`, never quietly narrowed to your cache. Do not use that library until it is
fixed.

## Fetching, publishing and the rest

These need a shared library and are refused with `share-no-library` on a local-only machine:

- `fetch {"id"}`: download a shared asset into your cache (its files are checked against their
  hashes).
- `publish {"id", "handle", "attest": true}`: share an asset from your cache. Only assets whose
  origin the sharing policy allows can be published, and you sign a rights attestation.
- `status {"id"}`: where a publish is (the services backend reviews uploads before they are listed).
- `rate {"id", "by", "stars", "reason"}` and `report {"id", "reason"}`: star a shared asset (1 to 5),
  or report a problem with it. An agent's rating needs a `reason` of 10 to 200 characters; a person
  passes `"kind": "person"`.

Writes never fall back to your cache: a publish that cannot reach the library fails and says why.

Two verbs work on a local-only machine too. `retire {"id", "reason", "by"}` marks an asset in your
cache as not to be used, and also withdraws it from the shared library when there is one.
`rate {"id", "score", "by"}` (0 to 10) is your own review of an asset in your cache.

## When something is refused

Every refusal is `{"ok": false, "error": {"code", "message", "next"}}`. `next` says what to do. The table lists the common codes; the full list is in `packages/assets/src/share/errors.ts`.

| Code | Meaning | What to do |
|---|---|---|
| `share-no-library` | No shared library is set up here, or it cannot be opened. | Run `info` and fix what `shared.problem` names, or search with the default scope. |
| `share-timeout` | The shared library did not answer, or could not be reached at all. | Check the network and try again. Raise `ARGUS_LIBRARY_TIMEOUT_MS` on a slow link. |
| `share-bad-signature` | Something read from the library did not verify. | Fetch again. If it persists, check which library key you trust. |
| `share-not-supported` | The verb is not available on this backend. | `services` cannot retire, seed or take down; those run on the company's `r2` machines. |
| `share-signed-out` | The services backend needs you signed in. | Sign in, then try again. |

