Skip to content

Repository files navigation

This is a prototype for a bidirectional mirror between a local folder and an Are.na channel, and vice-versa. Drop a file into a marked folder and it shows up on the channel or add an Image/Attachment block on Are.na and the file appears in the folder. I have always been curious if something like it might be possible, and now with the release of v3 of the Are.na API, it seemed worth a try.

Setup

Requires Python 3.10+.

git clone https://github.com/mncmncmnc/arena-sync.git
cd arena-sync

python3 -m pip install -r requirements.txt

# Personal access token: https://are.na/settings/personal-access-tokens
cp .env.example .env
# edit .env and set ARENA_TOKEN=…

chmod +x arena_sync.py
mkdir -p ~/.local/bin
ln -sf "$(pwd)/arena_sync.py" ~/.local/bin/arena-sync

Make sure ~/.local/bin is on your PATH (add export PATH="$HOME/.local/bin:$PATH" to ~/.zshrc if needed, then open a new terminal). Check the install:

arena-sync --help

Optional: set a sync root so channels marked with ** on Are.na can be auto-adopted (any directory works)

arena-sync root ~/arena

Markers

Both sides use the super literal substring ** (two asterisks side by side) in their title or folder name:

Side How to opt in
Local folder Folder name contains ** (e.g. the channel i want to sync **)
Are.na channel Channel title contains **

All allowlisted files inside a tracked folder sync and individual filenames need no marker. Only direct children of the folder are synced (no nested subdirectories). Dotfiles, symlinks, and non-allowlisted types are skipped. Optional per-folder exclusions: .arena-sync/ignore (gitignore-esque).

Allowlisted extensions: jpg jpeg png gif webp heic heif tif tiff bmp svg avif pdf mp3 mp4 mov m4a wav.

Sync semantics

Action Effect
Add file to disk Uploaded to Are.na
Add block on Are.na (Image / Attachment) Downloaded to disk
Delete file from disk Connection removed from Are.na
Delete block on Are.na Tombstoned: file kept on disk, never re-uploaded
Rename file on disk (same content) Path updated in state, no transfer
Modify file content (same path) Old block disconnected, new content uploaded

Asymmetric deletes are intentional. Disk deletion is reversible (Trash, Time Machine, file-sync history), so it propagates destructively. Are.na deletion stays defensive, in that it stops syncing that file rather than removing it locally — a misclick in the web UI shouldn't nuke local files.

To clear a tombstone or error and re-upload: arena-sync relink <file>, then sync. To wipe everything and re-sync fresh: delete <folder>/.arena-sync/state.json and run sync again.

Link, Embed, and Text blocks on Are.na are ignored (they don't round-trip to a local file).

Each tracked folder keeps a sidecar at <folder>/.arena-sync/ (channel.json, state.json, optional ignore). Channels are bound by numeric id. Synced channels get an INFO line linking to this repo: Synced with Are.na Sync.

Caveats

  • One machine. The sidecar lives inside the folder, so any file-sync service (Dropbox, iCloud, etc.) will sync it too. I would recommend running arena-sync on a single computer as two hosts syncing the same folder can conflict over state.json.
  • forget only unregisters. It removes the folder from ~/.config/arena-sync/folders. It does not delete the Are.na channel, the local folder, or the .arena-sync/ sidecar.

Manual sync (default)

Sync runs only when you ask. This is the intended day-to-day mode.

arena-sync sync                                          # discover marked channels (if root set) + sync all registered
arena-sync sync ~/arena/"the channel i want to sync **"  # one folder only (no discovery)
arena-sync status                                        # dry-run, including would-adopt lines

Local → Are.na (init)

arena-sync init ~/arena/"the channel i want to sync **"

Creates a private channel titled the channel i want to sync ** (INFO stamped with a link to this repo), writes .arena-sync/, registers the folder, runs an initial sync. Pass --title only if you want the Are.na title to differ from the folder name (it should still always include **).

Are.na → local (adopt / discovery)

Auto (with sync root): rename a channel title to the channel i want to sync **, then arena-sync sync. It lands under the sync root as {root}/the channel i want to sync **/, gets registered, stamped, and synced.

Custom path:

arena-sync adopt 1234567                                          # lands in <sync-root>/<title>
arena-sync adopt 1234567 ~/arena/"the channel i want to sync **"  # explicit folder
arena-sync adopt the-channel-i-want-to-sync                       # by slug

adopt requires ** in the channel title. Folder name must include ** too.

Other commands

arena-sync root                                                  # print sync root
arena-sync root ~/arena                                          # set sync root
arena-sync list
arena-sync forget ~/arena/"the channel i want to sync **"
arena-sync relink ~/arena/"the channel i want to sync **/photo.jpg"

Automatic sync (optional)

A launched timer can run arena-sync sync on a schedule. Skip this unless you want unattended automatic mirroring.

cp com.maxwell.arena-sync.plist ~/Library/LaunchAgents/
# Edit ProgramArguments if your symlink path differs
launchctl load ~/Library/LaunchAgents/com.maxwell.arena-sync.plist

Runs every 5 minutes. This has the same one-machine caveat as above — a file-sync service will also sync .arena-sync/state.json.

To stop:

launchctl unload ~/Library/LaunchAgents/com.maxwell.arena-sync.plist

About

are.na sync

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages