PhotoBlad

Getting started

From a fresh clone to your own photos in the gallery, on one computer.

PhotoBlad is still being built. Phases 1–7, non-destructive editing and storage across drives are done: the web gallery, accounts with password and passkey sign-in, backups, sharing, devices and video, editing that never touches your originals, storage locations across several drives, and self-hosting with containers, Caddy and Tailscale. This page runs PhotoBlad from source on your own computer, to try it out or to work on it, and a copy run this way is for that computer only. To run it on a server and reach it from your other devices over HTTPS, follow Self-hosting.

What you need

  • The .NET 10 SDK, version 10.0.100 or newer. The repository’s global.json pins 10.0.100 and rolls forward to newer feature bands.
  • Git, and access to the repository. It’s private for now. [TBD: how to get access while the repository is private]
  • FFmpeg, if you want video posters and playback. It’s optional: without it, videos are still backed up and listed (see below).
  • Node.js, but only if you change the web app’s styles (see below).
  • [TBD: supported operating systems. So far PhotoBlad has been developed on macOS with Apple silicon.]
  • [TBD: recommended hardware, and disk space for thumbnails, video playback copies and backups]

FFmpeg, for videos

On a Mac with Homebrew:

brew install ffmpeg

PhotoBlad looks for ffmpeg and ffprobe on the PATH, then in /opt/homebrew/bin, /usr/local/bin and /usr/bin. To use another copy, set PhotoBlad:Video:Ffmpeg and PhotoBlad:Video:Ffprobe to their full paths. Playback copies need an FFmpeg with the libx264 and AAC encoders, and Homebrew’s has both. [TBD: how to install FFmpeg on other systems]

Without FFmpeg, the owner sees a notice saying why videos can’t be processed. Install it and restart PhotoBlad, and the videos it listed meanwhile get their posters and playback copies.

1. Get the code

git clone git@github.com:dustinblad/PhotoBlad.git
cd PhotoBlad

2. Build

dotnet build PhotoBlad.sln

A clean build reports 0 warnings and 0 errors.

3. Start PhotoBlad

Run everything with Aspire. It starts the Server and the Indexer together and opens the Aspire dashboard, where you can follow their logs and health:

dotnet run --project src/PhotoBlad.AppHost --launch-profile http

The http launch profile means you don’t need a trusted HTTPS development certificate. With the Aspire CLI, run dotnet aspire run instead; run dotnet tool restore once first to install the version the repository pins.

Or start the two services yourself, each in its own terminal:

dotnet run --project src/PhotoBlad.Indexer
dotnet run --project src/PhotoBlad.Server

The Indexer scans folders, stores backups and imports, moves files between storage locations and is the only service that writes to the library. The Server hosts the web app, reads the library and passes scans, uploads and imports on to the Indexer.

Then open http://localhost:5180.

4. Create the owner account

The first time you open PhotoBlad, it asks for a setup code. The Server prints a one-time code in its log, on the line PhotoBlad has no owner yet. Open /setup and enter the setup code … (with Aspire, open the Server’s console logs in the dashboard). Enter the code, then choose your name, a username and a password of at least 12 characters. Until the owner account exists, the Server prints a new code each time it starts.

5. Scan your first folder

As the owner, choose Scan folder, enter the full path of a folder of photos on the computer running PhotoBlad, and start the scan. PhotoBlad reads JPEG, PNG, WebP and HEIC photos, and MP4, M4V, MOV, 3GP, WebM, MKV and AVI videos. They appear in the gallery as soon as indexing finishes, for everyone signed in: the folders the owner scans are the family library.

On a Mac, allow PhotoBlad to read the folder. macOS keeps programs out of the Desktop, Documents and Downloads folders, the Photos library in Pictures, and external or network drives until you allow them, and it can answer as if the folder weren’t there. If the scan says the folder “doesn’t exist or isn’t readable”, or finds nothing in a folder that has photos, open System Settings → Privacy & Security and turn on Terminal, or the app that starts PhotoBlad, under Files and Folders. Or give it Full Disk Access, the surer way for Pictures and external drives. Then start PhotoBlad again.

PhotoBlad only reads your photo folders: nothing in them is moved, renamed, re-encoded or deleted. Everything it creates, the database, the thumbnails and videos’ playback copies, goes into its data folder, ~/.photoblad by default. To use another folder, set PhotoBlad:DataDirectory, for example with the PhotoBlad__DataDirectory environment variable. A scan also skips PhotoBlad’s storage locations, however their folders are written, so a backup is never mistaken for a library photo.

6. Back up from your browser

Anyone signed in can back up photos and videos from their own computer: use the Upload button, or drag files onto the drop zone. PhotoBlad takes JPEG, PNG, WebP and HEIC photos, and MOV, MP4, M4V, 3GP, MKV, WebM and AVI videos, recognised by their contents rather than their names, up to 16 GiB each.

The browser works out each file’s SHA-256 hash first and asks PhotoBlad which files it still needs, so nothing you’ve already backed up is sent again. PhotoBlad checks every file it receives against its hash before keeping it.

Your backups are kept in a storage location (see Where your backups are kept) and are private until you choose to share them (step 8). The phone apps, which are in progress for Phase 8, will back up the same way.

7. Invite your family

In Settings → Members, add each family member and send them the invite link PhotoBlad shows. Each link works once, for up to 7 days. Members choose their own password. Only the owner scans and imports folders, and manages members and storage.

Everyone shares the family library, the folders the owner scans. Backups are different: each person’s are private to them, the owner included, until they share them.

Anyone can add a passkey in Settings → Passkeys and then sign in with Face ID, Touch ID, Windows Hello or a security key. Passkeys work at the addresses listed in PhotoBlad:Auth:PublicOrigins; in development that’s http://localhost:5180. In containers, PHOTOBLAD_PUBLIC_ORIGINS sets them: see Self-hosting.

If a member forgets their password, make them a new link in Settings → Members. If you forget the owner’s password, reset it on the server:

dotnet run --project src/PhotoBlad.Server -- users reset-password <username>

8. Share your backups, or don’t

In Settings → Sharing, turn on Share my backups with the family, and everyone signed in sees what you back up, marked “Shared by” with your name, and can download the originals. It’s off to start with, for everyone, the owner included, and turning it off hides your backups again straight away. The same page shows what your backups take up. As the owner, you see what everyone’s backups take up, as counts and sizes only, in Settings → Storage.

The switch in the gallery’s header chooses what it shows: Everything you can see, the Family library (the scanned folders and every shared backup) or My backups.

9. Keep track of your devices

Settings → Devices lists every browser and phone signed in as you, and when each was last active. Signing one out takes effect straight away: its next request finds it signed out. Sign out all other devices signs out all of them except the browser you’re using, which is worth doing after losing a phone. Changing your password signs out your other devices too.

Videos and Live Photos

With FFmpeg installed, videos get posters and play in the browser, and you can seek through them. A browser plays the original when it can. For a video that not every browser plays, PhotoBlad makes an H.264 playback copy in the background, at most 1080p, and the player shows “Preparing video…” until it’s ready. The copy is disposable. The original is never changed, and Download original always gives you the exact file.

A Live Photo appears as one tile with a LIVE badge, whether it was backed up or found in a scanned folder. Open it and press to play the motion; Download original offers the Photo or the Motion clip.

Edit photos and videos

Open a photo you may edit and press Edit. You can edit the library’s photos if you’re an owner, and your own backups whoever you are. You can:

  • crop, rotate, flip and straighten;
  • adjust light and colour;
  • add a filter.

The preview follows every slider as you drag, and holding Compare shows the photo before your changes. Save stores your edits beside the photo: the edited look is made from the original, which is never changed. Revert takes you back to it, and the Edited chip in the viewer lets you hold to see the original.

In a photo’s details you can correct its date or where it was taken, and add a caption and tags. These are kept in PhotoBlad’s database, never written into the file, and the camera’s own values are kept too. Hearts mark favourites, which are private to you, and the gallery can show just your favourites or one tag.

For a video, Edit opens the trim: keep just the part you want. Download then offers a quick copy, lossless and made in seconds, or an exact one, re-encoded and frame-accurate. Under Versions you can keep a photo you edited in another app, such as Lightroom, next to its original and compare the two. Download always offers the original too, exactly as it was stored.

Where your backups are kept

Backups, imported photos and versions are kept in storage locations: folders on the computer running PhotoBlad, apart from your photo folders and the data folder. The first is Uploads, ~/PhotoBlad/Uploads by default, which PhotoBlad makes itself on a fresh install.

To put the first location somewhere else, set PhotoBlad:Uploads:Directory before the first start, for example with the PhotoBlad__Uploads__Directory environment variable. It can’t be the data folder or inside it, and the data folder can’t be inside it. PhotoBlad reads that setting once, when it registers the first location. After that, locations are kept in the database and changed in Settings → Storage. If you ran PhotoBlad before storage locations existed, its first start registered the folders it already used: Uploads, and ~/PhotoBlad/Versions, if it held files, as a read-only location called Versions.

As the owner, add more locations, on other drives if you like. In Settings → Storage, choose Add a location, enter the full path of a folder that already exists and give the location a name. PhotoBlad never creates the folder. It refuses a folder that is, or is inside or around, its own data folder, another location or a folder you scan, and it refuses a whole disk, the home folder, a system folder, and a folder it can’t read and write in. To limit where locations can be added, list the allowed folders in PhotoBlad:Storage:AllowedRoots. A location has:

  • a mode: Active takes new files, Read-only keeps what it has and takes nothing new, and Draining takes nothing new and moves everything it holds elsewhere;
  • an order, in which the shared pool’s locations fill;
  • an optional cap, the most PhotoBlad keeps there, counting only its own files;
  • a reserve, free space PhotoBlad always leaves on the drive: 1 GiB unless you choose another, and the first location starts with the PhotoBlad:Uploads:FreeSpaceMarginBytes setting;
  • optionally, the people whose own drive it is (see Give someone their own drive).

A new file goes to the drive of the person it is for, if the person has one. Otherwise, or when that drive is full or offline, it goes to the shared pool: the Active locations that are nobody’s own drive, in order, and the first that is online and has room takes it. Room means that the drive’s free space less its reserve fits the file, and that the file stays within the cap, if there is one. If no location has room, PhotoBlad’s server says it is out of space and the browser pauses uploads: make room by freeing some or adding a location, then resume them. A file can be up to 16 GiB by default (PhotoBlad:Uploads:MaxFileBytes).

New files are filed by the month they were taken and by person, as {yyyy}/{MM}/{Person}/{name}, where Person is the person’s user name, or Family for the family library: for example 2024/07/alice/IMG_1234.HEIC. A file that doesn’t record when it was taken is filed by the date the phone gives, the modification time of an imported file, or else by when it was backed up. A version is filed by the month and person of its original. Files kept before storage locations stay where they are, filed as {user}/{yyyy}/{MM}/, and both layouts stay readable. Either way they are plain folders of photos and videos under their own names, so they make sense without PhotoBlad.

Every backup is stored byte for byte and written once. It arrives in a temporary .incoming folder inside its location, is checked against its hash, saved to disk and made read-only, and only then gets its name, one no other file has: a different IMG_0001.HEIC in the same folder becomes IMG_0001-2.HEIC. Imported photos and versions arrive the same way. After that PhotoBlad never changes or overwrites a file, and moves one only as Moves between locations describes.

A backup is always PhotoBlad’s own copy, even when the same photo is in a scanned library folder, so reorganising your photo folders never touches a backup.

Each location’s folder holds a small marker file, .photoblad-location, which PhotoBlad reads back before it writes anything there. When it can’t, for example because a drive isn’t plugged in and its mount point is an empty folder, the location is offline: nothing is written there, and no folder is created. Thumbnails already made still show, and opening or downloading a file whose only copy is there answers “Storage offline” until the drive is back. PhotoBlad looks every 30 seconds and puts a location back online by itself. If the drive came back under another folder, Find… on the location’s card points the location there, and PhotoBlad accepts the folder only when the marker in it is that location’s own.

Give someone their own drive

As the owner, you can dedicate a drive to one person: a location that keeps only that person’s files, meaning backups, imported photos and versions. In Settings → Storage, choose the drive in the Drive column of Storage by person, or, when you add or edit a location, choose Assigned people under Who stores here and tick the person. Each person has one drive, and one drive can be several people’s. Storage by person also shows how much each person has backed up, as counts and sizes only.

From then on the person’s new backups go straight to that drive, and older ones move there, one file at a time. A photo that two people both backed up is stored once, and stays put if it is already on the other person’s drive. While the drive is offline or full, new backups go to the shared pool and move back by themselves once it can take them. Choosing Shared pool again moves the person’s files back to the pool.

Import a folder

Scan folder indexes a folder where it is. Import copies a folder’s photos and videos into PhotoBlad’s storage. As the owner, choose Import in the header, next to Scan folder, and enter the full path of a folder on the computer running PhotoBlad. Then choose where the files go: the Family library, which everyone signed in sees, or one person, in which case the files are that person’s backups, seen by others only if that person shares them. Nothing is chosen for you, and the dialog says who will see the files. Choose Start import.

  • Anything PhotoBlad already has, judged by its content, is skipped and never stored twice. A photo in a scanned folder stays where it is, and gains a copy in storage.
  • The folder is left exactly as it was. PhotoBlad opens its files read-only and creates, renames and deletes nothing in it. Delete it yourself once you’ve checked the library.
  • A panel follows the import: how many files were copied, already stored, unsupported or failed, how much is copied and how fast, and how long is left. Cancel import stops it after the file being copied, and what was copied stays. When it ends, the panel lists the unsupported and failed files, and the list can be downloaded.
  • One import runs at a time. If no location has room, it waits and carries on once one has. If PhotoBlad stops meanwhile, the import starts again from the beginning next time, and skips what is stored already.
  • PhotoBlad refuses a folder that is, or is inside or around, its own data folder or a storage location, and it refuses a whole disk, the home folder and system folders.

Moves between locations

A file changes place only by a verified move, which the Indexer does in the background, one file at a time. It copies the file into the other location, checks the copy against the file’s SHA-256 hash, switches the library over to the copy, and only then removes the old file. The bytes never change. A copy that fails its check leaves the old file where it was, and a move picks up where it stopped after a crash. Moves happen when:

  • a person is given a drive, and older files move to it;
  • a drive that was offline or full is back, and the files that went to the pool meanwhile move home;
  • a location is over its cap, and its newest files move to other locations until it is under;
  • a location is set to Draining, and everything on it moves out. Once nothing is stored there, Retire… takes it off PhotoBlad’s list: its folder stays, and only the marker file and the .incoming folder are deleted.

Settings → Storage shows the moves in a strip above the locations, which opens into a panel: the file being copied with its speed and time left, how many moves are in each state, and any that failed, each with Retry and Cancel. Moves pause while a scan or an import runs, and carry on by themselves afterwards. Pause stops them until you choose Resume. Either way the file being moved is finished first, so nothing is left half moved. Files in scanned folders are never moved.

If you lose the database

The storage locations are enough to put the backups back. Each location’s folder holds its files as they were stored, its marker, and a manifest, .photoblad/manifest.jsonl, that PhotoBlad adds to as it places and moves files there. The manifest makes the recovery exact: who owns each file, co-owners and devices included, and which original each version belongs to. Without it, the folders’ names say who owns what. Edits, favourites and the accounts themselves live only in the database, so keep the data folder in your backups too.

The command below works on an existing database: one restored from an older copy of the data folder, or, if the data folder is gone, a new one, made by starting PhotoBlad once and creating the accounts again with the same usernames.

Stop the Indexer, then run:

dotnet run --project src/PhotoBlad.Indexer -- storage rebuild

It reads every storage location, in both folder layouts, and records every backup and version that the database doesn’t know, giving each to the accounts the manifest names, or else to the account its folder is named for. Files in a folder no account is named for are recorded without an owner, until that account exists again and you run the command once more. It only reads the locations, never changes a file, and is safe to run again. It ends with a report of anything it couldn’t place or tell apart.

If the database doesn’t know a location, add --root <folder> for each such location’s folder: its marker brings the location back, with its name, and a folder without a marker is refused. If you use another data folder than the default, add --PhotoBlad:DataDirectory=<path> at the end. uploads rebuild is the command’s older name, and still works.

Then start PhotoBlad again, and the Indexer indexes the backups the command found. In containers the steps are in Self-hosting.

Run the tests

dotnet test

Change the web app’s styles

The web app’s compiled CSS is checked in, so you only need Node.js to change its styles. Install the tools once:

cd src/PhotoBlad.Client
npm install

After that, every build regenerates wwwroot/css/app.css.

Remove PhotoBlad

Copy your storage locations first. Each location’s folder, ~/PhotoBlad/Uploads and any you added in Settings → Storage, holds people’s original backups, imported photos and versions, and they may be the only copies. Deleting a folder deletes them.

Delete the cloned folder and the data folder, ~/.photoblad. Your photo folders were never changed. The storage locations are plain folders of photos and videos that open without PhotoBlad, so keep them, or copy them somewhere safe before you delete them. Each also holds a marker file, .photoblad-location, and, once files are stored, a hidden .photoblad folder; neither holds a photo.

What’s next

  • Privacy & security explains what PhotoBlad does with your photos and backups, and who can see them.
  • Self-hosting runs PhotoBlad as containers with Docker or Podman, and reaches it from your other devices over HTTPS through Caddy or Tailscale. It’s done (Phase 7), and tested with Podman.
  • Phone apps for iOS and Android are next: they’re in progress for Phase 8, and not released. They’ll back up through the same API as the web app.
  • Troubleshooting. On a Mac, a scan that can’t read a folder is usually macOS keeping PhotoBlad out: allow it as Scan your first folder says. [TBD: more troubleshooting, for example the HTTPS development certificate]