Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Grimmory Self Hosted Library

CSecurity

Expert knowledge for setting up, configuring, and extending Grimmory — a self-hosted book library manager supporting EPUBs, PDFs, comics, Kobo sync, OPDS, and multi-user management.

81 stars
0 votes
0 copies
0 views
Added 9/19/2026
businessgojavashellbashsqlspringdockergitapidatabase

Works with

cliapi

Security Analysis

C71/100
mediumUses curl or wget to download content
criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study
criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add reason-machines/trending-skills --skill grimmory-self-hosted-library --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Grimmory Self Hosted Library?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Grimmory Self Hosted Library
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/reason-machines-grimmory-self-hosted-library/badge)](https://www.skillsdirectory.com/skills/reason-machines-grimmory-self-hosted-library)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: grimmory-self-hosted-library
description: Expert knowledge for setting up, configuring, and extending Grimmory — a self-hosted book library manager supporting EPUBs, PDFs, comics, Kobo sync, OPDS, and multi-user management.
triggers:
  - set up grimmory
  - self-hosted book library
  - grimmory configuration
  - grimmory docker setup
  - grimmory bookdrop
  - grimmory kobo sync
  - grimmory opds
  - grimmory metadata lookup
---

# Grimmory Self-Hosted Library Manager

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

Grimmory is a self-hosted application (successor to BookLore) for managing your entire book collection. It supports EPUBs, PDFs, MOBIs, AZW/AZW3, and comics (CBZ/CBR/CB7), with a built-in browser reader, annotations, Kobo/OPDS sync, KOReader progress sync, metadata enrichment, and multi-user support.

---

## Installation

### Requirements
- Docker and Docker Compose

### Step 1: Create `.env`

```ini
# Application
APP_USER_ID=1000
APP_GROUP_ID=1000
TZ=Etc/UTC

# Database
DATABASE_URL=jdbc:mariadb://mariadb:3306/grimmory
DB_USER=grimmory
DB_PASSWORD=${DB_PASSWORD}

# Storage: LOCAL (default) or NETWORK
DISK_TYPE=LOCAL

# MariaDB
DB_USER_ID=1000
DB_GROUP_ID=1000
MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE=grimmory
```

### Step 2: Create `docker-compose.yml`

```yaml
services:
  grimmory:
    image: grimmory/grimmory:latest
    # Alternative registry: ghcr.io/grimmory-tools/grimmory:latest
    container_name: grimmory
    environment:
      - USER_ID=${APP_USER_ID}
      - GROUP_ID=${APP_GROUP_ID}
      - TZ=${TZ}
      - DATABASE_URL=${DATABASE_URL}
      - DATABASE_USERNAME=${DB_USER}
      - DATABASE_PASSWORD=${DB_PASSWORD}
      - DISK_TYPE=${DISK_TYPE}
    depends_on:
      mariadb:
        condition: service_healthy
    ports:
      - "6060:6060"
    volumes:
      - ./data:/app/data
      - ./books:/books
      - ./bookdrop:/bookdrop
    healthcheck:
      test: wget -q -O - http://localhost:6060/api/v1/healthcheck
      interval: 60s
      retries: 5
      start_period: 60s
      timeout: 10s
    restart: unless-stopped

  mariadb:
    image: lscr.io/linuxserver/mariadb:11.4.5
    container_name: mariadb
    environment:
      - PUID=${DB_USER_ID}
      - PGID=${DB_GROUP_ID}
      - TZ=${TZ}
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${DB_USER}
      - MYSQL_PASSWORD=${DB_PASSWORD}
    volumes:
      - ./mariadb/config:/config
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "mariadb-admin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 5s
      retries: 10
```

### Step 3: Launch

```bash
docker compose up -d

# View logs
docker compose logs -f grimmory

# Check health
curl http://localhost:6060/api/v1/healthcheck
```

Open http://localhost:6060 and create your admin account.

---

## Volume Layout

```
./data/          # App data, thumbnails, user config
./books/         # Your book files (mounted at /books)
./bookdrop/      # Drop-zone for auto-import (mounted at /bookdrop)
./mariadb/       # MariaDB data
```

---

## Environment Variables Reference

| Variable | Description | Default |
|---|---|---|
| `USER_ID` | UID for the app process | `1000` |
| `GROUP_ID` | GID for the app process | `1000` |
| `TZ` | Timezone string | `Etc/UTC` |
| `DATABASE_URL` | JDBC connection string | required |
| `DATABASE_USERNAME` | DB username | required |
| `DATABASE_PASSWORD` | DB password | required |
| `DISK_TYPE` | `LOCAL` or `NETWORK` | `LOCAL` |

---

## Supported Book Formats

| Category | Formats |
|---|---|
| eBooks | EPUB, MOBI, AZW, AZW3 |
| Documents | PDF |
| Comics | CBZ, CBR, CB7 |

---

## BookDrop (Auto-Import)

Drop files into `./bookdrop/` on your host. Grimmory watches the folder, extracts metadata from Google Books and Open Library, and queues books for review.

```
./bookdrop/
  my-novel.epub        ← dropped here
  another-book.pdf     ← dropped here
```

Flow:
1. **Watch** — Grimmory monitors `/bookdrop` continuously
2. **Detect** — New files are picked up and parsed
3. **Enrich** — Metadata fetched from Google Books / Open Library
4. **Import** — Review in UI, adjust if needed, confirm import

Volume mapping required in `docker-compose.yml`:
```yaml
volumes:
  - ./bookdrop:/bookdrop
```

---

## Network Storage Mode

For NFS, SMB, or other network-mounted filesystems, set `DISK_TYPE=NETWORK`. This disables destructive UI operations (delete, move, rename) to protect shared mounts while keeping reading, metadata, and sync fully functional.

```ini
# .env
DISK_TYPE=NETWORK
```

---

## Java Backend — Key Patterns

Grimmory is a Java application (Spring Boot + MariaDB). When contributing or extending:

### Project Structure (typical Spring Boot layout)

```
src/main/java/
  com/grimmory/
    config/          # Spring configuration classes
    controller/      # REST API controllers
    service/         # Business logic
    repository/      # JPA repositories
    model/           # JPA entities
    dto/             # Data transfer objects
```

### REST API — Base Path

All endpoints are under `/api/v1/`:

```bash
# Health check
GET http://localhost:6060/api/v1/healthcheck

# Books
GET http://localhost:6060/api/v1/books
GET http://localhost:6060/api/v1/books/{id}
POST http://localhost:6060/api/v1/books
PUT http://localhost:6060/api/v1/books/{id}
DELETE http://localhost:6060/api/v1/books/{id}

# Shelves
GET http://localhost:6060/api/v1/shelves
POST http://localhost:6060/api/v1/shelves

# OPDS catalog (for compatible reader apps)
GET http://localhost:6060/opds
```

### Example: Querying the API with Java (OkHttp)

```java
import okhttp3.*;
import com.fasterxml.jackson.databind.ObjectMapper;

public class GrimmoryClient {

    private final OkHttpClient http = new OkHttpClient();
    private final ObjectMapper mapper = new ObjectMapper();
    private final String baseUrl;
    private final String token;

    public GrimmoryClient(String baseUrl, String token) {
        this.baseUrl = baseUrl;
        this.token = token;
    }

    public String getBooks() throws Exception {
        Request request = new Request.Builder()
            .url(baseUrl + "/api/v1/books")
            .header("Authorization", "Bearer " + token)
            .build();

        try (Response response = http.newCall(request).execute()) {
            return response.body().string();
        }
    }
}
```

### Example: Spring Boot Controller Pattern

```java
@RestController
@RequestMapping("/api/v1/books")
@RequiredArgsConstructor
public class BookController {

    private final BookService bookService;

    @GetMapping
    public ResponseEntity<Page<BookDto>> getAllBooks(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(required = false) String search) {
        return ResponseEntity.ok(bookService.findAll(page, size, search));
    }

    @GetMapping("/{id}")
    public ResponseEntity<BookDto> getBook(@PathVariable Long id) {
        return ResponseEntity.ok(bookService.findById(id));
    }

    @PostMapping
    public ResponseEntity<BookDto> createBook(@RequestBody @Valid CreateBookRequest request) {
        return ResponseEntity.status(HttpStatus.CREATED)
                .body(bookService.create(request));
    }

    @PutMapping("/{id}/metadata")
    public ResponseEntity<BookDto> updateMetadata(
            @PathVariable Long id,
            @RequestBody @Valid UpdateMetadataRequest request) {
        return ResponseEntity.ok(bookService.updateMetadata(id, request));
    }
}
```

### Example: JPA Entity Pattern

```java
@Entity
@Table(name = "books")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    private String author;
    private String isbn;
    private String format;  // EPUB, PDF, CBZ, etc.

    @Column(name = "file_path")
    private String filePath;

    @Column(name = "cover_path")
    private String coverPath;

    @Column(name = "reading_progress")
    private Double readingProgress;

    @ManyToMany
    @JoinTable(
        name = "book_shelf",
        joinColumns = @JoinColumn(name = "book_id"),
        inverseJoinColumns = @JoinColumn(name = "shelf_id")
    )
    private Set<Shelf> shelves = new HashSet<>();

    @CreationTimestamp
    private LocalDateTime createdAt;

    @UpdateTimestamp
    private LocalDateTime updatedAt;
}
```

### Example: Service with Metadata Enrichment

```java
@Service
@RequiredArgsConstructor
public class MetadataService {

    private final GoogleBooksClient googleBooksClient;
    private final OpenLibraryClient openLibraryClient;
    private final BookRepository bookRepository;

    public BookDto enrichMetadata(Long bookId) {
        Book book = bookRepository.findById(bookId)
            .orElseThrow(() -> new BookNotFoundException(bookId));

        // Try Google Books first
        Optional<BookMetadata> metadata = googleBooksClient.search(book.getTitle(), book.getAuthor());

        // Fall back to Open Library
        if (metadata.isEmpty()) {
            metadata = openLibraryClient.search(book.getIsbn());
        }

        metadata.ifPresent(m -> {
            book.setDescription(m.getDescription());
            book.setCoverUrl(m.getCoverUrl());
            book.setPublisher(m.getPublisher());
            book.setPublishedDate(m.getPublishedDate());
            bookRepository.save(book);
        });

        return BookDto.from(book);
    }
}
```

---

## OPDS Integration

Connect any OPDS-compatible reader app (Kybook, Chunky, Moon+ Reader, etc.) using:

```
http://<your-host>:6060/opds
```

Authenticate with your Grimmory username and password when prompted.

---

## Kobo / KOReader Sync

- **Kobo**: Connect via the device sync feature in Grimmory settings. The app exposes a sync endpoint compatible with Kobo's API.
- **KOReader**: Configure KOReader's sync plugin to point to your Grimmory instance URL.

---

## Multi-User & Authentication

### Local Authentication
Create users from the admin panel at http://localhost:6060. Each user has isolated shelves, reading progress, and preferences.

### OIDC Authentication
Configure via environment variables (refer to full documentation at https://grimmory.org/docs/getting-started for OIDC-specific variables such as `OIDC_ISSUER_URI`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`).

---

## Building from Source

```bash
# Clone the repository
git clone https://github.com/grimmory-tools/grimmory.git
cd grimmory

# Build with Maven
./mvnw clean package -DskipTests

# Or build Docker image locally
docker build -t grimmory:local .

# Use local build in docker-compose.yml
# Comment out 'image' and uncomment 'build: .'
```

---

## Common Docker Commands

```bash
# Start services
docker compose up -d

# Stop services
docker compose down

# View app logs
docker compose logs -f grimmory

# View DB logs
docker compose logs -f mariadb

# Restart only the app
docker compose restart grimmory

# Pull latest image and redeploy
docker compose pull && docker compose up -d

# Open a shell inside the container
docker exec -it grimmory /bin/bash

# Database shell
docker exec -it mariadb mariadb -u grimmory -p grimmory
```

---

## Troubleshooting

### Container won't start — DB connection refused
```bash
# Check MariaDB health
docker compose ps mariadb
# Should show "healthy". If not:
docker compose logs mariadb
# Ensure DATABASE_URL host matches the service name: mariadb:3306
```

### Books not appearing after BookDrop
```bash
# Verify file permissions — UID/GID must match APP_USER_ID/APP_GROUP_ID
ls -la ./bookdrop/
# Check app logs for detection events
docker compose logs -f grimmory | grep -i bookdrop
```

### Permission denied on ./books or ./data
```bash
# Set ownership to match APP_USER_ID / APP_GROUP_ID
sudo chown -R 1000:1000 ./books ./data ./bookdrop
```

### OPDS not accessible from reader app
```bash
# Confirm port 6060 is reachable from your device
curl http://<host-ip>:6060/api/v1/healthcheck
# Check firewall rules if on a remote server
```

### High memory usage
MariaDB and Grimmory together require at minimum ~512 MB RAM. For large libraries (10k+ books), allocate 1–2 GB.

### Metadata not enriching
Google Books and Open Library require outbound internet access from the container. Verify DNS and network:
```bash
docker exec -it grimmory curl -s "https://www.googleapis.com/books/v1/volumes?q=test"
```

---

## Contributing

Before opening a pull request:
1. Open an issue and get maintainer approval
2. Include screenshots/video proof and pasted test output
3. Follow backend and frontend conventions in `CONTRIBUTING.md`
4. AI-assisted code is allowed but you must run, test, and understand every line

```bash
# Run tests before submitting
./mvnw test

# Check code style
./mvnw checkstyle:check
```

---

## Links

- **GitHub**: https://github.com/grimmory-tools/grimmory
- **Docker Hub**: https://hub.docker.com/r/grimmory/grimmory
- **GHCR**: `ghcr.io/grimmory-tools/grimmory`
- **Discord**: https://discord.gg/FwqHeFWk
- **Docs**: https://grimmory.org/docs/getting-started
- **License**: AGPL-3.0

Attribution

reason-machinesreason-machines
View sourceMore from reason-machines →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Solution Architect

Designs system architecture, component specifications, and technical integration strategy. Use when: designing solutions, system architecture, technology stack, or integration approaches.

192 votes

Akorchak:Venture Assessment

Generate a comprehensive VC investment assessment report for a company

72 votes

Stock Analysis

Analyze stocks and cryptocurrencies using Yahoo Finance data. Supports portfolio management (create, add, remove assets), crypto analysis (Top 20 by market cap), and periodic performance reports (daily/weekly/monthly/quarterly/yearly). 8 analysis dimensions for stocks, 3 for crypto. Use for stock analysis, portfolio tracking, earnings reactions, or crypto monitoring.

6511 votes

Just Fucking Cancel

Find and cancel unwanted subscriptions by analyzing bank transactions. Detects recurring charges, calculates annual waste, and helps you cancel with direct URLs and browser automation. Use when: 'cancel subscriptions', 'audit subscriptions', 'find recurring charges', 'what am I paying for', 'save money', 'subscription cleanup', 'stop wasting money'. Supports CSV import (Apple Card, Chase, Amex, Citi, Bank of America, Capital One, Mint, Copilot) OR Plaid API for automatic transaction pull. Out...

6511 votes

Telegram Compose

Compose rich, readable Telegram messages using HTML formatting via direct Telegram API. Use when: (1) Sending any Telegram message beyond a simple one-line reply, (2) Creating structured messages with sections, lists, or status updates, (3) Need formatting unavailable via Clawdbot's Markdown conversion (underline, spoilers, expandable blockquotes, user mentions by ID), (4) Sending alerts, reports, summaries, or notifications to Telegram, (5) Want professional, scannable message formatting wit...

6511 votes
View all in business →