A native macOS desktop application for photo metadata management and face recognition — an open-source alternative to Adobe Bridge and Photo Mechanic. Built with SwiftUI and Metal for Apple Silicon.
License: GPL-3.0
3.0.0 Beta 2 is available as a public testing release. See the 3.0 feature guide, known limitations, and privacy draft for the release-candidate behavior and boundaries.
- macOS 26.0 or later
- Apple Silicon (arm64)
brew install aagedal/casks/aagedal-photo-agentOr build from source:
xcodebuild -project "Aagedal Photo Agent.xcodeproj" \
-scheme "Aagedal Photo Agent" build- Browse folders with fast 240x240 thumbnail previews
- Full-screen loupe view with keyboard navigation, prefetching, and edited-preview rendering
- Star ratings (0-5) and color labels with keyboard shortcuts
- Photo Mechanic-style cull shortcuts in full-screen (bare digits for rating/label, X to trash)
- Sort by name, date modified, date added, file size, or star rating
- Filter by star rating, color label, person shown, or missing required metadata fields
- Full-text search across filenames and IPTC metadata fields
- Folder favorites, recent folders, and drag-and-drop folder organization in the sidebar
- Import from memory cards or folders with a non-blocking progress bar
- Copy verification (SHA-256) so files are checksummed before the source is released
- Dual-destination backup — primary and backup copies written and verified in one pass
- Capture-date sorting into year-grouped date folders, with an Import Title applied automatically
- Unified import/upload activity history with sticky completion banners
- Standard: JPEG, PNG, TIFF, HEIC, HEIF, BMP, GIF, WebP, AVIF, JPEG XL
- RAW: CR2, CR3, NEF, NRW, ARW, RAF, DNG, RW2, ORF, PEF, SRW
- RAW archiving: Create unedited 16-bit JPEG XL or TIFF decodes with Linear RAW or Camera RAW processing, or lossless/lossy DNG files when the free Adobe DNG Converter is installed. Develop edits are never baked into archive pixels; the matching XMP sidecar is copied unchanged. Archives can go into each work folder’s
Archivesub-folder, a separate root that mirrors the main ingest structure, or a folder chosen for each batch. C2PA-protected RAW archives are signed with the source credential as a parent ingredient when a signing identity is configured; otherwise the app warns before creating unsigned files.
Non-destructive RAW development with real-time Metal GPU preview:
- Exposure, contrast, highlights, shadows, whites, blacks
- White balance (temperature and tint), with a click/drag-to-neutral eyedropper that samples the pre-WB source like Adobe Camera Raw
- Vibrance and saturation
- Per-color HSL — Hue / Saturation / Density adjustments matched to the vectorscope channels
- Film emulation controls for grain, halation, bloom, vignette, and edge blur
- Customizable Develop panel with per-slider visibility preferences
- Tone curves (Adobe Camera Raw compatible)
- Crop, straighten, and rotation
- Reorderable develop layer chain — a horizontal strip of cards (Global node plus local masks) that you can drag to reorder
- Local adjustments with elliptical/radial masks — each mask has independent tonal and color controls
- HDR/EDR rendering with extended dynamic range
- Before/after comparison toggle
- Undo/redo support
- Copy/paste develop settings between images (⌥V pastes including crop)
- Edits stored in XMP sidecar files for cross-tool compatibility — develop settings and masks are written in Adobe Camera Raw's
crsencoding so ACR / Bridge detect and render them
- Edit the app's descriptive IPTC/XMP field set, including headline, caption, keywords, people and organisations shown, ordered creators, creator contact, credit and rights, Subject/Scene Codes, Media Topics, structured Genre and PLUS Image Supplier values, precision-aware Date Created, locations, instructions, source, and GPS coordinates
- Local description assistance: correct grammar or improve wording in Bokmål, Nynorsk, or English, then review/edit the suggestion before applying it. The wand beside Description opens the assistant. Settings → Description Assistant offers Borealis 4B for Norwegian or Gemma 3 4B Instruct for general multilingual use. Both downloads use pinned Q4_K_M GGUF weights (2.49 GB each, SHA-256 verified), and downloaded models can be selected again without another transfer. llama.cpp and Metal libraries ship inside the app, so only the model needs downloading. Existing GGUF files and converted MLX folders can also be selected. Bundled runtime details.
- Optionally append named people in left-to-right order from an existing face scan. Names and normalized face positions enter the request; the app constructs the name list deterministically. Unnamed, excluded, and out-of-date face records are omitted. Ordering refers to the upright original image, rather than a developed crop. This iteration processes one photo; immutable per-photo requests and shared model admission provide the basis for a later batch queue.
- Non-destructive editing via JSON sidecar files with explicit save
- Copy and paste metadata between images
- Structured keywords — manage multiple named Photo Mechanic-style hierarchical lists, with one or several active at a time. Includes offline IPTC Media Topics in all 13 published languages and variants; follows the system language with a US English fallback and an independent language override. Existing keywords remain available as My Keywords. Custom lists, selection, and language settings are included in keyword archives, backups, and iCloud sync. IPTC Media Topics checks for updates weekly while the app runs, with an automatic-update toggle and a Check for Updates button; validated downloads are cached for offline use.
- Batch metadata application via templates with variable interpolation
- Template variables:
{date},{date:FORMAT},{dateCreated},{dateCreated:YYYYMMDD},{dateCreated:DDMMYYYY},{dateCreated:YYYY-MM-DD},{dateCaptured},{dateCaptured:YYYYMMDD},{dateCaptured:DDMMYYYY},{dateCaptured:YYYY-MM-DD},{filename},{seq},{persons},{keywords},{initials},{field:FIELDNAME}— variables also resolve inside keywords and Person Shown - Template hotkeys (Ctrl+1-9) for rapid workflows
- Required-metadata definitions that drive the browser's missing-field filter and the pre-upload check
- Metadata mirrored to both IPTC and XMP for cross-tool interoperability
- Correct IPTC
CodedCharacterSettagging so Nordic / non-ASCII characters round-trip through other apps - Pure-Swift in-process metadata engine (SwiftMediaMetadata 3) — no external binaries, no subprocess overhead
The generated metadata field and delivery support table lists every
current descriptive field ID, writer mapping, normalized read-back rule, and carrier boundary. It is
an implementation-support statement, not a claim of completed manual round trips through Adobe
Bridge, Photo Mechanic, or every supported browsing/export format.
Headline and localized Dublin Core Title are independent. SwiftMediaMetadata 3 preserves
ordered rdf:Alt language alternatives and exact language tags through the supported read/write paths;
Headline writes do not create, clear, or replace dc:title or IIM Object Name. External Adobe Bridge,
Photo Mechanic, and remaining licensed-container round trips are still limited to the evidence named in
the support table and validation records.
- A separately built, hardened-runtime
photo-agent-mcphelper is bundled for local STDIO MCP clients; it does not listen on the network. - Automation is disabled by default. Settings → Automation manages explicit folder grants and provides a copyable setup for Codex CLI, Claude Code, OpenCode 1.x, and OpenCode v2. Authorization refreshes shared preferences on every read, so persistent clients see revocation and fresh grant generations; failed refreshes or saves refuse authority.
- The current foundation exposes read-only server, photo-input-format, root, path-admission and photo-revision tools with strict canonical-path,
identity, link, special-file, and private-store refusal. The helper shares photo/folder reservations
with retained field, variable/template, Write All, Primary Develop and Batch Rename GUI execution. The helper
discovers stable metadata/Develop template UUIDs, names and revision hashes with
list_templatesfrom explicitly authorized default local or custom template folders (template iCloud sync must be off). Photo-revision inspection captures opaque source, XMP and owned app-sidecar tokens.preview_metadata_templatepreviews a template for one photo using exact template and photo revisions, with Append/Replace behavior matching the editor for descriptive fields, creators, organisations, scene/subject codes, date, country, source type, urgency, Media Topic, Genre and Image Supplier.preview_metadata_template_batchextends the same preview to 1–8 explicit photos, retaining and revalidating every photo and returning no partial result. Both previews resolve{filename}from each retained photo in Headline, Description, Extended Description and Instructions. Other variables, Keywords, instant processing and unsupported fields are refused; it creates no draft or approval.get_photo_metadatareturns bounded, typed effective editorial values with field provenance, pending/conflict state and those revision tokens.prepare_iptc_patchreturns read-only, revision-bound normalized before/after values, exact inputs and compatibility warnings for supported descriptive fields, exact carrier hashes and preservation baselines. Keyword edits bind the exact local Approved Keywords list and settings, using the editor's Strict policy, canonical spelling and duplicate handling. iCloud keyword lists are not yet supported by patch previews.get_iptc_patch_planrevalidates the retained preview and keyword authority. These expiring read-only plans survive helper restart in a bounded private local archive but cannot be committed by MCP.get_native_review_request_capacityexplicitly initializes or migrates request coordination storage and returns its durablerequestEpoch.request_iptc_patch_reviewqueues a durable request with that epoch, a new lowercase UUIDrequestID, theplanID, and purposependingDraftorxmpPublication. In Settings → Automation → Show Client Review Requests, refresh and inspect the request, then explicitly approve and apply it.get_native_review_requestreports the retained intent, linked operation ID and matching retained outcome; unavailable history stays explicitly unconfirmed. Settings shows these outcomes and lets you request cancellation during execution.cancel_native_review_requestcancels before admission or requests cooperative cancellation of linked work. Status, cancellation and exact retries retain the original epoch and request ID across helper restart; requests grant no consent and interrupted admission is never replayed. This private archive retains at most 256 requests without automatic eviction. Settings can explicitly remove requests cancelled before admission, or separately remove linked requests whose exact retained operation confirms a finished outcome, and rotate the epoch. Cleanup preserves operation and recovery history. Active, admitted, uncertain and missing-history requests remain retained. An uncertain XMP operation is eligible only with an exact matching retained receipt for completed restoration or unchanged staging; missing, replaced or incomplete recovery evidence keeps the request retained. Removed intents cannot be retried; clients must never silently resubmit them under a new epoch. Retained requests keep their original epochs. Settings can explicitly apply an approved plan to a pending local draft after the photo is deselected in all editors, preserving the photo and XMP. Template application, face scans and physical photo mutation tools remain under implementation for 3.0; transcription start uses the separate native grant below.get_operation_statusandcancel_operationexpose durable coordination records and cooperative cancellation requests. The native pending-draft executor is connected with distinctiptc_draftoutcomes. Available transcription batch status includes ordered per-photo outcomes without paths or transcript text. Saved transcripts are ready for metadata variables; failed or cancelled batches can retain a saved prefix. Caption launches the shared batch backend after explicit native consent; helper transcription invocation remains unfinished. A request is not completion.get_photo_voice_memoinspects one photo's saved adjacent WAV relationship under the authorized root and returns exact relationship/audio revision evidence. It does not expose audio or transcript content, decode WAVs, inspect provider readiness, or start transcription.prepare_voice_transcriptionretains an immutable five-minute preview for one to eight explicit photos and requested provider options.get_voice_transcription_planrechecks every photo, metadata, relationship, WAV and root grant before returning it. Preparation writes private coordination storage; neither tool grants consent, starts inference or saves drafts. Separate epoch-bound transcription review requests can be queued, inspected and cancelled through MCP, then revalidated in Settings → Automation → Transcription Intent Review. Photo order and requested provider options remain exact. Settings can separately review the exact selected native provider, locale/model/runtime and options, then start one durably linked operation after explicit consent. Whole-set rooted reservations and identity checks span recognition and verified create-only transcript saves. Existing transcripts refuse admission; cancellation preserves saved drafts. Closing Settings does not stop admitted work, and linked requests never replay. Helper tools can request cooperative cancellation and inspect the linked operation.open_voice_transcription_reviewasks the running app to open one exact original request/epoch. A private local socket authenticates both processes against the matching signed app/helper pair. The app repeats whole-set validation and the native UI clears previous execution consent.reviewRequiredacknowledges a presentation request only; an already linked request returns its exactly matched operation handle without claiming completion. No provider consent or execution authority is supplied by the helper. Copied, unsigned, mismatched, stale and unavailable peers refuse. After exact native provider review and checked consent, Allow Helper to Start Once enablesstart_voice_transcriptionfor that request for 60 seconds while the review stays open. The app consumes this session-only grant once and schedules guarded admission;executionRequestedconfirms scheduling, never durable admission, inference or completion. Provider changes, consent withdrawal, dismissal and repeat review revoke the grant. Exact linked retries return the original operation handle without starting again. Saved drafts still require separate caption approval. create_teamadds a team with a complete numbered roster to the Teams library. Enable Allow team creation in Settings → Automation as well as local automation. With Teams iCloud sync on, the request stays local until you open Teams → Review Imports, review the roster and choose Add Team or Reject. Approved imports use the app’s coordinated iCloud storage; unavailable iCloud leaves the request pending. Retry the same MCP call to check itsawaiting_confirmation,accepted, orrejectedstatus. A connected AI client can research this week's team sheets using its own web tools, then submit the team name, sport, kit colours and players. The helper itself does not browse. Supply a client-generatedteamIDUUID and reuse it with the same content on retries. Creation never replaces existing teams, assigns teams to a match, or links players to Known People. Player numbers must be unique integers from 0 through 9999. Unknown names, numbers and colours should be verified from a reliable source before submission.- Settings → Transcription selects Apple Speech, Whisper with embedded FFmpeg, or advanced Custom FFmpeg Whisper. Download Tiny, Base, Small, multilingual Turbo or Large v3 explicitly in the app; installed models are checked against pinned size/SHA-256 before use. Inference stays local, and Caption retains compact playback, Transcribe and review controls. Language, English translation and GPU request settings persist and are recorded in each editable draft. Custom files still require explicit session execution consent. Provider discovery does not expose transcription execution to MCP.
Rebuilt for 2.0 around a bundled on-device AuraFace (ArcFace) model, with eye-aligned crops and improved, fully editable face grouping — review groups, merge or split people, and drag faces between groups.
- Automatic face detection using the Apple Vision framework
- Face embeddings from a bundled AuraFace (ArcFace) CoreML model — 512-dimension vectors compared by cosine distance
- Quality-gated hierarchical clustering with eye-aligned face crops
- Quality scoring: confidence, face size, and blur detection
- Known People database with per-person embeddings and sample management
- Auto-matching with configurable confidence thresholds
- Import/export database (ZIP format)
- Interactive multi-face suggestions UI during metadata editing
- Dedicated Unmatched faces group with drag-to-group / ungroup actions
- Face data written to image metadata on save
- Waveform scope (Shift+1)
- Parade / RGB scope (Shift+2)
- Vectorscope (Shift+3)
- CIE 1931 chromaticity diagram (Shift+4) with target gamut overlay and HDR-aware display gamut indicator
- Gamut clipping soft proof for both edit and browse views
- All scopes rendered via Metal GPU compute shaders
- Source-revision-bound analysis cases that keep evidence separate from source metadata
- Pixel Analysis views for channels, luminance, alpha, fixed-parameter edges, and a labeled compression/residual visualization, with linked hover sampling and source-pixel inspection
- Source facts, namespace-preserving metadata/provenance inspection, C2PA states, and narrow consistency findings with alternatives and limitations instead of an authenticity verdict
- Photo annotations, source-pixel measurement, and optional user calibration
- OSINT timeline and map evidence with photo/map annotations, linked objects, field-of-view geometry, and an offline solar-position direction overlay
- Immutable PDF reports with selected findings, evidence figures, methodology, limitations, source identity, and map attribution or a schematic fallback
- Portable
.pintImage Analysis Project export/import containing the working-folder images, matching XMP sidecars, and folder-local Photo Agent case/metadata/version documents, with a manifest that checks every archived file before import
The app does not include an AI-origin detector, clone detector, automatic AI-artifact highlighting, or sun/shadow consistency verdict in 3.0. The Meta Content Seal and Google SynthID entries are privacy-labeled links to external services; Photo Agent does not upload the image to them.
- Compare exactly two images in side-by-side, stacked, or adjustable wipe layouts
- Synchronize normalized pan/zoom, temporarily unlock alignment, save an offset, and reset safely
- Enter comparison from Browser, Develop, or full-screen and present it on Clean Feed
- Save named Develop versions in an app-private JSON catalog without multiplying XMP states
- Duplicate, rename, delete, compare, or promote a named version to Primary; promotion first creates a recovery snapshot and verifies the resulting XMP read-back
Named versions are not interoperable XMP edits until explicitly promoted to Primary.
Render edited images with the same pixel-perfect Metal pipeline used for preview:
- SDR formats: JPEG, PNG, TIFF, HEIC, AVIF, JPEG XL
- HDR formats: Adaptive HDR JPEG (ISO gain map), 10-bit HEIC, 10-bit AVIF, JPEG XL, 16-bit TIFF, 16-bit PNG
- Color gamuts: sRGB, Display P3, Rec. 2020, Adobe RGB
- TIFF compression options: None, LZW, ZIP
- Quality slider for lossy formats
- Batch render selected or all images
AVIF can be encoded with native macOS Image I/O or bundled FFmpeg
(arm64, libaom-av1). JPEG XL encoding uses bundled FFmpeg (libjxl).
Experimental preview. C2PA signing has not yet been verified end-to-end and may change, or be turned off by default, in a future release.
- Detect and display C2PA content credentials on images
- Warnings before destructive writes to C2PA-protected images
- Experimental signing of images with C2PA content credentials — certificate and private key stored in the macOS Keychain
- Powered by bundled c2patool
- Upload selected or all images via FTP or SFTP
- Multiple connection profiles with a Test Connection button
- Credentials stored securely in macOS Keychain
- Pre-upload required-field check that is sidecar-aware (falls back to the XMP sidecar for RAW files)
- Edited images rendered into a per-folder
Uploaded/folder before sending, with batch abort - Progress tracking with upload overlay, automatic retry, and human-readable errors
- Deadline Send freezes the preflight, filenames, metadata, Develop state, export settings, and destination before producing isolated staged copies; warnings require explicit per-batch acceptance
- Deadline delivery supports staged copies only: SDR JPEG/TIFF and HDR Adaptive JPEG gain-map/16-bit TIFF. RAW sources are rendered into those derivatives; Deadline Send refuses original-file and XMP-sidecar-only delivery strategies
- Staged bytes receive the authoritative resolved metadata, are parsed back for semantic verification, and retain preservation evidence before upload. Failed and cancelled workflows keep verified staging evidence for exact resume or confirmed cleanup in Activity
FTP/SFTP delivery acknowledgement has deliberate limits. FTP/FTPS/SFTP protocol success and an optional
remote existence/size observation are not a cryptographic remote-byte verification. The production
adapter rechecks the local file's identity, size, and SHA-256 before requesting credentials, but there is
still a narrow path-based time-of-check/time-of-use interval before curl opens that path. SFTP currently
supports passwords supplied through a private temporary mode-0600 netrc file; SSH private-key identities
are not supported.
- Hand off images to external editors (Lightroom, Capture One, etc.)
- Import workflow for externally-edited files
Opt-in sync that keeps your library settings in step across Macs, configured in Settings → iCloud Sync:
- Master "Sync everything" toggle, or per-category control
- Synced categories: metadata templates, keyword lists, the Known People database, the Teams / roster library, and portable app settings
- Stored in the app's iCloud Drive container so it follows you to your other Macs
- Passwords, signing keys, and machine-specific values (file paths, certificates, FTP servers) stay on-device and are never synced
- Coordinated through
NSFileCoordinatorso syncing never forks conflicting duplicate folders
- Delivery receipts and retained delivery workflows live in local Application Support and are not part of iCloud Sync. Retained workflow directories are also excluded from device backup
- Workflow catalog and Activity summaries are deliberately limited and contain no credentials or editorial metadata values. Receipt Activity details additionally omit filenames, source paths, and content hashes
- Exact private workflow state necessarily contains the frozen plan, local source identities, resolved editorial metadata, destination details, and retained staged copies. It is used only for validation, resume, and delivery execution; it is not placed in Activity summaries, logs, analytics, or sync
- Retained workflows and staging are removed only after an explicit confirmation in Activity. Receipts can also be deleted there; the local receipt repository otherwise keeps at most 250 receipts and drops entries older than 365 days when its retention boundary runs
| Shortcut | Action |
|---|---|
| Cmd+O | Open folder |
| Shift+Cmd+I | Import photos |
| Cmd+B / Cmd+N | Previous / Next image |
| Cmd+0-5 | Set star rating |
| Option+0-8 | Set color label |
| Cmd+T | Open template palette |
| Ctrl+1-9 | Apply template 1-9 |
| Cmd+E | Open in external editor |
| H | Toggle HDR mode |
| G | Toggle gamut clipping |
| M | Toggle masks panel |
| Cmd+J | Add new mask |
| Cmd+W (Develop) | Remove selected local layer, or reset Global |
| Cmd+D | Mute selected mask (or the Global layer) |
| Option+V | Paste develop settings (including crop) |
| Cmd+R / Shift+Cmd+R | Rotate right / left |
| Cmd+S | Render selected |
| Shift+Cmd+S | Render all |
| Shift+Cmd+W | Write pending metadata |
| Cmd+U / Shift+Cmd+U | Upload selected / all |
| Shift+1-4 | Scope: Waveform / Parade / Vector / Chromaticity |
| Space | Full-screen toggle |
| 0-5 (full-screen or Develop) | Set rating; 0 clears |
| Middle-click (Develop filmstrip) | Copy clicked image's settings to current selection |
MVVM with a services layer, built primarily on Apple frameworks plus a small set of bundled helper binaries.
- Swift 6 with strict concurrency (
@MainActordefault isolation,Sendableservices) - Metal GPU pipeline for real-time image editing, scope rendering, and export
- SwiftMediaMetadata 3 (SPM, pure Swift) for metadata read/write
- Image I/O for native 8-bit and 10-bit AVIF encoding
- FFmpeg (bundled, arm64) for alternative AVIF and JPEG XL encoding
- c2patool (bundled) for C2PA signing
- Apple Vision for face detection; bundled AuraFace (ArcFace) CoreML model for face embeddings and recognition
| Location | Contents |
|---|---|
.photo_metadata/ (per folder) |
JSON metadata sidecars |
.xmp sidecars (per folder) |
Camera RAW edit settings |
.face_data/ (per folder) |
Face detection data and thumbnails |
~/Library/Application Support/Aagedal Photo Agent/KnownPeople/ |
Known People database |
~/Library/Application Support/Aagedal Photo Agent/Templates/ |
Metadata presets |
~/Library/Application Support/Aagedal Photo Agent/Lists/ |
Keyword lists |
When iCloud Sync is enabled, the Templates, KnownPeople, Teams, and Lists folders move to the app's iCloud Drive container (iCloud.aagedal.Aagedal-Photo-Agent/Documents/) instead of Application Support.
The app uses Sparkle for in-app auto-updates. Releases are signed with an EdDSA key and advertised through the canonical appcast.xml on GitHub. Codeberg keeps a synchronized legacy copy for older installed builds whose feed URL still points there.
- Resolve Swift packages so Sparkle's command-line tools are downloaded:
xcodebuild -resolvePackageDependencies -project "Aagedal Photo Agent.xcodeproj" - Generate an EdDSA key pair. The private key is stored in your login keychain; the public key is printed to stdout:
"$(find ~/Library/Developer/Xcode/DerivedData -name generate_keys -path '*/Sparkle*' | head -n 1)" - Paste the public key into
Aagedal Photo Agent/Info.plistunderSUPublicEDKey, replacingREPLACE_WITH_PUBLIC_KEY_FROM_GENERATE_KEYS. Commit the plist; never commit the private key.
- Bump
MARKETING_VERSIONandCURRENT_PROJECT_VERSIONin the Xcode project. Keep the bundle version numeric (for example3.0.0). SetAAGEDAL_RELEASE_VERSIONto3.0.0-beta.2for a beta, or to the numeric version for a stable release. Every beta and subsequent stable release must have a strictly increasing build. Beta DMGs and archives use the full beta identity; generated appcast items carrysparkle:channel=beta. Only beta installations opt into that channel; the eventual stable release remains available to them through the default channel. - Add the release notes to
CHANGELOG.md, including a concise### Highlightslist for the Sparkle appcast. AuraFace is bundled for this release. The release assistant verifies the exported app containsContents/Resources/AuraFaceR100.mlmodelcwith the reviewed model weights before notarization. - Commit and push the exact release source, then wait for the macOS CI / Clean build and unfiltered tests check to pass for that commit. The workflow performs a clean
build-for-testing, an unfilteredtest-without-building, generated-metadata drift checking, JSON/plist validation, conflict-marker scanning, and whitespace checks. Configure this check as required on the protected release branch in GitHub; repository files cannot enforce that remote setting. - From a clean checkout of that same commit, run the release assistant with an authenticated GitHub CLI:
Before accessing signing credentials or building, it rejects a dirty worktree and verifies a successful CI run tied to the exact
scripts/release.sh
HEADSHA. The accepted run is recorded underbuild/release/. It then archives, exports with Developer ID, notarizes and staples the app and DMG, Sparkle-signs the DMG, and inserts the appcast item. When an archive or valid exported app has matching version/build metadata and the source-revision marker written by the script, its terminal menu can resume from that artifact instead of rebuilding; unmarked or stale-revision artifacts are rejected. SetRELEASE_BUILD_MODE=reuseorRELEASE_BUILD_MODE=rebuildto make that choice non-interactively. - Upload the DMG to
https://aagedal.me/apps/photoagent/, using the exact filename printed by the script. - Commit and push the generated
appcast.xmlto GitHub, tag the release, then synchronize the website fallback appcast and legacy Codeberg copy. - For stable releases, bump the cask in the
aagedal/homebrew-taprepo. For public betas, publish a GitHub prerelease with tag3.0.0-beta.2, attach the notarized DMG and its SHA-256, and leave the stable cask/latest release unchanged. Include the beta limitations and backup guidance from the changelog.
An emergency can bypass the CI lookup only with RELEASE_TEST_GATE_OVERRIDE=EMERGENCY, a written RELEASE_TEST_GATE_OVERRIDE_REASON of at least 20 characters, and confirmation by typing (or setting RELEASE_TEST_GATE_OVERRIDE_CONFIRM to) the full current SHA. The script prints a prominent warning and records the revision, operator, timestamp, and reason in build/release/release-test-gate.json and the append-only-per-worktree release-test-gate-audit.jsonl. Preserve those files with the release records. An override does not permit releasing uncommitted source.
GPL-3.0 - see LICENSE for details.
License texts ship with the app (Settings → Licenses) and live under Aagedal Photo Agent/Resources/.
| Component | Purpose | License |
|---|---|---|
| FFmpeg | Image encoding and local Whisper transcription | GPL-3.0 |
| c2patool | C2PA content credentials | MIT |
| Sparkle | Software updates | MIT |
| SwiftMediaMetadata | Image, audio, and video metadata | GPL-3.0 |
| AuraFace-v1 | Face recognition model | Apache-2.0 |
The app bundles a GPL-licensed FFmpeg binary. In accordance with the GPL, the corresponding source is available:
- FFmpeg 9.0.1, built with
--enable-gpl --enable-version3 --enable-whisper. This full build supports image export and local voice memo transcription. Network and device support is compiled in; transcription restricts input protocols to local files. - Exact source inputs, local patches and build evidence
are pinned in the component manifest. Preparation:
scripts/ffmpeg/prepare_full_candidate.py. The complete corresponding source companion must accompany a release; an upstream FFmpeg tarball alone does not reproduce this modified build and its dependencies.
Versions, immutable upstream and build-recipe revisions, artifact SHA-256 values, licenses, target
architectures, and expected runtime capabilities for bundled binaries and the optional AuraFace model are
recorded in Aagedal Photo Agent/Resources/bundled-components.json. The repository validator checks that
manifest against every present artifact; the required FFmpeg and c2patool binaries must always be present.
The application's own source is published under GPL-3.0. SwiftMediaMetadata is resolved from its
GPL-3.0 upstream repository at the version pinned in Package.resolved (currently
3.0.1); the shipped license text, public
table, and in-app label consistently identify GPL-3.0. See the
documentation-readiness validation.