directory: fix README inaccuracies, complete flags, expand self-hosting docs

- correct /filter name-filter semantics (long/blocked names, not "no name");
  note it needs --profile-name-limit / --blocked-*-file to do anything
- list the remaining directory flags and note core chat options + --help
- add the required cabal.project.local OpenSSL step; use `cabal list-bin`
- hosting page: copy files out of the repo (directory.js is a symlink, data
  URL is a top-level const), provide /img/group.svg, promoted.json unused
- bootstrap: bot prints/creates its contact address on startup
- document --owners-group / --directory-file (+ --migrate-directory-file)
- redraw lifecycle diagram; Active->PendingUpdate on link removal vs
  Active->PendingApproval on other profile changes; SuspendedBadRoles auto-restore
This commit is contained in:
Narasimha-sc
2026-05-11 18:08:05 +00:00
parent 4d36e7be6a
commit de11021d01
+111 -74
View File
@@ -16,12 +16,16 @@ Chat bot for registering and searching groups. Superusers and admins are configu
```sh
git clone https://github.com/simplex-chat/simplex-chat
cd simplex-chat
# OpenSSL build configuration (required) — copy the file for your OS:
cp scripts/cabal.project.local.linux cabal.project.local # on macOS: scripts/cabal.project.local.mac
cabal build simplex-directory-service
```
The compiled binary is placed at:
```
dist-newstyle/build/<arch>-linux/ghc-<ver>/simplex-chat-<ver>/x/simplex-directory-service/build/simplex-directory-service/simplex-directory-service
See [docs/CONTRIBUTING.md](../../docs/CONTRIBUTING.md) for the full build setup (toolchain, OpenSSL headers, branch compatibility).
Find the compiled binary with:
```sh
cabal list-bin simplex-directory-service
```
Or run directly without installing:
@@ -35,18 +39,20 @@ cabal run simplex-directory-service -- <flags>
### Getting your contact ID (bootstrap)
`--super-users` is required and takes `CONTACT_ID:DISPLAY_NAME`. On a fresh database, use `--run-cli` with a placeholder to connect, then find your real ID:
`--super-users` is required and takes one or more `CONTACT_ID:DISPLAY_NAME` pairs. On a fresh database you don't yet know your contact ID, so start the bot with `--run-cli` and a placeholder, connect to it, then look up your real ID:
```sh
simplex-directory-service --run-cli --super-users 999:nobody --database /path/to/db
# connect from your phone, then in the bot terminal:
# on startup the bot prints (and, unless --no-address, creates) its contact address:
# "Bot's contact address is: simplex:/contact#..."
# connect to it from your SimpleX Chat app, then in the bot terminal:
/contacts # lists connected contacts by name
/i alice # shows full info for contact "alice", including their contact ID
# note the ID, then restart:
# note the ID, then restart without --run-cli:
simplex-directory-service --super-users 2:alice --database /path/to/db
```
Both `contactId` and `localDisplayName` must match exactly.
In each `--super-users` / `--admin-users` pair both `contactId` and `localDisplayName` must match the contact exactly. (A mismatched name doesn't stop the bot from sending admin notifications to that contact ID, but commands from that contact will be rejected.)
### Minimal run
@@ -56,34 +62,56 @@ simplex-directory-service \
--database /path/to/db
```
### All flags
### Flags
`simplex-directory-service` also accepts the standard SimpleX Chat core options (custom SMP/XFTP servers, `--socks-proxy`, network settings, etc.) — run `simplex-directory-service --help` for the complete list. The directory-specific options are:
| Flag | Default | Description |
|---|---|---|
| `--super-users ID:NAME[,...]` | *(required)* | Super-user contacts (comma-separated) |
| `--admin-users ID:NAME[,...]` | none | Admin-only contacts |
| `--owners-group ID:NAME` | none | Group to invite listed-group owners into |
| `-d / --database PATH` | `~/.simplex/simplex_directory_service` | DB file prefix |
| `--directory-file PATH` | none | Append-only state log |
| `--web-folder PATH` | none | Write static listing JSON + images here |
| `--no-address` | off | Skip creating/checking the bot contact address |
| `--service-name NAME` | `SimpleX Directory` | Bot display name |
| `--run-cli` | off | Run as interactive CLI (useful for bootstrap) |
| `--admin-users ID:NAME[,...]` | none | Admin-only contacts (comma-separated) |
| `--owners-group ID:NAME` | none | Group (by group ID) that owners of listed groups are invited into — automatically on listing, or via `/invite` |
| `-d / --database PATH` | `~/.simplex/simplex_directory_service` | Database file path prefix |
| `--directory-file PATH` | none | Append-only log of directory state (see [Directory state log](#directory-state-log)) |
| `--migrate-directory-file check\|import\|export\|listing` | — | Check, import (log → DB), export (DB → log), or regenerate listing files, then exit |
| `--web-folder PATH` | none | Write static listing JSON + group images here (see [Hosting the directory page](#hosting-the-directory-page)) |
| `--no-address` | off | Skip checking/creating the bot's contact address |
| `--service-name NAME` | `SimpleX Directory` | Bot display name (without `*` characters) |
| `--profile-name-limit N` | unlimited | Max display-name length allowed to connect / join groups (used by the `name` join filter) |
| `--blocked-words-file PATH` | none | Words not allowed in profiles (used by the `name` join filter and profile review) |
| `--blocked-fragments-file PATH` | none | Word fragments not allowed in profiles |
| `--blocked-extenstion-rules PATH` | none | Substitution rules that expand the blocked-words list (the flag is spelled this way in the binary) |
| `--name-spelling-file PATH` | none | Character-substitution rules for matching disguised names |
| `--captcha-generator PATH` | none | Executable that renders a captcha image; without it captchas are sent as plain text |
| `--voice-captcha-generator PATH` | none | Executable that renders a voice captcha |
| `--run-cli` | off | Run an interactive CLI alongside the bot (useful for bootstrap) |
| `-v / --version` | — | Print version and exit |
---
## Directory State Log
`--directory-file` keeps an append-only log of every change to directory state (registrations, status changes, promotions) alongside the SQLite database. It is optional but recommended for operability: it is human-inspectable and can be checked, exported, or re-imported with `--migrate-directory-file`. Without it, directory state lives only in the database.
---
## Hosting the Directory Page
The `web/` folder has a ready-to-use directory page — serve it with any static web server. No build step needed. Dark mode follows system preference. `directory.js` is a symlink to `website/src/js/directory.js` so it stays in sync.
The `web/` folder contains a ready-to-use page (`directory.html`) that renders a bot's directory as a searchable, paginated list — no build step. Dark mode follows the system preference.
The file must be served under a URL path starting with `/directory` (e.g. `http://example.com/directory.html` or `http://example.com/directory/`) — `directory.js` skips initialisation otherwise.
A few things to know before deploying it:
By default the page shows the official SimpleX directory at `https://directory.simplex.chat/data/`. To use your own bot's data instead, change this line near the bottom of `directory.js`:
```js
const simplexDirectoryDataURL = 'https://your-domain.example/data/';
```
- **Copy the files out of the repo.** `web/directory.js` is a symlink to `website/src/js/directory.js` (kept in sync with the main website), so don't edit it in place — copy `directory.html` and the *contents* of `directory.js` to your web root and edit the copy. The data URL can't be overridden from `directory.html` because `directory.js` declares it as a top-level `const`.
- **Serve it under a `/directory` path.** `directory.js` only initialises when the page path starts with `/directory` (e.g. `https://example.com/directory.html` or `https://example.com/directory/`).
- **Provide the fallback image.** Groups without a profile image (and any image that fails to load) fall back to `/img/group.svg`, resolved from the site root — copy `website/src/img/group.svg` to `<web-root>/img/group.svg`.
- **Point it at your bot's data.** Near the bottom of `directory.js`, change:
```js
const simplexDirectoryDataURL = 'https://your-domain.example/data/';
```
(the default is the official directory, `https://directory.simplex.chat/data/`). The page fetches `listing.json` from that URL and loads group images relative to it.
- **Optional:** the "Also available as a SimpleX chat bot" link in `directory.html` points at the official directory bot — update it to your bot's address.
Then run the bot with `--web-folder` pointing to that path. The bot writes `listing.json`, `promoted.json`, and group images there (refreshed every 5 minutes or on approval):
Then run the bot with `--web-folder` pointing at the folder served at that URL. The bot writes `listing.json` (used by the page), `promoted.json` (the promoted subset — written but not rendered by the bundled page), and group images, refreshing every 5 minutes and immediately when a group is approved or its listing/promotion status changes:
```sh
simplex-directory-service \
--super-users 2:alice \
@@ -106,10 +134,10 @@ The bot sends a welcome message automatically when you connect.
| Action | Syntax | Effect |
|---|---|---|
| Search | `<text>` | Returns up to 10 groups whose name or welcome message matches; sorted by member count |
| Next page | `/next` or `.` | Next page of the most recent search |
| Next page | `/next` or `.` | Next page of the most recent search (after ~5 min of inactivity, or with no recent search, falls back to listing all groups) |
| Recent groups | `/new` | Groups added most recently |
| All groups | `/all` | All listed groups, sorted by member count |
| Help | `/help [registration\|r\|commands\|c]` | Show registration or commands help (default: registration) |
| Help | `/help [registration\|r\|commands\|c]`, or `/h` | Show registration or commands help (default: registration) |
---
@@ -119,7 +147,7 @@ Registration is a three-step process — see [DIRECTORY.md](../../docs/DIRECTORY
1. Invite the directory bot to your group as `admin`.
2. Add the link the bot sends you to the group's welcome message.
3. Wait for admin approval (usually within 24 hours).
3. Wait for admin approval (usually within a day, except holidays).
If a group with the same display name is already registered (but not yet listed or suspended), the bot asks you to confirm with `/confirm`. If the name is already listed or suspended in the directory, registration is blocked.
@@ -127,7 +155,7 @@ If a group with the same display name is already registered (but not yet listed
### 3. Managing Your Groups (user commands)
> **Note on `<ID>` vs `<ID>:<name>`:** Commands shown with `<ID>[:<name>]` (`/role`, `/filter`, `/link`) accept just the ID — the name is optional. All other group commands (`/confirm`, `/delete`, and all admin/super-user commands) require both ID and name as `<ID>:<name>`. The bot provides the exact `ID:name` string in its messages so you can copy it directly.
> **Note on `<ID>` vs `<ID>:<name>`:** Commands shown with `<ID>[:<name>]` (`/role`, `/filter`, `/link`) accept just the ID — the name is optional. `/confirm`, `/delete`, and all admin/super-user commands require both, written as `<ID>:<name>`. For user commands the ID is the registration ID shown by `/list`; for admin and super-user commands it's the group ID included in the bot's admin notifications. When the bot expects an `<ID>:<name>` argument it normally quotes the whole command for you (e.g. for `/confirm` and `/approve`), so you can copy it directly; for `/delete` you build it from the ID and name shown by `/list`.
#### List groups
@@ -136,7 +164,7 @@ If a group with the same display name is already registered (but not yet listed
/ls
```
Shows all groups you have registered with their current status.
Shows all groups you have registered, with their current status, member count, and the `/role` and `/filter` commands for each.
#### Confirm duplicate name
@@ -162,16 +190,16 @@ Omit the role argument to view the current setting. `member` (default) lets new
/filter <ID>[:<name>] [preset | flags]
```
Omit the argument to view the current filter.
Omit the argument to view the current filter. Filters apply to people joining via the directory-managed link.
**Presets** (mutually exclusive):
| Preset | Effect |
|---|---|
| `off` | No filter |
| `basic` | Reject joins from profiles without images that have no name set |
| `moderate` (or `mod`) | Reject all no-name profiles; require captcha for profiles without images |
| `strong` | Reject all no-name profiles; require captcha for all profiles |
| `basic` | For profiles without an image: reject long or inappropriate names |
| `moderate` (or `mod`) | Reject long/inappropriate names from all profiles; require captcha from profiles without an image |
| `strong` | Reject long/inappropriate names from all profiles; require captcha from all profiles |
**Flags** (combine freely):
@@ -181,11 +209,13 @@ Omit the argument to view the current filter.
| Flag | Condition | Effect |
|---|---|---|
| `name` | `=all` (default) or `=noimage` | Reject joins from profiles with no name set |
| `captcha` | `=all` (default) or `=noimage` | Require captcha to join |
| `name` | `=all` (default) or `=noimage` | Reject joins from profiles whose name is too long (`--profile-name-limit`) or contains blocked words/fragments (`--blocked-words-file` / `--blocked-fragments-file`) |
| `captcha` | `=all` (default) or `=noimage` | Require the joiner to solve a captcha |
| `observer` | `=all` (default) or `=noimage` | Make new members observers instead of members |
`=noimage` means the condition applies only to profiles that have no profile image.
`=noimage` means the condition applies only to profiles that have no profile image (`=no_image` and `=no-image` are also accepted).
> The `name` filter only has an effect when the bot was started with `--profile-name-limit` and/or `--blocked-words-file` / `--blocked-fragments-file`; otherwise it does nothing. The `captcha` filter sends a much stronger challenge when `--captcha-generator` (or `--voice-captcha-generator`) is configured — without it the captcha text is sent as a plain message.
#### View or upgrade group link
@@ -218,7 +248,7 @@ Admins receive a notification whenever a group enters the PendingApproval state.
| List pending | `/pending [N]` | Show N groups awaiting approval (default: 10) |
| Message owner | `/owner <ID>:<name> <message>` | Forward a message to the group owner |
| Reject | `/reject <ID>:<name>` | *(Reserved, currently a no-op)* |
| Invite owner | `/invite <ID>:<name>` | Invite the group owner to the owners' group |
| Invite owner | `/invite <ID>:<name>` | Invite the group owner to the owners' group (requires `--owners-group`) |
---
@@ -226,7 +256,7 @@ Admins receive a notification whenever a group enters the PendingApproval state.
| Command | Syntax | Effect |
|---|---|---|
| Feature group | `/promote <ID>:<name> on\|off` | Add or remove group from promoted listing |
| Feature group | `/promote <ID>:<name> on\|off` | Add or remove group from the promoted listing (`promoted.json`) |
| Execute API command | `/exec <command>` or `/x <command>` | Run a raw SimpleX Chat API command |
---
@@ -234,38 +264,44 @@ Admins receive a notification whenever a group enters the PendingApproval state.
### 6. Group Lifecycle
```
Invited by owner
├── (unique name) ──────────────────────────┐
│ │
└── (duplicate name*) ─▶ PendingConfirmation
(/confirm) │
│ │
├─────────────┘
Proposed
PendingUpdate
(add bot link to welcome)
PendingApproval
(admin reviews)
┌───────────┴───────────┐
▼ ▼
Active (profile change**
(listed) → re-enters pending)
┌────┴─────┐
▼ ▼
Suspended SuspendedBadRoles
(by admin) (roles changed)
└────┬─────┘
Removed
Invited by owner
────────────────────────┐
unique name duplicate name*
│ PendingConfirmation
│ (/confirm)
│ │
└────────────┬─────────────┘
Proposed
│ (bot joins the group, creates the link)
PendingUpdate ◀────── (Active group, if its bot
(owner adds the link to welcome) link is removed)
PendingApproval ◀────── (Active group, on most
(admin reviews) profile changes**)
(/approve)
┌──────────────── Active ──────────────┐
│ (listed) │
▼ │ ▼
Suspended SuspendedBadRoles
(by admin; │ (bot/owner role lost;
/resume re-lists) │ auto-restored to
│ │ Active when fixed)
│ │ │
└───────────────────┼──────────────────┘
Removed
(owner /delete; owner removed from / left the
group; bot removed from group; or group deleted)
```
\* Only when the duplicate is registered but not yet listed or suspended.
@@ -274,15 +310,16 @@ If the name is already listed or suspended, registration is blocked entirely.
\*\* Profile changes only trigger re-approval when fields other than the
directory bot link are modified. If the only change is swapping the old bot
link for the new one, or changing only whitespace in the description, the
group stays Active.
group stays Active. If the link is removed entirely, the group returns to
PendingUpdate instead.
**State notes:**
- **PendingConfirmation** — bot has been invited but a group with the same display name is already registered (in a pending state). Owner must run `/confirm` to proceed. If a group with the same name is already listed or suspended, registration is blocked entirely.
- **Proposed** — name is unique (or duplicate confirmed via `/confirm`); bot is joining the group.
- **PendingUpdate** — bot has joined the group and created the group link; owner must add it to the group's welcome message.
- **PendingUpdate** — bot has joined the group and created the group link; owner must add it to the group's welcome message. An Active group also returns here if the directory bot link is later removed from the welcome message.
- **PendingApproval** — submitted for review. The `approval-id` shown to admins increments each time the group re-enters this state (e.g. after a profile change). An Active group re-enters this state when its profile is modified, unless the only change is swapping the old directory bot link for the new one or changing only whitespace in the description, in which case the group stays Active.
- **Active** — listed in directory; visible in search results.
- **Suspended** — hidden from directory by admin action; owner is notified.
- **SuspendedBadRoles** — hidden automatically when the directory bot loses its `admin` role or the registering owner loses their `owner` role in the group.
- **Removed** — delisted permanently (by owner via `/delete`, or after removal from the group).
- **Suspended** — hidden from directory by admin action (`/suspend`); restored with `/resume`; owner is notified.
- **SuspendedBadRoles** — hidden automatically when the directory bot loses its `admin` role, or the registering owner loses their `owner` role, in the group; automatically restored to Active once the roles are corrected.
- **Removed** — delisted permanently: by owner via `/delete`, when the registering owner is removed from or leaves the group, when the bot is removed from the group, or when the group is deleted. The group can be re-registered afterwards.