Skip to content
andrew.dunn.dev

bairn

Source

A personal photo archive for households whose nursery or preschool uses Famly. Pulls images and videos from the parent-side feed to disk, with full EXIF and XMP metadata embedded into each file (educator name, kids tagged, post body, timestamp, timezone). Optional Immich upload for households that already self-host one.

human-rate fetchwritesuploadsresume stateVENDORFamlyGraphQL + CDNCLIbairnpaginate the feedfetch best resolutionembed metadatasave with retrysingle static binarySTOREdiskcanonical, EXIF + XMPOPTIONALImmichself-hostedSTATEstate file$XDG_STATE_HOME/bairnCONTRACTsingle household • single account • single host • human-rate • no redistribution
bairn pulls a household's Famly feed to the operator's own disk with the metadata embedded in every file, and optionally to a self-hosted Immich.

Single static Go 1.25 binary. Spec-first typed clients (genqlient against the Famly GraphQL surface, oapi-codegen against the Immich OpenAPI spec). Five-state typestate asset lifecycle (Discovered → Downloaded → Saved → Uploaded? → Recorded) tracked in a JSON state file under $XDG_STATE_HOME, locked with flock, written atomically. Polite retry primitive on cenkalti/backoff/v5 that honors Retry-After and treats 4xx auth errors as permanent. Discovery toolkit (shape probe, traffic-capture playbook, GraphQL introspection) under discovery/, with vendor-specific outputs gitignored.

Highlights

  • Embeds full Famly-side context into each saved JPEG (educator, kids tagged, post body, timestamp, timezone) so the archive is usable independently of the platform.
  • Best-resolution downloads: rewrites the CDN URL’s size segment to the originally-reported width and height before fetching.
  • Hand-rolled XMP packet writer that splices the APP1 segment into the JPEG bytes at the correct pre-SOS position (dsoprea’s library appends after SOS, where readers ignore it).
  • Sink interface separates disk (always-on canonical) from Immich (env-gated, optional). New sinks slot in without touching the orchestration loop.
  • Seven architecture decisions documented at docs/decisions/0001-0007.
  • LLM-augmented CI via gitlab.com/dunn.dev/pipeline/claude-drift-triage for shape-signature drift triage.
  • MIT licensed.

The story of why this exists, the methodology bairn implements, and the wider point about vendor stance is in A Progressive Vendor Stance for the Agent-Native Future.