LDAP-based Gravatar server
  • Go 82.8%
  • Nix 16.4%
  • Dockerfile 0.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Cass Fridkin 6e32b32889
README: document deployment, configuration, and monitoring
- Deployment: nix flake example wiring the overlay and services.ldapatar
  module into a consumer's nixosSystem, with inputs.nixpkgs.follows;
  Containerfile note.
- Configuration: full resolution chain for the config file (flag > env >
  user config dir via os.UserConfigDir > /etc), precedence order (CLI >
  env > file > defaults), and per-option tables (32 rows: flag, env var,
  config key, type, default) split by Config file / LDAP / Server / Avatar
  / Log.
- Monitoring: healthz/readyz semantics (readyz gates on initial directory
  sync completion, 503 until then), metrics TODO note.

The nix example is machine-verified: extracted verbatim, locked against
the real git+https remote (rev 5104a99), nixosSystem with the module
enabled evaluates (enable=true, unit wantedBy multi-user.target), and the
generated YAML matches the example's settings verbatim.

Assisted-by: Pi (z-ai/glm-5.3)
2026-10-11 17:34:14 -04:00
cmd/ldapatar Add AGPL-3.0-only license and copyright notice 2026-10-11 12:35:31 -04:00
nix Strictly typed settings schema mirroring RegisterFlags 2026-10-11 13:32:03 -04:00
.envrc Add direnv (use flake) and a devShell to the flake 2026-10-11 13:11:58 -04:00
.gitignore Ignore .direnv/ cache 2026-10-11 13:12:04 -04:00
config.go Search default config paths when no explicit config is set 2026-10-11 13:49:08 -04:00
config_test.go Fix crop-top-bias help text and honor explicit zero in config 2026-10-04 13:47:03 -04:00
Containerfile Add AGPL-3.0-only license and copyright notice 2026-10-11 12:35:31 -04:00
flake.lock Add nix flake with package, overlay, and NixOS module 2026-10-11 12:44:19 -04:00
flake.nix Resolve the Go toolchain from go.mod 2026-10-11 13:17:55 -04:00
go.mod Add config file support, precedence ordering, and validation 2026-09-20 15:22:39 -04:00
go.sum Add config file support, precedence ordering, and validation 2026-09-20 15:22:39 -04:00
handler.go Decouple AvatarServer from LDAPClient via DirectoryClient interface 2026-10-04 13:48:22 -04:00
handler_test.go Allow zero crop bias and composite transparency onto white for JPEG 2026-10-04 13:47:01 -04:00
ldap.go Make LDAP connection reset conditional on the failed connection 2026-10-04 13:46:52 -04:00
ldap_test.go Make LDAP connection reset conditional on the failed connection 2026-10-04 13:46:52 -04:00
LICENSE Add AGPL-3.0-only license and copyright notice 2026-10-11 12:35:31 -04:00
README.md README: document deployment, configuration, and monitoring 2026-10-11 17:34:14 -04:00
server.go Decouple AvatarServer from LDAPClient via DirectoryClient interface 2026-10-04 13:48:22 -04:00
server_test.go gofmt: align struct literal in TestAvatarServerRefreshError 2026-10-04 14:01:09 -04:00

LDAPatar

LDAPatar is an LDAP-backed Gravatar-compatible avatar server in a single Go binary. Configure via CLI args, environment variables, and/or config files. Starting it is as simple as:

$ ldapatar -b dc=swag,dc=lgbt

You can then configure other services' GRAVATAR_URL to your ldpatar deployment, and you'll see user's profile photos :)

Deployment

As a go binary, you can simply compile and drop the ldapatar binary wherever you want. A Containerfile and a nix package are also provided for your convenience. Precompiled binaries will be available once I figure out my CI setup.

Nix Service

A Nix flake is provided, exposing a package overlay and a NixOS service module. Consumed from your own flake:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    ldapatar = {
      url = "git+https://git.swag.lgbt/cass/ldapatar.git";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { nixpkgs, ldapatar, ... }: {
    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ldapatar.nixosModules.default # provides services.ldapatar
        {
          nixpkgs.overlays = [ ldapatar.overlays.default ]; # provides pkgs.ldapatar

          services.ldapatar = {
            enable = true;
            settings = {
              ldap.url = "ldaps://ldap.swag.lgbt";
              server.http.listen = "127.0.0.1:8080";
            };
            # environmentFile = "/run/secrets/ldapatar"; # LDAPATAR_LDAP_BIND_PASS=...
          };
        }
      ];
    };
  };
}

Configuration

LDAPatar supports layered configuration. An invocation of ldapatar will read configuration from the following sources, ranked in order of precedence:

  1. CLI Arguments (e.g. ldapatar --ldap.base-dn="dc=swag,dc=lgbt")
  2. Environment variables (e.g. LDAPATAR_LDAP_BASE_DN="dc=swag,dc=lgbt" ldapatar)
  3. A config file (e.g. /etc/ldapatar/ldapatar.yaml). The path to this config file is determined by the following:
    1. The --config flag (e.g. ldapatar --config ./ldapatar.yaml)
    2. The LDAPATAR_CONFIG environment variable
    3. The user config directory as determined by os.UserConfigDir(), with /ldapatar/ldapatar.yaml appended (e.g. ~/.config/ldapatar/ldapatar.yaml)
    4. /etc/ldapatar/ldapatar.yaml

If a config file path is set via the --config flag or the LDAPATAR_CONFIG environment variable, ldapatar will terminate with a fatal error if the path specified doesn't exist. Otherwise, it'll load implicit defaults.

You can specify configuration in multiple ways; for example, a command-line argument and a config file. Configurations are merged at runtime, and any higher-precedence values override lower-precedence ones.

Configuration options

Config file

Name Description CLI Flag Environment Variable Config File Setting Type Default Value
Config file path Path to the YAML/JSON configuration file to load -c, --config LDAPATAR_CONFIG - path see above

LDAP

Name Description CLI Flag Environment Variable Config File Setting Type Default Value
LDAP server URL LDAP server backing the avatar pool -l, --ldap.url LDAPATAR_LDAP_URL ldap.url string (URL) ldap://127.0.0.1:3890
LDAP base DN Base DN for user searches -b, --ldap.base-dn LDAPATAR_LDAP_BASE_DN ldap.base-dn string (DN) dc=example,dc=com
LDAP bind DN Bind DN for authenticated searches -D, --ldap.bind-dn LDAPATAR_LDAP_BIND_DN ldap.bind-dn string (DN) unset (anonymous)
LDAP bind password Bind password -w, --ldap.bind-pass LDAPATAR_LDAP_BIND_PASS ldap.bind-pass string unset — prefer an environment variable or systemd EnvironmentFile; config files are not secret
Email fields LDAP attributes holding user email addresses --ldap.email-fields LDAPATAR_LDAP_EMAIL_FIELDS ldap.email-fields list of strings mail
Avatar field LDAP attribute holding the avatar photo --ldap.avatar-field LDAPATAR_LDAP_AVATAR_FIELD ldap.avatar-field string jpegPhoto
User filter Search filter clause selecting users --ldap.user-filter LDAPATAR_LDAP_USER_FILTER ldap.user-filter string (objectClass=person)
Page size LDAP search page size; 0 disables paging --ldap.page-size LDAPATAR_LDAP_PAGE_SIZE ldap.page-size unsigned integer 500
Timeout LDAP network dial and search timeout --ldap.timeout LDAPATAR_LDAP_TIMEOUT ldap.timeout duration string 10s
StartTLS Upgrade plain connections with StartTLS --ldap.start-tls LDAPATAR_LDAP_START_TLS ldap.start-tls boolean false
Insecure skip verify Skip LDAP server TLS certificate verification --ldap.insecure-skip-verify LDAPATAR_LDAP_INSECURE_SKIP_VERIFY ldap.insecure-skip-verify boolean false
CA certificates Paths to PEM CA bundles for LDAP TLS --ldap.ca-certs LDAPATAR_LDAP_CA_CERTS ldap.ca-certs list of paths unset (system pool)

Server

Name Description CLI Flag Environment Variable Config File Setting Type Default Value
HTTP listen HTTP listen address -a, --server.http.listen LDAPATAR_SERVER_HTTP_LISTEN server.http.listen string (address) 127.0.0.1:8080
HTTP read timeout HTTP read timeout --server.http.read-timeout LDAPATAR_SERVER_HTTP_READ_TIMEOUT server.http.read-timeout duration string 10s
HTTP write timeout HTTP write timeout --server.http.write-timeout LDAPATAR_SERVER_HTTP_WRITE_TIMEOUT server.http.write-timeout duration string 15s
HTTP shutdown timeout Graceful HTTP server shutdown timeout --server.http.shutdown-timeout LDAPATAR_SERVER_HTTP_SHUTDOWN_TIMEOUT server.http.shutdown-timeout duration string 5s
Sync interval Periodic directory sync interval -r, --server.sync.interval LDAPATAR_SERVER_SYNC_INTERVAL server.sync.interval duration string 5m
Sync cooldown Minimum cooldown between on-demand refreshes -m, --server.sync.cooldown LDAPATAR_SERVER_SYNC_COOLDOWN server.sync.cooldown duration string 30s
Photo cache size Maximum raw photos cached in memory --server.cache.photo.size LDAPATAR_SERVER_CACHE_PHOTO_SIZE server.cache.photo.size integer 10000
Photo cache TTL Raw photo cache TTL --server.cache.photo.ttl LDAPATAR_SERVER_CACHE_PHOTO_TTL server.cache.photo.ttl duration string 15m
Render cache size Maximum rendered images cached in memory --server.cache.render.size LDAPATAR_SERVER_CACHE_RENDER_SIZE server.cache.render.size integer 10000
Client max-age Cache-Control max-age for clients --server.cache.client.max-age LDAPATAR_SERVER_CACHE_CLIENT_MAX_AGE server.cache.client.max-age duration string 5m
Client stale-while-revalidate Cache-Control stale-while-revalidate duration --server.cache.client.stale-while-revalidate LDAPATAR_SERVER_CACHE_CLIENT_STALE_WHILE_REVALIDATE server.cache.client.stale-while-revalidate duration string 24h

Avatar

Name Description CLI Flag Environment Variable Config File Setting Type Default Value
Upstream URL Libravatar/Gravatar fallback URL --avatar.upstream-url LDAPATAR_AVATAR_UPSTREAM_URL avatar.upstream-url string (URL) https://seccdn.libravatar.org/avatar/
Default size Default avatar size in pixels when omitted --avatar.default-size LDAPATAR_AVATAR_DEFAULT_SIZE avatar.default-size integer 80
Max size Maximum avatar size in pixels --avatar.max-size LDAPATAR_AVATAR_MAX_SIZE avatar.max-size integer 2048
Crop top bias Top-bias crop ratio for portraits (0.0=top, 0.5=centered) --avatar.crop-top-bias LDAPATAR_AVATAR_CROP_TOP_BIAS avatar.crop-top-bias float 0.2
JPEG quality JPEG encoding quality (1-100) --avatar.jpeg.quality LDAPATAR_AVATAR_JPEG_QUALITY avatar.jpeg.quality integer 85
GIF num colors GIF encoding maximum colors (1-256) --avatar.gif.num-colors LDAPATAR_AVATAR_GIF_NUM_COLORS avatar.gif.num-colors integer 256

Log

Name Description CLI Flag Environment Variable Config File Setting Type Default Value
Format Log format --log.format LDAPATAR_LOG_FORMAT log.format one of pretty, json, text pretty
Level Log level --log.level LDAPATAR_LOG_LEVEL log.level one of debug, info, warn, error info

Administration

By default, ldapatar provides two health endpoints:

  • /healthz returns 200 when the service is up and running
  • /readyz returns 200 after the initial directory sync has completed, and a 503 until then.

I haven't implemented a Prometheus-compatible /metrics endpoint mostly because I haven't gotten around to it yet. Logging is all structured/leveled, and you can opt in to JSON logging for consumption by log aggregators.