> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nebyoulous.space/llms.txt
> Use this file to discover all available pages before exploring further.

# Sync frames

> Upload a folder into an integration set, or mirror a card to a local folder. Both skip duplicates and resume after an interruption.

`nebyoulous sync` copies files from a local folder to one of two places:

* **Cloud:** an integration set, with `--to-cloud <id-or-title>` or
  `--create "<title>"`.
* **Local:** another folder, with `--to-local <path>`. This runs offline.

The source is never modified, moved, or deleted.

## Upload to an integration set

```bash theme={null}
# See the plan first. Nothing is uploaded.
nebyoulous sync --from ./M31 --to-cloud "Andromeda 2026-05-24" --dry-run

# Upload only FITS lights.
nebyoulous sync --from ./M31 --to-cloud "Andromeda 2026-05-24" \
  --include "*.fit,*.fits" --frame-type LIGHT

# Create the set and upload in one step.
nebyoulous sync --from ./M42 --create "M42 2026-04-20" --notes "Two nights"
```

The source folder's name becomes the top folder in the set, so
`--from ./M31` puts files under `M31/` and keeps the subfolders below it.

Each run follows the same steps:

**1. Plan**

Walk the source, apply `--include`, `--exclude`, `--frame-type`, and
`--max-size`, and skip files already in the set or the local manifest.

**2. Upload**

Ask the API for presigned URLs in batches (`--batch-size`, default 100,
server cap 500). Upload each file straight to storage, `--concurrency` at a
time (default 4).

**3. Complete**

Mark each batch complete with the API and record the finished files in
the manifest.

## Mirror to a local folder

`--to-local` copies files into another folder. Each copy goes to a temp file
first and is then renamed into place. It needs no sign-in and no network,
which makes it handy for backing up a card in the field:

```bash theme={null}
nebyoulous sync --from /Volumes/NIKON_Z6 --to-local ~/raw/M31-2026-05-24 \
  --include "*.fit,*.fits" --frame-type LIGHT
```

Local copies land straight under the destination. The source folder's name
isn't added.

## What gets skipped

`--dry-run` and `--json` list every skipped file with a reason:

| Reason | Why |
| - | - |
| `duplicate-server` | A file with the same path and size is already in the set, or in the local destination. |
| `duplicate-manifest` | An earlier run already finished this file. |
| `filtered-frame-type` | It doesn't match `--frame-type`. |
| `ambiguous-frame-type` | `--frame-type` is set and the type can't be told. Add `--include-ambiguous` to keep these. |
| `unsupported-content-type` | Uploads accept `.fit`, `.fits`, `.fts`, `.jpg`, `.jpeg`, `.png`, `.webp`, and `.zip`. Compressed FITS (`.fits.gz`) and camera raw files are skipped. |
| `too-large` | It's over `--max-size` (default 1 GB). |

**Frame type** comes from the FITS `IMAGETYP` header when there is one, then
from words like `light`, `dark`, `flat`, and `bias` in the file name or path.

## Resuming

A manifest records every file that finished:

* `.nebyoulous-sync.json` (cloud) or `.nebyoulous-sync-local.json` (local) in
  the source folder, when it's writable.
* Otherwise a file under `~/.nebyoulous/state`, or `NEBYOULOUS_STATE_DIR` if
  set. Camera cards mounted read-only end up here.

Run the same command again after a failure. It skips what finished and
retries the rest. Uploads also skip anything the set already has, so a fresh
machine without the manifest doesn't upload duplicates.

Every flag is listed in the [command reference](/cli/commands#sync).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.