<!-- Generated by crates/swarmops-cli/src/reference.rs from the code: do not edit. Change the code, then run SWARMOPS_REGEN_REFERENCE=1 with the regen_reference test. -->

# swarmops blob put

Store FILE's content as a blob, outside any commit, and print its id, which a change names it by: {"blob": ID}. A change list holds at most 64 MiB and a commit's contents 1 GiB together; a blob named by id counts toward neither.

```yaml
title: "swarmops blob put"
description: "Store FILE's content as a blob, outside any commit, and print its id, which a change names it by: {\"blob\": ID}. A change list holds at most 64 MiB and a commit's contents 1 GiB together; a blob named by id counts toward neither."
group: "write"
since: "0.1.0"
mutates: true
identity_required: true
mcp_tool: "blob_put"
envelope_op: "blob_put"
exit_codes: [0]
errors: []
syntax: "swarmops blob put [OPTIONS] <FILE>"
```

## Syntax

```text
swarmops blob put [OPTIONS] <FILE>
```

## Required arguments

### `FILE`

Syntax: `<FILE>`.

The file whose content to store, or - for stdin.

## Optional arguments

The command also takes the [global options](../cli.md#global-options).

## Examples

### CLI

```sh
swarmops --repo [tmp]/store --as agent/worker-17 blob put [tmp]/payload.txt
```

```json
{
  "ok": true,
  "op": "blob_put",
  "api": 1,
  "result": {
    "blob": "[blob]",
    "size": 12,
    "written": true
  },
  "meta": {
    "repo": "[tmp]/store",
    "elapsed_ms": "[elapsed_ms]",
    "resolved": {},
    "auth": "trust"
  }
}
```

Exit code 0. Recorded by the CLI tests. A name in square brackets, such as `[tmp]` or `[c1]`, stands for what differs from run to run: the test's temporary directory, an id, a time.

### MCP

No recorded example yet.

## Details

`blob put` stores one file's bytes in the store as a blob, outside any commit, and prints the
blob's id. A later change names it by that id, `{"content": {"blob": "<id>"}}`, and the commit
then neither sends nor reads those bytes again. Its MCP tool is `blob_put`, which takes the
content inline, in base64 or as a file on the server's host.

### Why it exists

A change list is at most 64 MiB of text, and a commit's contents at most 1 GiB together. A blob
named by id counts toward neither: only one blob's own limit applies, 512 MiB. So large or many
files go in one `blob put` each, then one commit names them all. Over MCP, where every request
line is at most 64 MiB, it is also the way to send a file once and refer to it in several
commits.

### Behaviour

- FILE is read whole, by the command, on this host; `-` reads stdin to its end.
- The id is the content's hash: the same bytes always give the same id, whoever puts them. A put
  of bytes the store holds already writes nothing and says `written: false`; so does a put that
  a concurrent writer of the same bytes beat. `size` is the content's length in bytes.
- The blob is durable when the command returns.
- It is a mutation, so it needs an identity (`--as` or `SWARMOPS_AGENT`), but it records it
  nowhere: a blob has no author, no time and no metadata. The commit that names it records who
  wrote the file.
- It runs outside any transaction: it writes the object alone, takes no lock on the store's
  metadata and writes no journal frame, so [`op log`](../commands/op-log.md) does not list it
  and there is nothing to undo. Until a commit names the blob, nothing in the metadata does.

### Patterns

```sh
BASE=$(swarmops resolve main | jq -r .result.id)
B=$(swarmops blob put big.bin | jq -r .result.blob)
printf '[{"path": "assets/big.bin", "op": "put", "content": {"blob": "%s"}}]' "$B" > change.json
swarmops commit -m "add the asset" --changes change.json --parent "$BASE"
```

- With many files, put them first, then commit one change list that names every id. The commit's
  `blobs_written` is 0 for each of them.
- Before putting a large file again, [`blob has`](../commands/blob-has.md) tells whether the
  store holds it already.
- A blob nothing names stays in the store: nothing removes it, as nothing is removed yet
  ([Where a store may live](../../operate/stores.md#where-a-store-may-live)).

### Failures

| Error | Exit | When |
| --- | --- | --- |
| `E_IDENTITY_REQUIRED` | 5 | No identity. Checked before the file is read. |
| `E_INVALID_ARG` | 2 | FILE cannot be read (`details.file` names it). |
| `E_TOO_LARGE` | 1 | The content is over 512 MiB (`details.scope` `put`), measured by the file's length before it is read. |
| `E_IO` | 1 | The object store could not be written; `details.fs_op` names the file operation that failed. |

## Return information

`result` is a `BlobPutResp` (`swarmops schema BlobPutResp` prints its JSON Schema).

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `blob` | `string` | yes | The blob's id, which a change names it by: `{"blob": id}`. |
| `size` | `integer` | yes | The content's size in bytes. |
| `written` | `boolean` | yes | Whether this call placed the blob in the object store; false when the store held it already, or a concurrent writer placed it first. |

Envelope `op`: `blob_put`.

Exit codes:

- `0`: Success.

## Errors

No golden step shows this command failing yet.

Any command that opens a store may also fail with a store error, such as [`E_NOT_A_REPO`](../errors.md#e_not_a_repo), [`E_IO`](../errors.md#e_io) or [`E_STORE_CORRUPT`](../errors.md#e_store_corrupt); a command line the grammar refuses is [`E_INVALID_ARG`](../errors.md#e_invalid_arg) (exit 2).

## See also

- [`swarmops commit`](commit.md)
- [`swarmops land`](land.md)
- [`swarmops blob has`](blob-has.md)
- [CLI reference](../cli.md)
- [Error codes](../errors.md)
