---
title: "Artifact"
description: "The stored file a successful run produced. One run, one artifact."
url: "https://saved.sh/docs/concepts/artifacts"
---

An **artifact** is the stored file a run produced. It exists only once archived: a failed run
leaves a failed run and no artifact.

**One run produces exactly one artifact.** Sources that are naturally many files, `s3`,
`folder`, are streamed into a single archive, which keeps
`keep_last` counting runs rather than files and keeps one restore one operation.

## What it records [#what-it-records]

| Field                     | Meaning                                                  |
| ------------------------- | -------------------------------------------------------- |
| `filename`                | Derived from the source. A convenience, not a contract   |
| `size_bytes`              | The size of what is **stored**                           |
| `original_bytes`          | The size of what was **uploaded**, before our transforms |
| `checksum`                | SHA-256 of the file **as uploaded**                      |
| `compressed`, `encrypted` | What was actually done to it                             |
| `key_id`                  | Fingerprint of the public key it was encrypted to        |
| `lock`                    | Whether, and until when, it cannot be deleted            |
| `locations`               | One row per copy                                         |

<Callout type="warn">
  **`checksum` attests to what you gave us, not to what we stored.** For a local backup those
  are the same object, so it matches the downloaded file directly. For cloud and manual backups
  where we applied compression or encryption, it matches only after you decrypt.
  `original_bytes` differing from `size_bytes` is the signal.
</Callout>

## An artifact has locations, not a location [#an-artifact-has-locations-not-a-location]

A run delivering to our storage and two of your buckets produces **one artifact with three
locations**. Each says where a copy went and whether it got there.

| Location kind | Where                   |
| ------------- | ----------------------- |
| `permanent`   | Our archive             |
| `destination` | One of your own buckets |

A copy that failed is visible as a failed location rather than being rounded away. That is how
a partially delivered run stays honest: the artifact exists, the run failed, and the locations
say precisely what you have.

## States [#states]

```
(none) ──finalize──► archived ──retention sweep──► expired
                              ──you delete──────► deleted
```

An artifact is written by the pipeline and never by an API call. There is no endpoint that
creates one.

## It is an ordinary file [#it-is-an-ordinary-file]

There is no saved.sh container format. An artifact is a dump wrapped in at most two standard
layers:

```
[ PGP message  [ compressed  [ the dump ] ] ]
```

Both wrappers are optional and standard. `gpg --decrypt` opens one, and so does anything else
that speaks OpenPGP.

<Callout>
  This is deliberate, and it costs us something. A container format would let us record the
  source type and layer order in a header, and would be more convenient eleven months a year.
  In the twelfth, when the thing that went wrong is us, you would need a working saved.sh to
  read your own backup. See [artifact format](/docs/recover/artifact-format).
</Callout>

## Deleting [#deleting]

| Refusal       | Meaning                                                                  |
| ------------- | ------------------------------------------------------------------------ |
| Locked        | Inside its protection window. Nothing removes it early, including us     |
| Last artifact | The only one left. A backup that has run keeps something to restore from |

Deleting removes **our** copy. Copies in your own buckets are left where they are, because we
hold write credentials rather than a mandate to destroy data in your storage.

Ordinary expiry is [retention](/docs/concepts/retention), applied by a daily sweep. Deleting by
hand is for one artifact you want gone now.

## Downloading [#downloading]

Downloads use a presigned URL straight from object storage. The bytes do not pass through our
API.

Two things follow:

* **We do not observe the transfer.** The audit trail records that a URL was *issued*, which is
  the last moment we can see.
* **A `skip_permanent` backup cannot be downloaded from us**, because the bytes only ever went
  to your bucket. We tell you where instead.

## Next [#next]

<Cards>
  <Card href="/docs/concepts/retention" title="Retention" description="How long an artifact lives." />

  <Card href="/docs/recover" title="Recover" description="Turning one back into data." />

  <Card href="/docs/backups/delivery" title="Delivery" description="Why an artifact has several locations." />
</Cards>
