• Go 75.2%
  • JavaScript 7.6%
  • HTML 5.3%
  • CSS 5.3%
  • YAML 3%
  • Other 3.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-17 22:05:15 +03:00
.forgejo/workflows chore: drop checkout pin in release workflow 2026-10-13 17:14:24 +03:00
ansible chore: add schemas, rename from .golangci.yml to .golangci.yaml 2026-10-13 14:01:34 +00:00
contrib chore: update nix vendorHash 2026-10-17 22:05:15 +03:00
internal test: replace transport fakes in web tests with plain data 2026-10-17 21:52:48 +03:00
tests/browser refactor: split the app package into web and media 2026-10-17 21:41:51 +03:00
.env.example fix: return 502 from embed endpoints when upstream fails 2026-09-22 14:03:05 +03:00
.envrc Add Nix package and NixOS module 2026-01-30 10:15:22 +02:00
.gitattributes chore: fix leftovers from PR #6 2026-10-13 17:11:56 +03:00
.gitignore Add Nix package and NixOS module 2026-01-30 10:15:22 +02:00
.golangci.yaml chore: add schemas, rename from .golangci.yml to .golangci.yaml 2026-10-13 14:01:34 +00:00
build.sh refactor: split the app package into web and media 2026-10-17 21:41:51 +03:00
compose.example.yaml chore: add schemas, rename from .golangci.yml to .golangci.yaml 2026-10-13 14:01:34 +00:00
config.go refactor: split the app package into web and media 2026-10-17 21:41:51 +03:00
config_tags_test.go refactor: split the app package into web and media 2026-10-17 21:41:51 +03:00
config_test.go refactor: split the app package into web and media 2026-10-17 21:41:51 +03:00
Dockerfile refactor: split the app package into web and media 2026-10-17 21:41:51 +03:00
flake.lock chore(nix): update nixpkgs pin for Go 1.27 2026-09-07 04:01:58 +03:00
flake.nix fix(nix): inject version instead of hardcoding it 2026-09-07 03:44:51 +03:00
go.mod refactor: replace hand-written caches with otter 2026-10-17 21:20:02 +03:00
go.sum refactor: replace hand-written caches with otter 2026-10-17 21:20:02 +03:00
instances.json fix(instances): use JSON null for empty overlay networks 2026-09-19 02:25:27 +03:00
main.go refactor: split the app package into web and media 2026-10-17 21:41:51 +03:00
Makefile refactor: split the app package into web and media 2026-10-17 21:41:51 +03:00
README.md refactor: split the app package into web and media 2026-10-17 21:41:51 +03:00
renovate.json ci: auto-update nix vendorHash 2026-10-04 12:52:45 +03:00

Gelbooru-Go

A lightweight private frontend for Gelbooru written in Go with caching and security features. Besides the Go standard library it depends only on golang.org/x/net, golang.org/x/sync and the otter cache, keeping dependencies minimal. Every page works without JavaScript; the one small script (internal/web/static/app.js) adds colored tag autocomplete and ←/→ keyboard navigation.

Opening a post from a gallery page adds Previous / Next links that follow the order of that search, across result pages.

Instances

Public instances of Gelbooru-Go:

Country Address Rating
Germany gel.bloat.cat General
Chile gel.nadeko.net General

Requirements

  • Latest version of Go

Deployment

Docker Compose

Copy compose.example.yaml to compose.yaml and .env.example to .env, then edit them as needed:

cp compose.example.yaml compose.yaml
cp .env.example .env

Edit .env file with your configuration. You can obtain your API key and user ID from Gelbooru Account Options.

Then run:

docker compose up -d

Without Docker

Clone the repository:

git clone https://git.bloat.cat/vlnst/gelbooru-go && cd gelbooru-go

Set environment variables and run:

export API_KEY=your_api_key_here
export USER_ID=your_user_id_here
export PORT=3000
export POSTS_LIMIT=30
export RATING=general
export IMAGE_CACHE_MB=128
make run

You can automate this using systemd or another init system.

Behind Anubis

Enable Open Graph passthrough in the site's policy (openGraph.enabled: true). Full-image mode returns the same HTML to browsers and crawlers, including real title/artist text and full-image metadata. It also advertises /oembed?url=<post URL>&format=json and /users/<artist-id>/statuses/<id>; Discord can retrieve the accompanying /api/v1/statuses/<id> response. These read-only compatibility endpoints mirror the preview format used by PixivFE and are enabled only with FULL_IMAGE_EMBEDS=true. Artist identities are derived from credited artist tags, not uploader IDs.

Preview crawlers must also be able to retrieve the proxied image. These example rules allow common preview user agents only on post, media, and the preview discovery endpoints, and Discord's media proxy (which plays embed videos with a browser-like user agent) only on embed media URLs. Add them before generic challenge/deny rules in a policy scoped to this site. User-agent matching is spoofable: a client faking one of these user agents also skips the challenge for /proxy. Other requests retain the configured Anubis protection. Existing previews may remain cached by the sharing app.

Configuration

Required Variables

Optional Variables

  • PORT - Server port (default: 3000)
  • POSTS_LIMIT - Posts per page, 1-100 (default: 30)
  • RATING - Maximum allowed rating (default: general)
  • IMAGE_CACHE_MB - Memory budget for the image cache in megabytes, 0 disables it (default: 128)
  • FULL_IMAGE_EMBEDS - Use PixivFE-style rich previews: original image, post title, credited artist, rating, score, and dimensions, with oEmbed and Mastodon-compatible discovery (default: false). Video posts include video metadata and a preview image. Embed appearance depends on the sharing app.
  • TAGS_ON_LEFT - Show grouped post tags in a left sidebar instead of the collapsed Tags section (default: false). On narrow screens, the sidebar appears below the image.

Browser preferences

The search field suggests tags as you type; use arrow keys and Enter or click to select. Negative tags retain their - prefix. Suggestions are fetched through this server.

Use -tag in the search field to exclude posts with that tag. JavaScript is required for autocomplete and arrow-key navigation. Search, filters, pagination, media, and grouped tag links are server-rendered and work with JavaScript disabled, regardless of FULL_IMAGE_EMBEDS or TAGS_ON_LEFT. JavaScript-only controls appear after the script initializes. A reverse proxy such as Anubis may independently require JavaScript to pass its challenge.

Post tags are grouped and colored by Artist, Copyright, Character, General, and Metadata, then alphabetized within each group. Categories share the cached upstream post-page response used for comments and artist metadata. Unavailable or unknown categories appear under Other, preserving every tag.

Rating System

Ratings follow a hierarchy: general → sensitive → questionable → explicit

RATING sets the highest rating this instance lets visitors select; it does not choose the ratings shown in every search. For example, RATING=explicit lets visitors choose general, sensitive, questionable, and explicit, while RATING=questionable makes only general, sensitive, and questionable available.

Visitors can check one or more ratings in Ratings. The gallery includes only those ratings. The selection is saved in a cookie and included in page links, so it stays active while browsing and can be shared. A new visitor starts with general selected. With RATING=general there is nothing to choose, so the Ratings group is not shown at all.

Performance & Security

Caching

  • API responses: Cached for 5 minutes
  • Images: Cached for 60 minutes
  • Large files (>10MB) and videos are streamed instead of cached

Security Features

  • Security headers: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection
  • Request deduplication: Multiple concurrent requests for the same post share a single API call (singleflight pattern)
  • URL validation: Proxy endpoint only allows Gelbooru domains
  • Content-Type validation: Only allowed media types are served
  • Size limits: Maximum image size 50MB, URL length 2048 chars, tags length 500 chars

Contributing

This project follows Semantic Versioning and uses Conventional Commits for commit messages.

Commit Message Format

Follow the Conventional Commits specification:

  • fix: - Bug fixes (patch version bump)
  • feat: - New features (minor version bump)
  • feat!: or fix!: - Breaking changes (major version bump)
  • docs: - Documentation changes
  • style: - Code style changes (formatting, etc.)
  • refactor: - Code refactoring
  • perf: - Performance improvements
  • test: - Test additions or changes
  • chore: - Other changes (build, config, etc.)

Examples:

  • fix: resolve memory leak in cache cleanup
  • feat: add support for custom rating filters
  • feat!: change API response format

Code Quality

Before submitting code:

  1. Format code: go fmt
  2. Run linter: golangci-lint run