A modern photo-sharing platform that revives the feature set of Zoto (2008) — albums, tags, comments, contacts, messaging, granular permissions — with an AI-powered assistant and OCR search pipeline.
- Backend: Python 3.11+, FastAPI, SQLAlchemy 2.0 (async), PostgreSQL
- Frontend: Jinja2 templates, HTMX, Fuse.js (client-side fuzzy search)
- Image Processing: Pillow (renders/thumbnails), exifread (EXIF extraction), OpenCV + Tesseract (OCR)
- AI: Claude API — vision analysis for auto-tagging, chat assistant with UI control
- Image Management — Upload, auto-generate thumbnails/renders, EXIF extraction, rotate, download
- AI Auto-Tagging — Claude Vision analyzes uploads: generates title, description, and relevant tags. Cross-references existing tags for consistency.
- Search — PostgreSQL full-text search (weighted: title > tags > description > OCR text) with ILIKE fallback, plus Fuse.js client-side instant search
- OCR Pipeline — Extract text from scanned images via OpenCV preprocessing + Tesseract, searchable
- Albums & Sets — Create albums, reorder images, organize albums into sets
- Tags — Per-image tags with tag clouds, bulk tagging, rename across library
- Comments — Threaded comments on images, visibility toggle
- Contacts & Groups — Add contacts, create custom groups, mutual contact detection
- Messages — Internal messaging with read/reply status tracking
- Permissions — Granular 8-type permission system (view, tag, comment, print, download, geotag, vote, blog) with 5 access levels (private, public, specific groups, contacts only, inherit from account defaults)
- AI Chat Assistant — Persistent chat widget that searches your library, controls the UI (shows images, navigates pages, displays inline previews), tags/describes images, creates albums
- Fullscreen Lightbox — Double-click any image to view fullscreen, Escape to close
- Python 3.11+
- Docker (for PostgreSQL)
- Tesseract OCR (optional, for OCR pipeline)
# Clone and enter the project
cd zoto-revival
# Start PostgreSQL
docker-compose up -d
# Create a virtualenv and install
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -e .
# Configure environment
cp .env.example .env
# Edit .env with your SECRET_KEY
# Run database migrations
alembic upgrade head
# Start the server
uvicorn zoto.main:app --reload --port 8081Open http://localhost:8081, register an account, and start uploading.
To enable auto-tagging and the chat assistant, add your Claude API key in Settings (click your username in the nav bar).
src/zoto/
├── main.py # FastAPI app, lifespan, 404 handler
├── config.py # pydantic-settings configuration
├── web.py # Browser routes (Jinja2 templates)
├── db/
│ ├── engine.py # Async engine + session factory
│ └── models.py # 16 SQLAlchemy models
├── api/
│ ├── deps.py # Auth (JWT + cookie), permission checking
│ ├── auth.py # Register, login, token
│ ├── users.py # Profile CRUD
│ ├── images.py # Upload, renders, analyze, CRUD
│ ├── albums.py # Album CRUD, image management
│ ├── tags.py # Tagging, bulk tag, rename, tag cloud
│ ├── comments.py # Comments with visibility control
│ ├── contacts.py # Contact management, groups
│ ├── messages.py # Internal messaging
│ ├── sets.py # Album set management
│ ├── search.py # Full-text + Fuse.js index endpoint
│ ├── permissions.py # Permission get/set
│ └── chat.py # AI chat with tool use
├── services/
│ ├── storage.py # Hash-based filesystem (originals + renders)
│ ├── render.py # Pillow resize/crop/rotate
│ ├── exif.py # EXIF metadata extraction
│ ├── ocr.py # OpenCV + Tesseract OCR pipeline
│ ├── search.py # PG full-text search + Fuse.js index builder
│ ├── vision.py # Claude Vision auto-analysis
│ ├── chat.py # Chat agent with data + UI tools
│ └── activity.py # Activity logging
├── schemas/
│ └── __init__.py # Pydantic request/response models
├── templates/ # Jinja2 HTML templates
│ ├── base.html
│ ├── auth/
│ ├── images/
│ ├── albums/
│ ├── search/
│ ├── users/
│ ├── errors/
│ └── partials/
└── static/
├── css/main.css # Dark theme
└── js/
├── htmx.min.js
├── fuse.min.js
├── search.js # Client-side fuzzy search
└── chat.js # Chat widget with UI control
| Model | Description |
|---|---|
| User | Accounts with profile, quota, API key |
| Image | Photos with EXIF, dimensions, GPS, fulltext index |
| ImagePermissions | 8 permission types per image (view, tag, comment, etc.) |
| AccountPermissions | Account-level permission defaults |
| Album | Photo albums with display settings |
| AlbumImage | Album-image association with ordering |
| AlbumPermissions | Album-level view/comment permissions |
| AlbumSet | Sets that group albums |
| AlbumSetXref | Set-album association with ordering |
| Tag | Image tags (composite key: image + tagger + name) |
| Comment | Image comments with visibility flag |
| ContactGroup | User contact groups (auto per-contact or custom) |
| ContactMember | Group membership |
| Message | Internal messages with send/receive status |
| ImageOCR | OCR extraction results with text regions |
| ActivityLog | Activity audit trail |
Inherited from the original Zoto platform. Each permission type (view, tag, comment, print, download, geotag, vote, blog) has a flag value:
| Flag | Meaning |
|---|---|
| 0 | Private (owner only) |
| 1 | Public (everyone) |
| 2 | Specific groups |
| 3 | Contacts only |
| 4 | Inherit from account defaults |
Permissions cascade: image-level overrides account-level defaults (flag=4 triggers inheritance).
Built by combining:
- Zoto Server (2008) — Original Flickr-era photo platform (Python/Twisted/PostgreSQL). The original source is preserved in
archive/. - Gordon Landreth Photography (github.com/arts-link/gordon-landreth-photography) by Benjamin Strawbridge — OCR pipeline and Fuse.js search implementation.
See LICENSE.