Dokky 2.5.0: Less Noise, More Substance (and AI Done Right)

 

Some releases just stack up features. Others fundamentally improve how you actually use a product every single day.
Dokky 2.5.0 belongs firmly in the second category.

Following months of intense development, this version doesn’t try to wow you with gimmicks. Instead, it does something much harder: it makes the entire platform smarter, faster, and more aligned with the project’s core vision.

Dokky Suite - Features
Dokky Suite – Features

Smart AI with a Real Purpose

No automated audiobooks or flashy, useless features here.
Dokky Suite now analyzes the pages of public PDFs to generate an ultra-compact summary (two sentences, straight to the point) alongside a locally saved audio version. It’s clean, local, and essential.
The goal is simple: grasp what a document is about in seconds.

Searching for something? Get an instant preview.
Don’t want to read the whole thing? Listen to a quick audio snippet.
Need accessibility support? Enjoy built-in, practical assistance.

Dokky Suite - Text to Speech PDF

Best of all, this won’t turn Dokky into an expensive service. The integration is designed to be sustainable and virtually free, requiring only an external API key (set up in literally just a few clicks).
Privacy First: This feature is strictly confined to public documents. Private and group contents remain completely untouched. It’s one of those subtle features you don’t notice at first, but after a week, you’ll wonder how you ever managed without it.

Private Groups: Less Chaos, Total Control

Dokky Suite - Private Chats
Dokky Suite – Private Chats

Behind the scenes, one of the most impactful upgrades is the new group chat system.
Yes, it’s now powered by SQLite, making it significantly faster and more responsive. But performance is only half the story; every file within a group now gets its own dedicated database. This architecture ensures:

– Isolated discussions: Zero cross-context noise or leaks.
– Clean scalability: Smooth performance as your library grows.
– Keyword search: Finally, full-text search comes to group chats.

Type a keyword, view the relevant messages, and jump instantly to that exact point in the conversation. No more endless scrolling. No more wondering “who said what?”.

Natural Collaboration & Under-the-Hood Cleanup

Inline annotations on group PDFs are now sleeker and more visually polished. It might seem like a detail, but when collaborating on shared documents, the user experience (UX) makes all the difference. The Groups dashboard notification system has also been streamlined to deliver instant context with fewer distractions.

On the infrastructure side, we’ve tackled key optimizations, including proper IPFS unpinning during document deletion. File lifecycles are now clean, predictable, and transparent for both admins and users.

Add to that a refined sidebar, database upgrades, translation updates, and codebase optimizations. Combined, they bridge the gap between “it works” and “it works flawlessly.”
A Real-World Scenario? Imagine you have a large library of public PDFs. You search for a specific topic. Instead of opening random files, Dokky lets you instantly read or listen to a summary.

Meanwhile, your Team collaborates in private groups:

– Every document has its own organized, searchable chat.
– Inline notes make feedback direct and visual.
– Notifications keep you in the loop without cluttering your screen.

Everything is exactly where it should be. And crucially, it’s fast.
Dokky Suite 2.5.0 isn’t a loud release. It’s a deliberate one:

– AI is used only where it adds genuine value.
– Chat becomes a productivity tool, not a distraction.
– Infrastructure becomes simpler yet significantly more powerful.

Most importantly, it stays 100% faithful to the philosophy behind the project: total control, self-hosting, and long-term sustainability.

Take a look at Dokky Live Demo
Buy Dokky Suite with 20% off

Why You Should Stop Using Imagick for PDF Previews (And What to Do Instead)

 

If you manage a web application where users upload documents, you’ve undoubtedly run into that classic Friday afternoon nightmare: a user uploads a heavy, complex PDF, and the server suddenly crashes due to memory exhaustion.

Nine times out of ten, the culprit is Imagick. But how did we end up relying so heavily on this module, and why has it become such a massive bottleneck today? Let’s take a step back to understand the root of the problem.

A Little History: Why Did We Always Use Imagick?

In the PHP ecosystem, image manipulation has historically been handled by the GD library. It is lightweight, fast, and pre-installed on virtually every hosting environment. So why did everyone choose Imagick for handling PDFs?

The answer lies in how files are structured:

  • The GD library only understands pixels: It was built to manipulate raster formats (PNG, JPEG, GIF). It doesn’t have the slightest clue how a PDF file is put together.
  • PDF is a vector format (and more): A PDF contains geometric coordinates, embedded fonts, text layers, and vector paths. Turning a PDF page into a JPEG requires a highly complex rasterization engine.

This is where ImageMagick (via the PHP Imagick extension) stepped in. It historically delegated this heavy lifting to Ghostscript, a powerful PostScript interpreter installed at the OS level. For years, this was the only viable path: PHP passed the PDF to Imagick, Ghostscript chewed through it on the server, and spat out the first page as a JPEG.

The Hidden Cost on Your Server

While this server-side approach works, it introduces three major pain points in Production Environments and SHared Hosting:

  1. Devastating Resource Consumption: Rasterization via Ghostscript is incredibly RAM and CPU intensive. If multiple users upload files simultaneously, your server’s resources evaporate instantly.
  2. System Instability: Configuring and maintaining Ghostscript on Linux servers can be a security nightmare, especially given historical vulnerabilities that often force hosting providers to block PDF reading entirely.
  3. Synchronous User Experience: The user has to sit and wait for the heavy server-side processing to finish before they can even see a thumbnail, resulting in frustratingly long loading times.

The Modern Solution: “Client-Driven” Architecture

Modern browsers possess incredible computing power. Why not offload the heavy lifting to the client?

By leveraging PDF.js (Mozilla’s brilliant open-source library that powers Firefox’s native PDF viewer), we can make the user’s browser download the PDF, render the first page inside an HTML5 <canvas>, and generate the image locally.

Once generated, the preview image is sent back to the server via a simple asynchronous AJAX request. The PHP backend performs zero rendering calculations; it just receives a ready-to-go JPEG and saves it. Minimal effort, maximum efficiency.

1. The Frontend (JavaScript)

The strategy is to intercept the rendering of the first page using PDF.js, extract the Blob from the canvas, and POST it to our backend.


// Rendering the first page on a Canvas using PDF.js
pdfDoc.getPage(1).then(page => {
    const viewport = page.getViewport({ scale: 1.0 });
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    
    canvas.width = viewport.width;
    canvas.height = viewport.height;

    const renderContext = { canvasContext: context, viewport: viewport };
    
    // Execute visual rendering on the canvas
    page.render(renderContext).promise.then(() => {
        
        // THE TRICK: Convert the canvas into a compressed JPEG on the client side
        canvas.toBlob(function(blob) {
            if (!blob) return;

            // Prepare the payload for the async request
            const formData = new FormData();
            formData.append('file_id', '12345'); // Indicative file ID
            formData.append('my_lazy_preview', blob, 'lazy_preview.jpg');

            // Send the preview to the server (Lazy Preview)
            fetch('ajax-save-lazy-preview.php', {
                method: 'POST',
                body: formData
            })
            .then(r => r.json())
            .then(data => console.log("Preview saved successfully!", data))
            .catch(err => console.error("Network error", err));
            
        }, 'image/jpeg', 0.85); // 0.85 strikes the perfect balance between quality and file size
    });
});

2. The Backend (PHP)

Because the heavy computational work happened in the user’s browser, our PHP script becomes incredibly lightweight and secure. No Imagick required, no Ghostscript needed. Just native, standard PHP functions:


<?php
// ajax-save-lazy-preview.php

header('Content-Type: application/json');

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['my_lazy_preview'])) {
    $fileId = intval($_POST['file_id'] ?? 0);
    $file = $_FILES['my_lazy_preview'];

    // Basic security validation (extension and size)
    $allowedMime = ['image/jpeg', 'image/jpg'];
    if (!in_array($file['type'], $allowedMime) || $file['size'] > 2 * 1024 * 1024) {
        echo json_encode(['success' => false, 'error' => 'Invalid file type or file too large']);
        exit;
    }

    // Destination path (indicative)
    $targetDir = __DIR__ . "/previews/";
    $targetFile = $targetDir . "preview_" . $fileId . ".jpg";

    // Store the image directly without reprocessing it
    if (move_uploaded_file($file['tmp_name'], $targetFile)) {
        // Optional: Update your database here to flag that this file now has a preview
        echo json_encode(['success' => true, 'message' => 'Preview saved!']);
    } else {
        echo json_encode(['success' => false, 'error' => 'Failed to save file']);
    }
    exit;
}

echo json_encode(['success' => false, 'error' => 'Invalid request']);

Done! 🥳


Handling Edge Cases Safely: Do you know Dokky Script?

Dokky Suite - Self Hosted Document management and much more
Dokky Suite – Self Hosted Document management and much more

Implementing this workflow from scratch works perfectly for smaller, straightforward projects. However, if you are building an Ecosystem centered around Documentation or complex file management, tedious edge cases will eventually pop up: What if the user scrolls through pages too quickly? How do we prevent browser memory leaks when handling 500-page PDFs? How do we handle secure token-based streaming?

If you are developing a document-centric system and need a production-ready, highly optimized solution, it’s worth looking into Dokky script.

Dokky is a Self Hosted PHP script specifically engineered for Managing Documentation. It natively integrates this exact Lazy Preview mechanism, using an IntersectionObserver to monitor visible pages, dynamically unloading distant ones to free up client RAM, and seamlessly automating async preview generation directly to your backend.

Instead of manually wrestling with canvas lifecycles and server-side configurations, you can explore the Dokky Live Demo to see it in action, or check out the Project Description Page to see how it can slot cleanly into your current development stack.

How to Display PDFs from IPFS on the Web (Without Losing Your Mind Over Gateways)

 

If you have ever tried to integrate IPFS (InterPlanetary File System) into a traditional web application, you’ve likely stumbled upon the worst-kept secret of decentralization: public gateway latency.

The core concept is brilliant: you upload a PDF, get a unique and immutable CID (Content Identifier), and you’re good to go. The file is secure, distributed, and tamper-proof. But the moment you need to pass that file, for exampleto a browser-based PDF viewer, a major hurdle arises: Which public gateway will respond first? And what if that specific gateway is down today?

How to Render PDFs from IPFS on the Web
How to Render PDFs from IPFS on the Web

Today, we’ll explore how I solved this issue on the backend, optimizing performance using a parallel gateway racing system and smart caching.

The Problem: The IPFS Gateway Lottery

To display an IPFS file inside an iframe or a JavaScript viewer, we need a public HTTP gateway (such as Cloudflare, Pinata, or Web3.Storage) to translate the IPFS protocol into a standard https:// URL.

The catch is that public gateways are highly unpredictable:

  • A gateway might be lightning-fast right now and painfully slow five minutes later.
  • Some will time out if the file hasn’t fully propagated across the network yet.
  • Hardcoding a single gateway introduces a single point of failure (SPOF).

The solution? Pit the gateways against each other.
First one to respond wins.

Architecture of the Solution (In a Nutshell…)

To guarantee maximum loading speeds for the Document Viewer, we have implemented a three-step strategy:

Session Memory: It remembers the last gateway that responded successfully during the user’s active session.
Parallel Gateway Racing: If the preferred gateway fails or lags, it queries a list of alternative gateways simultaneously using asynchronous HTTP requests.
Smart Caching & Fallback: It caches the winning URL (e.g., via SQLite or JSON) to avoid re-running the race on every page refresh. If the entire IPFS network is unreachable, it seamlessly falls back to a locally stored file.

Implementation: The curl_multi_init Trick

Here is a conceptual Snippet showing how to implement parallel checks in PHP. Instead of testing gateways sequentially (which would take ages), we fire off HEAD requests all at once.
PHP


// A list of public gateways to put to the test
$default_gateways = [
    'https://gateway.pinata.cloud/ipfs',
    'https://cloudflare-ipfs.com/ipfs',
    'https://w3s.link/ipfs'
];

function checkIPFSGatewayParallel($cid, $gateways) {
    $mh = curl_multi_init();
    $chs = [];

    // Prepare asynchronous requests (HEAD only, to keep it lightning fast)
    foreach ($gateways as $gateway) {
        $url = rtrim($gateway, '/') . '/' . $cid;
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_NOBODY, true); // Don't download the PDF, just look for a 200 OK
        curl_setopt($ch, CURLOPT_TIMEOUT, 2);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        
        curl_multi_add_handle($mh, $ch);
        $chs[$gateway] = $ch;
    }

    // Execute handles in parallel
    $active = null;
    do {
        $mrc = curl_multi_exec($mh, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

    $found_url = false;
    // ... descriptor read loop ...
    // The first gateway to return HTTP Code 200 breaks the loop and wins the race!
    
    return $found_url;
}

Why is this approach so efficient?

By setting CURLOPT_NOBODY = true, we issue a HEAD request. We aren’t downloading the actual PDF (which could be several megabytes); we are merely asking the gateway: “Hey, do you have this CID ready to serve?“. The first one to reply with a 200 OK wins the right to serve the file to our viewer.

Here the Global Logical Flow; within the application lifecycle, verifying the CID follows an optimized pipeline:

1 – Cache Check: Has this CID been linked to a working gateway in the last hour? If yes, use it.
2 – Fast Track: Try the gateway stored in the user’s session ($_SESSION[‘preferred_gateway’]). Estimated latency: just a few milliseconds.
3 – The Race: If the Fast Track fails, the parallel race described above triggers. The winner is saved to both the session and the cache.
4 – Hard Fallback: If the IPFS network is experiencing temporary congestion, the system switches to the local file system (uploads/file.pdf), ensuring the user always sees their document.

Once the definitive URL is fetched (whether from IPFS or local storage), you simply pass it to your client-side viewer (like an iframe or to a webpage instance), and you are good to go.

Want to Skip the Headache?

Dealing with cURL timeouts, configuring caching databases, managing local fallbacks, and tweaking viewers to avoid CORS issues with IPFS gateways can quickly turn into a micro-management nightmare.

If you want to integrate an immutable, decentralized, and blazing-fast Document Management System into your projects without writing, testing, and maintaining all this backend boilerplate… well, there’s Dokky Suite. It handles exactly this (and a whole lot more) natively, with just one click. 😉