# Idno: an IndieWeb social publishing platform with a real event queue

> Idno is a PHP social publishing platform built around IndieWeb and ActivityPub protocols, and its most operationally interesting feature is the asynchronous event queue that moves Webmention pings, syndication and ActivityPub delivery out of the web request and into a background worker. The current release is 1.6.4 from 2026-03-01 and the last push was on 2026-09-09.

**idno/idno** — A social publishing platform.

- Repository: https://github.com/idno/idno
- Website: https://withknown.com/opensource
- Stars: 1,125 · Forks: 197
- Language: PHP
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/idno-idno

## A publishing platform rather than a social network, and the difference shows

Idno is described as a social publishing platform, and the repository topics name the territory it occupies: cms, cms-framework, indieweb, php, social-network.

The distinction from a hosted social network is the point. In a network like this one, your posts live on your own domain, and the network protocols are how other people's servers find out about them. The ActivityPub protocol is the federation layer, and Webmention is the older mechanism for sending a notification when you link to someone's page. Idno implements both, which is why the project topics carry the indieweb label.

The trade-off runs through the whole design. Owning your content means owning the operation of a PHP application, a database, a queue worker and, for federation to work, an activity server that other servers can reach. Idno's documentation is honest about this and leads with the install requirements: PHP 8.3 or newer with selected extensions, together with a supported database backend.

The repository layout shows a full application rather than a library. There is an `Idno/` directory for the core, `IdnoPlugins/` for the plugin system, `ConsolePlugins/` for the command line plugins, `Themes/` for the theme layer, `templates/`, `configuration/`, `docker/`, `docs/`, `Tests/`, `languages/` for translations and `Uploads/` for user files. There is a `composer.json` for the PHP dependencies, a `Gruntfile.js` and a `package.json` for the JavaScript and CSS build, a `mkdocs.yml` for the documentation, and separate `version.idno` and `version.known` files, which reflect that the project was previously called Known and still carries that name in places.

## By default everything happens inside the request that triggered it

The most useful thing in this README is the explanation of how the event system works, because the default is a decision with consequences.

By default, Idno processes events synchronously. Webmention pings, syndication and ActivityPub delivery all happen inside the web request that triggered them. The documentation states plainly what that costs: it makes page saves slow, and a timeout or crash loses the work.

That is a fair characterisation of the synchronous approach. A federated social platform sending outbound HTTP requests to remote servers, during a request whose user is waiting for a page to save, has a latency profile determined by the slowest remote server on the internet. A remote host that is slow to respond holds your page load open.

The alternative is the asynchronous queue. Enabling it moves all of that into a background worker. The web request drops an event into the database and returns immediately, and the worker picks it up and processes it separately. The failure mode improves too: the documentation notes that if the worker dies, queued events pile up in the database but are not lost, and they will be processed once the worker is restarted.

So the choice is not between working and not working. It is between a single-process application that is simple and fragile under latency, and a two-part system that is more moving parts and far more predictable. For a site with a handful of readers, the synchronous default is fine. For a site that federates with real traffic, the queue is not optional, and the documentation says so in unusually direct terms in the next section.

## ActivityPub does not work at all without the worker running

The documentation states that the asynchronous queue is required for ActivityPub, and this is the single most important operational fact about the project.

Follow-accept delivery, post distribution to followers, and update and delete propagation are all dispatched through the queue. If the worker is not running, those events sit in the database unprocessed and remote servers will never receive them.

That is a different kind of failure from the synchronous mode, and it is worth being precise about. A synchronous deployment that fails to deliver loses a delivery. An asynchronous deployment with a stopped worker silently accumulates a backlog, and from the outside the site looks fine, posts are saved, the pages render, and nothing arrives anywhere. The queue is enabled by one line in the configuration file:

```ini
event_queue = 'AsynchronousQueue'
```

Without that line, or with the default `SynchronousQueue`, all events are processed inline during the web request.

The worker itself is run from the command line, and the invocation carries the domain as an environment variable:

```bash
sudo -u www-data IDNO_DOMAIN='your.domain' ./idno.php service-event-queue
```

Two options are documented. `--queue` selects a named queue to process and defaults to `default`, and `--interval` sets the seconds to sleep between polling cycles, defaulting to one. The cycle mechanics are described in full: the worker runs an infinite loop, and each cycle it queries the database for up to 50 pending events, dispatches each one via an internal HTTP call to `/service/queue/dispatch/{id}` which triggers the event handler, runs garbage collection to remove completed events older than five minutes, and then sleeps for the interval before repeating.

That dispatch through an internal HTTP call is a design choice worth noting. The worker does not call the handlers in process, it makes a request to the application itself, which means the handler runs in the same request context a web request would, with the same configuration, and the worker is only a scheduler. It also means the application has to be reachable over HTTP for the queue to drain, which is a deployment constraint worth checking on a locked-down host.

## systemd or cron, and both workers need restarting after an update

The documentation recommends keeping the worker alive with systemd, and the unit file it gives is worth reading closely because it encodes the operational requirements.

```ini
[Unit]
Description=Idno async event queue worker
After=network.target

[Service]
Type=simple
User=www-data
Environment=IDNO_DOMAIN=your.domain
WorkingDirectory=/var/www/idno
ExecStart=/var/www/idno/idno.php service-event-queue #replace with your path
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
```

The unit runs as `www-data`, sets the domain through the environment, and specifies the working directory, which matters because the worker is reading the site's configuration from that location. `Restart=always` with `RestartSec=5` is what turns the crash-and-lose-nothing property into an automatic recovery, and `After=network.target` handles the ordering against the network coming up.

Enabling it is two commands, `sudo systemctl daemon-reload` and `sudo systemctl enable --now idno-queue`, and status is checked with `sudo systemctl status idno-queue` and `sudo journalctl -u idno-queue -f` to tail the logs.

The alternative is cron, for hosts where systemd is not available, such as shared hosting. The `--once` flag makes the worker process one batch of pending events and exit, which is what makes it cron-shaped:

```
* * * * * sudo -u www-data IDNO_DOMAIN='your.domain' /var/www/idno/idno.php service-event-queue --once >> /var/log/idno-queue.log 2>&1
```

The trade-off is stated as well: this processes pending events once per minute, and events may take up to 60 seconds to be delivered, compared to about one second with the systemd worker.

A second worker exists for periodic background tasks, triggered through `cron/minute`, `cron/hourly` and `cron/daily` events, started the same way with `./idno.php service-cron`. And there is an operational step that is easy to miss, because it is mentioned in a note rather than a heading: when you update Idno core or any plugins, restart both services so they pick up the new code, with `sudo systemctl restart idno-queue idno-cron`.

## Installing it is a composer project, a git checkout or a package

There are three installation routes, and the one you pick determines how you get updates.

The cleanest is Composer. You can install Idno directly with `composer create-project idno/idno`, and optionally install the bleeding edge code the same way with `composer create-project idno/idno -s dev`. The `-s dev` flag matters because the default branch of the repository is `dev`, so the stable route and the development route are different branches of the same project.

The git route is for working on the code itself. You check out the repository with `git clone https://github.com/idno/idno.git` and then fetch dependencies with `cd idno; composer install`.

The third route is packages, and it comes with a caveat in the documentation: unofficial install packages, which are periodically built from the latest code, are available from the maintainer's site. Unofficial is the operative word, so for a production site the Composer route with a pinned version is the safer choice.

The full instructions are in the project documentation at docs.idno.co, and the repository is described as under active development.

There is also a Docker directory in the repository, which is worth checking before you build your own container setup, and the `warmup/` directory at the top level, whose name suggests it handles the first-run setup work. The `Uploads/` directory is where user-uploaded content lives, which is worth noting for anyone planning a backup strategy, and the `docker/` and `.dockerignore` files sit alongside it.

The build tooling is a two-language setup. Composer handles the PHP side, and the JavaScript and CSS side is built with Grunt: the `package.json` shows grunt, babel, sass, terser, modernizr, and the linting tools including stylelint with a sass guidelines config, csslint and jshint, plus a `postinstall` script that runs `opencollective-postinstall`.

## Where Idno is the wrong tool

The first case is a team that needs a hosted platform with moderation, abuse handling and multi-tenancy. Idno is a single-tenant application that one person or a small group runs on their own domain. There is no admin console for handling reports, no tenant isolation, no role hierarchy beyond what a plugin adds, and the operational surface is your own server.

The second case is a site that publishes rarely and wants federation to be a background detail. If you post a few times a year, the synchronous default is the simplest possible configuration and the queue worker is an operational burden you gain nothing from. The cost of the queue is a long-running process, a systemd unit, a cron alternative and a restart step after every update, and that is only justified when the volume or the latency requirements justify it.

The third case is a project that has already committed to another IndieWeb implementation, or to a static site with a build step. Idno is a database-backed application with a plugin architecture, a theme layer and a web interface. If your publishing model is a set of Markdown files in a repository, a static generator does the same job with none of the runtime, and there is no queue worker to keep alive.

The licensing point is worth reading carefully rather than assuming. The code is licensed under the Apache Software License 2.0 unless otherwise stated, and the project metadata records the licence as a custom one that GitHub cannot classify, so the LICENSE file is the authoritative text. The documentation adds two further points. The logos are the property of Idno, Inc, and permission from Idno, Inc is required to use the Idno name or logo as part of any project, product, service, domain or company name, except as included in official themes distributed by Idno. And third party libraries are licensed separately, with the list naming Bootstrap under Apache 2.0, jQuery and portions of Symfony under MIT, and ToroPHP under MIT.

## Conclusion

Idno fits a person or small group who wants their own domain to be the canonical home of their writing, with federation to Mastodon and other ActivityPub servers handled properly rather than bolted on. It does not fit a team that needs a hosted platform, a moderation queue or a multi-tenant deployment. Before you install it, set the asynchronous queue up first, because the documentation states that ActivityPub delivery requires the worker to be running, and read the LICENSE file, since the code is Apache-2.0 but the project separately reserves the Idno name and logo.

## FAQ

### How do I install Idno?

The simplest route is `composer create-project idno/idno`, or `composer create-project idno/idno -s dev` for the bleeding edge code, since the default branch is `dev`. You can also clone the repository with `git clone https://github.com/idno/idno.git` and run `composer install`. Idno requires PHP 8.3 or newer with selected extensions and a supported database backend.

### Why does Idno need the asynchronous event queue for ActivityPub?

Follow-accept delivery, post distribution to followers and update or delete propagation are all dispatched through the queue, so if the worker is not running those events sit in the database unprocessed and remote servers never receive them. You enable the queue by adding `event_queue = 'AsynchronousQueue'` to `config.ini`.

### How do I run the Idno event queue worker under systemd?

Create a systemd unit that runs `/var/www/idno/idno.php service-event-queue` as `www-data` with `IDNO_DOMAIN` set in the environment, then run `sudo systemctl daemon-reload` and `sudo systemctl enable --now idno-queue`. The `--interval` option sets the seconds between polling cycles and `--queue` selects a named queue, both defaulting to one second and the `default` queue.

### Can Idno run the queue on shared hosting without systemd?

Yes. The `--once` flag makes the worker process one batch of pending events and exit, which fits a cron entry, and the documentation notes this can take up to 60 seconds for delivery compared with about a second under systemd. A second worker, `service-cron`, handles the periodic minute, hourly and daily background tasks.

## Sources

- [idno/idno on GitHub](https://github.com/idno/idno)
- [Issues](https://github.com/idno/idno/issues)
- [Project website](https://withknown.com/opensource)
- [README](https://github.com/idno/idno/blob/dev/README.md)
- [Releases](https://github.com/idno/idno/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/idno-idno
