{"id":50278487,"url":"https://github.com/misospace/miso-gallery","last_synced_at":"2026-05-27T22:38:07.923Z","repository":{"id":340696669,"uuid":"1167098606","full_name":"misospace/miso-gallery","owner":"misospace","description":"Lightweight image gallery + manager","archived":false,"fork":false,"pushed_at":"2026-05-24T20:01:07.000Z","size":1992,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T22:37:59.788Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/misospace.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"security.py","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-26T00:01:34.000Z","updated_at":"2026-05-24T19:19:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/misospace/miso-gallery","commit_stats":null,"previous_names":["joryirving/miso-gallery","misospace/miso-gallery"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/misospace/miso-gallery","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misospace%2Fmiso-gallery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misospace%2Fmiso-gallery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misospace%2Fmiso-gallery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misospace%2Fmiso-gallery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/misospace","download_url":"https://codeload.github.com/misospace/miso-gallery/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/misospace%2Fmiso-gallery/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33586820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-27T22:38:07.171Z","updated_at":"2026-05-27T22:38:07.914Z","avatar_url":"https://github.com/misospace.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Miso Gallery\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/docker/v/ghcr.io/misospace/miso-gallery?sort=semver\u0026label=ghcr\" alt=\"GHCR\"\u003e\n  \u003cimg src=\"https://github.com/misospace/miso-gallery/actions/workflows/build.yaml/badge.svg\" alt=\"Build\"\u003e\n  \u003cimg src=\"https://img.shields.io/github/v/release/misospace/miso-gallery?sort=semver\" alt=\"Release\"\u003e\n  \u003cimg src=\"https://img.shields.io/github/license/misospace/miso-gallery\" alt=\"License\"\u003e\n\u003c/p\u003e\n\n\u003e A lightweight, self-hosted image gallery for AI-generated images.\n\n## Features\n\n- 🍲 **Simple**: Flask-based, minimal dependencies\n- 📱 **Mobile-friendly**: PWA-ready responsive design\n- 🔐 **Authentication**: OIDC (Authentik, Okta, Google) + local password\n- 🖼️ **Thumbnails**: Auto-generated thumbnail caching\n- 🗑️ **Bulk operations**: Multi-select delete\n- 🔄 **Refresh**: Live refresh button\n- 🐳 **Containerized**: Docker + Kubernetes deployment ready\n\n## Quick Start\n\n### Docker\n\n```bash\ndocker run -d --name miso-gallery \\\n  -p 5000:5000 \\\n  -v /path/to/images:/data \\\n  ghcr.io/misospace/miso-gallery:latest\n```\n\n### Docker Compose\n\n```yaml\nservices:\n  miso-gallery:\n    image: ghcr.io/misospace/miso-gallery:latest\n    ports:\n      - \"5000:5000\"\n    volumes:\n      - ./images:/data\n    environment:\n      - ADMIN_PASSWORD=your-password\n```\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `DATA_FOLDER` | No | `/data` | Path to image directory |\n| `IMAGE_BASE_URL` | No | - | Base URL for shareable links |\n| `PORT` | No | `5000` | Server port |\n\n### Authentication\n\nMiso Gallery supports two authentication methods:\n\n#### Local Password (Default)\n\n```bash\ndocker run -d \\\n  -e ADMIN_PASSWORD=your-password \\\n  ghcr.io/misospace/miso-gallery:latest\n```\n\n#### OIDC / Authentik\n\n```bash\ndocker run -d \\\n  -e AUTH_TYPE=oidc \\\n  -e OIDC_ISSUER=https://authentik.yourdomain.com \\\n  -e OIDC_CLIENT_ID=miso-gallery \\\n  -e OIDC_CLIENT_SECRET=your-secret \\\n  -e OIDC_CALLBACK_URL=https://miso-gallery.yourdomain.com/auth/callback \\\n  -e SECRET_KEY=your-session-secret \\\n  ghcr.io/misospace/miso-gallery:latest\n```\n\n#### Auth Configuration Options\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `AUTH_TYPE` | No | `local` | Auth method: `local`, `oidc`, or `none` |\n| `ADMIN_PASSWORD` | If local | - | Password for local auth (plaintext or Werkzeug hash: `pbkdf2:` / `scrypt:`) |\n| `SECRET_KEY` | Yes | - | Flask secret for sessions. Generate with: `python -c \"import secrets; print(secrets.token_urlsafe(48))\"` |\n| `LLM_READ_API_KEYS` | No | - | Comma-separated Bearer tokens with **read scope** (list, view, thumbnails). Write-scoped keys are also accepted here. |\n| `LLM_WRITE_API_KEYS` | No | - | Comma-separated Bearer tokens with **write scope** (delete, dedup, bulk operations, task execution). |\n| `LLM_API_KEYS` | No | — | Legacy single var; functions as both read and write. Deprecated in favour of `LLM_READ_API_KEYS` / `LLM_WRITE_API_KEYS`. |\n| `OIDC_ISSUER` | If OIDC | - | OIDC provider URL (e.g., https://authentik.example.com) |\n| `OIDC_CLIENT_ID` | If OIDC | - | OIDC client ID |\n| `OIDC_CLIENT_SECRET` | If OIDC | - | OIDC client secret |\n| `OIDC_CALLBACK_URL` | If OIDC | - | Callback URL for OIDC |\n\n#### Rate Limiting\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `RATE_LIMIT_REDIS_URL` | No | - | Redis/Dragonfly URL for shared rate-limit state (falls back to in-memory if unset/unreachable) |\n| `RATE_LIMIT_PREFIX` | No | `miso-gallery:ratelimit` | Key prefix for rate-limit entries |\n| `RATE_LIMIT_ROUTE_LIMITS` | No | - | JSON overrides per endpoint, e.g. `{\"auth\":{\"max_requests\":5,\"window\":300}}` |\n\nSee [docs/rate-limit-shared-backend.md](docs/rate-limit-shared-backend.md) for recommended production rollout and migration plan.\n\n#### Authentik Setup\n\n1. Create an Application in Authentik\n2. Create a Provider (OpenID Connect) with these settings:\n   - Client ID: `miso-gallery`\n   - Client Secret: Generate a secure secret\n   - Signing Key: Select default\n   - Redirect URIs: `https://miso-gallery.yourdomain.com/auth/callback`\n3. Copy the Provider URL (issuer) to `OIDC_ISSUER`\n\n## Kubernetes Deployment\n\n### HelmRelease Example\n\n```yaml\napiVersion: helm.toolkit.fluxcd.io/v2\nkind: HelmRelease\nmetadata:\n  name: miso-gallery\n  namespace: apps\nspec:\n  chart:\n    spec:\n      chart: app-template\n      version: 3.0\n  values:\n    controllers:\n      miso-gallery:\n        containers:\n          app:\n            image:\n              repository: ghcr.io/misospace/miso-gallery\n              tag: latest\n            env:\n              - name: ADMIN_PASSWORD\n                valueFrom:\n                  secretKeyRef:\n                    name: miso-gallery-secrets\n                    key: password\n            persistence:\n              data:\n                type: nfs\n                server: nfs.yourdomain.com\n                path: /path/to/images\n    service:\n      app:\n        ports:\n          http:\n            port: 5000\n    route:\n      app:\n        hostnames:\n          - gallery.yourdomain.com\n        parentRefs:\n          - name: envoy-external\n```\n\n## Features\n\n### Thumbnails\n\nThumbnails are automatically generated and cached in `.thumb_cache/` directory. This improves loading performance for large galleries.\n\n- Max size: 400x400\n- Format: Optimized JPEG\n- Auto-refresh: Thumbnails regenerate when source image changes\n\n### Multi-Select\n\n- Click checkboxes on images to select\n- Use \"Select All\" / \"Deselect All\" buttons\n- Bulk delete selected images\n\n### Direct Image Access\n\nEven when authentication is enabled, direct URLs to images remain publicly accessible:\n\n- `/view/folder/image.jpg` - Full resolution\n- `/thumb/folder/image.jpg` - Thumbnail\n\nThis allows sharing images while protecting the gallery UI.\n\n## LLM API\n\nMiso Gallery includes a JSON API intended for LLM agents and other machine-to-machine clients. The primary purpose is to let an external LLM client inspect and manage gallery state: list/search media, read metadata, tag, delete, bulk-delete, and deduplicate images.\n\nEnable the API by configuring one or more API keys. The desired model:\n\n- **Read keys** (`LLM_READ_API_KEYS`) grant access to list, view, and thumbnail endpoints.\n- **Write keys** (`LLM_WRITE_API_KEYS`) grant access to delete, dedup, bulk operations, and task execution. A write key also works on read endpoints (write implies read).\n- **Legacy keys** (`LLM_API_KEYS`) function as both read and write — supported for backward compatibility but deprecated.\n\nPrefer separate read and write keys for new deployments; `LLM_API_KEYS` remains supported as a legacy all-purpose key. When explicit `LLM_READ_API_KEYS` or `LLM_WRITE_API_KEYS` are set, the legacy `LLM_API_KEYS` value is ignored.\n\n```bash\ndocker run -d --name miso-gallery \\\n  -p 5000:5000 \\\n  -v /path/to/images:/data \\\n  -e SECRET_KEY=your-session-secret \\\n  -e ADMIN_PASSWORD=your-password \\\n  -e LLM_READ_API_KEYS=gallery-read-key \\\n  -e LLM_WRITE_API_KEYS=gallery-write-key \\\n  ghcr.io/misospace/miso-gallery:latest\n```\n\nAuthenticate each request with a Bearer token:\n\n```bash\ncurl -H \"Authorization: Bearer gallery-read-key\" \\\n  http://localhost:5000/api/llm/images\n```\n\nLLM API endpoints are token-authenticated and do not require CSRF tokens. Existing browser/session authentication continues to work for the UI.\n\n### Read Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/api/llm/images?q=\u003cquery\u003e` | Recursively list/search media. `q` matches filenames or relative paths. |\n| `GET` | `/api/llm/image/\u003crelpath\u003e` | Return metadata for a single image/video. |\n| `GET` | `/api/llm/recent?limit=N` | Return recent media sorted by modification time. Default `50`, max `500`. |\n| `GET` | `/api/llm/folders` | Return folder listing with relative paths and parent folders. |\n\nExample:\n\n```bash\ncurl -H \"Authorization: Bearer gallery-read-key\" \\\n  \"http://localhost:5000/api/llm/images?q=cat\"\n```\n\nResponse shape:\n\n```json\n{\n  \"count\": 1,\n  \"images\": [\n    {\n      \"name\": \"cat.jpg\",\n      \"rel_path\": \"cats/cat.jpg\",\n      \"media_type\": \"image\",\n      \"size\": 12345,\n      \"size_human\": \"12.1 KB\",\n      \"modified\": \"2026-04-28T12:34:56Z\",\n      \"mtime\": 1777398896.0,\n      \"view_url\": \"/view/cats/cat.jpg\",\n      \"thumb_url\": \"/thumb/cats/cat.jpg\"\n    }\n  ]\n}\n```\n\n### Write Endpoints\n\n| Method | Path | Body | Description |\n|--------|------|------|-------------|\n| `POST` | `/api/llm/tags` | `{\"rel_path\":\"cats/cat.jpg\",\"tag\":\"favorite\",\"action\":\"add\"}` | Add/remove tags. Tag persistence is currently log-only. |\n| `POST` | `/api/llm/delete` | `{\"rel_path\":\"cats/cat.jpg\"}` | Move one media file to trash and clear its thumbnail cache. |\n| `POST` | `/api/llm/bulk-delete` | `{\"rel_paths\":[\"a.jpg\",\"b.jpg\"]}` | Move multiple media files to trash. |\n| `POST` | `/api/llm/dedup` | `{}` or `{\"remove\":true}` | Find duplicate media by SHA-256. Defaults to dry-run; `remove:true` moves duplicates to trash. |\n\nDelete example:\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer gallery-write-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"rel_path\":\"cats/cat.jpg\"}' \\\n  http://localhost:5000/api/llm/delete\n```\n\nDedup dry-run example:\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer gallery-write-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}' \\\n  http://localhost:5000/api/llm/dedup\n```\n\n### Optional: Server-Side Task Execution\n\nMost LLM integrations do **not** need task execution. Use the gallery-management endpoints above unless you intentionally want Miso Gallery to expose a small set of preconfigured server-side automation commands.\n\nTask execution is an optional advanced feature that reuses the existing webhook task infrastructure. It is disabled unless `WEBHOOK_ENABLED=true`, and only commands explicitly configured through `WEBHOOK_TASK_*` environment variables can be run. This can be useful for trusted maintenance or generation scripts that should run on the gallery host, but it is not required for normal LLM-to-gallery interaction.\n\n```bash\ndocker run -d --name miso-gallery \\\n  -p 5000:5000 \\\n  -v /path/to/images:/data \\\n  -e SECRET_KEY=your-session-secret \\\n  -e LLM_READ_API_KEYS=gallery-read-key \\\n  -e LLM_WRITE_API_KEYS=gallery-write-key \\\n  -e WEBHOOK_ENABLED=true \\\n  -e 'WEBHOOK_TASK_GENERATE=python3 /data/scripts/generate.py {params.prompt}' \\\n  ghcr.io/misospace/miso-gallery:latest\n```\n\nRun a configured task:\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer gallery-write-key\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"task\":\"generate\",\"params\":{\"prompt\":\"a cozy bowl of miso soup\"}}' \\\n  http://localhost:5000/api/llm/task/run\n```\n\nTask execution requires a write-scoped LLM API key. Read-scoped keys can browse gallery metadata but cannot run configured server-side tasks. Task commands run from `DATA_FOLDER`, scalar params are shell-quoted, and the timeout is controlled by `WEBHOOK_TASK_TIMEOUT` with a default of 30 seconds.\n\n## Development\n\n### Local Development\n\n```bash\n# Clone and setup\ngit clone https://github.com/misospace/miso-gallery.git\ncd miso-gallery\n\n# Create virtual environment\npython3 -m venv venv\nsource venv/bin/activate\n\n# Install dependencies\npip install -r requirements.txt\n\n# Run development server\npython app.py\n```\n\n### Building\n\n```bash\n# Build Docker image\ndocker build -t miso-gallery:latest .\n\n# Run locally\ndocker run -p 5000:5000 -v ./images:/data miso-gallery:latest\n```\n\n### Releases\n\nUse the **Manual Release** GitHub Actions workflow and enter a version like `0.4.6`. It normalizes `v0.4.6` to `0.4.6`, updates the in-app version string in `app.py`, pushes that bump to `main` through the configured bot identity, creates the plain-semver tag, and creates the GitHub release with generated notes.\n\nThe **Build** workflow (triggered by a published release) validates that `APP_VERSION` in `app.py` matches the release tag before building Docker images. If they diverge, the build fails to prevent releasing a binary with an incorrect version string.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisospace%2Fmiso-gallery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmisospace%2Fmiso-gallery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmisospace%2Fmiso-gallery/lists"}