diff --git a/.github/glyphs/bolt.svg b/.github/glyphs/bolt.svg
new file mode 100644
index 0000000..bf8e109
--- /dev/null
+++ b/.github/glyphs/bolt.svg
@@ -0,0 +1 @@
+
diff --git a/.github/glyphs/broadcast.svg b/.github/glyphs/broadcast.svg
new file mode 100644
index 0000000..293d112
--- /dev/null
+++ b/.github/glyphs/broadcast.svg
@@ -0,0 +1 @@
+
diff --git a/.github/glyphs/database.svg b/.github/glyphs/database.svg
new file mode 100644
index 0000000..1aa500b
--- /dev/null
+++ b/.github/glyphs/database.svg
@@ -0,0 +1 @@
+
diff --git a/.github/glyphs/document.svg b/.github/glyphs/document.svg
new file mode 100644
index 0000000..5468e9c
--- /dev/null
+++ b/.github/glyphs/document.svg
@@ -0,0 +1 @@
+
diff --git a/.github/glyphs/elephant.svg b/.github/glyphs/elephant.svg
new file mode 100644
index 0000000..9080985
--- /dev/null
+++ b/.github/glyphs/elephant.svg
@@ -0,0 +1 @@
+
diff --git a/.github/glyphs/leaf.svg b/.github/glyphs/leaf.svg
new file mode 100644
index 0000000..5951662
--- /dev/null
+++ b/.github/glyphs/leaf.svg
@@ -0,0 +1 @@
+
diff --git a/.github/glyphs/mail.svg b/.github/glyphs/mail.svg
new file mode 100644
index 0000000..d90a43e
--- /dev/null
+++ b/.github/glyphs/mail.svg
@@ -0,0 +1 @@
+
diff --git a/.github/glyphs/queue.svg b/.github/glyphs/queue.svg
new file mode 100644
index 0000000..818b832
--- /dev/null
+++ b/.github/glyphs/queue.svg
@@ -0,0 +1 @@
+
diff --git a/.github/glyphs/search.svg b/.github/glyphs/search.svg
new file mode 100644
index 0000000..e3af19f
--- /dev/null
+++ b/.github/glyphs/search.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/adminer.svg b/.github/logos/adminer.svg
new file mode 100644
index 0000000..c2ce025
--- /dev/null
+++ b/.github/logos/adminer.svg
@@ -0,0 +1,3 @@
+
diff --git a/.github/logos/beanstalkd.svg b/.github/logos/beanstalkd.svg
new file mode 100644
index 0000000..3a38252
--- /dev/null
+++ b/.github/logos/beanstalkd.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/clickhouse.svg b/.github/logos/clickhouse.svg
new file mode 100644
index 0000000..4545fe2
--- /dev/null
+++ b/.github/logos/clickhouse.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/elasticsearch.svg b/.github/logos/elasticsearch.svg
new file mode 100644
index 0000000..01be8f8
--- /dev/null
+++ b/.github/logos/elasticsearch.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/elasticvue.svg b/.github/logos/elasticvue.svg
new file mode 100644
index 0000000..eea2015
--- /dev/null
+++ b/.github/logos/elasticvue.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/gotenberg.svg b/.github/logos/gotenberg.svg
new file mode 100644
index 0000000..bf4303f
--- /dev/null
+++ b/.github/logos/gotenberg.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/kafka-ui.svg b/.github/logos/kafka-ui.svg
new file mode 100644
index 0000000..2d1e147
--- /dev/null
+++ b/.github/logos/kafka-ui.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/kafka.svg b/.github/logos/kafka.svg
new file mode 100644
index 0000000..058f352
--- /dev/null
+++ b/.github/logos/kafka.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/localstack.svg b/.github/logos/localstack.svg
new file mode 100644
index 0000000..33aebf6
--- /dev/null
+++ b/.github/logos/localstack.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/.github/logos/mariadb.svg b/.github/logos/mariadb.svg
new file mode 100644
index 0000000..33c124c
--- /dev/null
+++ b/.github/logos/mariadb.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/memcached.svg b/.github/logos/memcached.svg
new file mode 100644
index 0000000..ba9180f
--- /dev/null
+++ b/.github/logos/memcached.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/mercure.svg b/.github/logos/mercure.svg
new file mode 100644
index 0000000..16770d6
--- /dev/null
+++ b/.github/logos/mercure.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/.github/logos/mongo-express.svg b/.github/logos/mongo-express.svg
new file mode 100644
index 0000000..c4f0721
--- /dev/null
+++ b/.github/logos/mongo-express.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/mongo.svg b/.github/logos/mongo.svg
new file mode 100644
index 0000000..cc12450
--- /dev/null
+++ b/.github/logos/mongo.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/opensearch-dashboards.svg b/.github/logos/opensearch-dashboards.svg
new file mode 100644
index 0000000..eea2015
--- /dev/null
+++ b/.github/logos/opensearch-dashboards.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/opensearch.svg b/.github/logos/opensearch.svg
new file mode 100644
index 0000000..17e55cc
--- /dev/null
+++ b/.github/logos/opensearch.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/pgadmin.svg b/.github/logos/pgadmin.svg
new file mode 100644
index 0000000..a1fd5d3
--- /dev/null
+++ b/.github/logos/pgadmin.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/phpmyadmin.svg b/.github/logos/phpmyadmin.svg
new file mode 100644
index 0000000..e1693c7
--- /dev/null
+++ b/.github/logos/phpmyadmin.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/postgres-pgvector.svg b/.github/logos/postgres-pgvector.svg
new file mode 100644
index 0000000..9e24063
--- /dev/null
+++ b/.github/logos/postgres-pgvector.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/postgres-timescaledb.svg b/.github/logos/postgres-timescaledb.svg
new file mode 100644
index 0000000..9e24063
--- /dev/null
+++ b/.github/logos/postgres-timescaledb.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/rabbitmq.svg b/.github/logos/rabbitmq.svg
new file mode 100644
index 0000000..ed548d3
--- /dev/null
+++ b/.github/logos/rabbitmq.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/redisinsight.svg b/.github/logos/redisinsight.svg
new file mode 100644
index 0000000..6afed08
--- /dev/null
+++ b/.github/logos/redisinsight.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/selenium.svg b/.github/logos/selenium.svg
new file mode 100644
index 0000000..f6162dd
--- /dev/null
+++ b/.github/logos/selenium.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/soketi.svg b/.github/logos/soketi.svg
new file mode 100644
index 0000000..826e53c
--- /dev/null
+++ b/.github/logos/soketi.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/solr.svg b/.github/logos/solr.svg
new file mode 100644
index 0000000..6e77eb7
--- /dev/null
+++ b/.github/logos/solr.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/logos/spamassassin.svg b/.github/logos/spamassassin.svg
new file mode 100644
index 0000000..11ea145
--- /dev/null
+++ b/.github/logos/spamassassin.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/stripe-mock.svg b/.github/logos/stripe-mock.svg
new file mode 100644
index 0000000..c602790
--- /dev/null
+++ b/.github/logos/stripe-mock.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/typesense-dashboard.svg b/.github/logos/typesense-dashboard.svg
new file mode 100644
index 0000000..eea2015
--- /dev/null
+++ b/.github/logos/typesense-dashboard.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/typesense.svg b/.github/logos/typesense.svg
new file mode 100644
index 0000000..eaec98b
--- /dev/null
+++ b/.github/logos/typesense.svg
@@ -0,0 +1 @@
+
diff --git a/.github/logos/valkey.svg b/.github/logos/valkey.svg
new file mode 100644
index 0000000..d31eeb0
--- /dev/null
+++ b/.github/logos/valkey.svg
@@ -0,0 +1 @@
+
diff --git a/.github/scripts/readme_logos.py b/.github/scripts/readme_logos.py
new file mode 100644
index 0000000..b486825
--- /dev/null
+++ b/.github/scripts/readme_logos.py
@@ -0,0 +1,82 @@
+"""Render the README's service logos from the store's own marks.
+
+The marks under services/ are monochrome by contract, which GitHub would draw
+black and lose on a dark theme. This paints each one in its declared colour on
+a white tile, so the README reads the same in either theme. A service with no
+mark gets the lerd glyph its `icon` names, in its category's tint, which is how
+the dashboard draws it too.
+
+Run from the repo root after adding or changing a service:
+ python3 .github/scripts/readme_logos.py
+"""
+
+import json
+import pathlib
+import re
+import sys
+
+ROOT = pathlib.Path(__file__).resolve().parents[2]
+GLYPHS = ROOT / ".github" / "glyphs"
+OUT = ROOT / ".github" / "logos"
+
+# The -600 tones of lerd's per-category tints (presetCategories.ts).
+CATEGORY_TINT = {
+ "databases": "#4f46e5",
+ "cache": "#d97706",
+ "messaging": "#7c3aed",
+ "search": "#0284c7",
+ "mail": "#e11d48",
+ "admin": "#059669",
+ "storage": "#0891b2",
+ "testing": "#c026d3",
+ "other": "#6b7280",
+}
+
+TILE = """\
+\
+{body}
+"""
+
+
+def split(svg: str, source: str) -> tuple[str, str]:
+ root = re.match(r'\s*]*viewBox="([^"]+)"[^>]*>(.*)\s*$', svg, re.S)
+ if not root:
+ sys.exit(f"{source} has no root")
+ return root.group(1), root.group(2)
+
+
+def services() -> list[dict]:
+ # Newer schemas list presets older binaries cannot read, so the README
+ # takes every index there is and lets a later one win a name.
+ merged = {}
+ for index in [ROOT / "services" / "index.json", *sorted(ROOT.glob("schema/*/services/index.json"))]:
+ for entry in json.loads(index.read_text())["services"]:
+ merged[entry["name"]] = entry
+ return list(merged.values())
+
+
+def render(entry: dict) -> str:
+ name = entry["name"]
+ mark = ROOT / "services" / f"{name}.svg"
+ if mark.exists():
+ viewbox, body = split(mark.read_text(), mark.name)
+ return TILE.format(inset=13, size=38, viewbox=viewbox, attrs=f' fill="{entry["color"]}"', body=body)
+
+ glyph = GLYPHS / f"{entry['icon']}.svg"
+ if not glyph.exists():
+ sys.exit(f"{name} has no mark and its icon {entry['icon']!r} has no .github/glyphs/{entry['icon']}.svg")
+ color = entry.get("color") or CATEGORY_TINT[entry["category"]]
+ viewbox, body = split(glyph.read_text(), glyph.name)
+ attrs = f' fill="none" stroke="{color}"'
+ return TILE.format(inset=12, size=40, viewbox=viewbox, attrs=attrs, body=body.replace("currentColor", color))
+
+
+def main() -> None:
+ OUT.mkdir(exist_ok=True)
+ for entry in services():
+ (OUT / f"{entry['name']}.svg").write_text(render(entry))
+ print(f"wrote .github/logos/{entry['name']}.svg")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/README.md b/README.md
index c814e84..6fa2524 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,35 @@
-# Lerd Service Store
-
-> The community-driven store of service presets that powers
-> [Lerd](https://lerd.sh) — add a database, cache, search engine,
-> or admin dashboard by editing YAML, no binary release required.
-
-[](https://lerd.sh)
-[](https://lerd.sh/usage/service-presets)
-[](LICENSE)
+
Lerd Service Store
+
+
+ The community-driven store of service presets that powers Lerd.
+ Add a database, cache, search engine, or admin dashboard by editing YAML, no binary release required.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Run `lerd service search`, pick something, `lerd service preset ` — and Lerd pulls the matching preset from this store, then knows exactly how to run it: which image and ports, how to wire your project's `.env`, which admin dashboard to embed, what it depends on, and which config files to mount for auto-login. Everything is a single YAML file. Add a service here and every Lerd install can use it within 24 hours, with no new Lerd release and no Go code.
@@ -30,56 +53,58 @@ All of it is data. None of it ships in the binary.
**Databases**
-| Service | What it is |
-|---------|------------|
-| `clickhouse` | ClickHouse column-oriented analytics database, with its own SQL console |
-| `mariadb` | MariaDB, the MySQL-compatible server (multi-version) |
-| `mongo` | MongoDB document database |
-| `postgres-pgvector` | PostgreSQL + pgvector for vector search (multi-version) |
-| `postgres-timescaledb` | PostgreSQL + TimescaleDB for time-series (multi-version) |
+| | Service | What it is |
+|:-:|---|---|
+| | `clickhouse` | ClickHouse column-oriented analytics database, with its own SQL console |
+| | `mariadb` | MariaDB, the MySQL-compatible server (multi-version) |
+| | `mongo` | MongoDB document database |
+| | `postgres-pgvector` | PostgreSQL + pgvector for vector search (multi-version) |
+| | `postgres-timescaledb` | PostgreSQL + TimescaleDB for time-series (multi-version) |
**Admin dashboards** — open inside the Lerd UI, auto-logged-in
-| Service | For |
-|---------|-----|
-| `phpmyadmin` | every installed MySQL / MariaDB service |
-| `pgadmin` | every installed PostgreSQL service |
-| `mongo-express` | MongoDB |
-| `redisinsight` | Redis |
-| `elasticvue` | Elasticsearch |
-| `typesense-dashboard` | Typesense |
-| `opensearch-dashboards` | OpenSearch |
-| `adminer` | every installed MySQL / MariaDB / PostgreSQL / ClickHouse service |
+| | Service | For |
+|:-:|---|---|
+| | `adminer` | every installed MySQL / MariaDB / PostgreSQL / ClickHouse service |
+| | `elasticvue` | Elasticsearch |
+| | `kafka-ui` | Kafka |
+| | `mongo-express` | MongoDB |
+| | `opensearch-dashboards` | OpenSearch |
+| | `pgadmin` | every installed PostgreSQL service |
+| | `phpmyadmin` | every installed MySQL / MariaDB service |
+| | `redisinsight` | Redis |
+| | `typesense-dashboard` | Typesense |
**Search & analytics**
-| Service | What it is |
-|---------|------------|
-| `elasticsearch` | Elasticsearch single-node engine |
-| `opensearch` | OpenSearch single-node engine |
-| `typesense` | Typesense typo-tolerant search |
-| `solr` | Apache Solr, the search backend Drupal and TYPO3 expect |
+| | Service | What it is |
+|:-:|---|---|
+| | `elasticsearch` | Elasticsearch single-node engine |
+| | `opensearch` | OpenSearch single-node engine |
+| | `solr` | Apache Solr, the search backend Drupal and TYPO3 expect |
+| | `typesense` | Typesense typo-tolerant search |
**Cache, queues & messaging**
-| Service | What it is |
-|---------|------------|
-| `valkey` | Valkey, the Redis-compatible cache (coexists with Redis) |
-| `memcached` | Memcached in-memory cache |
-| `rabbitmq` | RabbitMQ broker with management UI |
-| `soketi` | Pusher-compatible WebSocket server |
-| `beanstalkd` | Beanstalkd work queue |
-| `mercure` | Mercure hub for server-sent events, the Symfony realtime transport |
+| | Service | What it is |
+|:-:|---|---|
+| | `beanstalkd` | Beanstalkd work queue |
+| | `kafka` | Apache Kafka event streaming broker |
+| | `memcached` | Memcached in-memory cache |
+| | `mercure` | Mercure hub for server-sent events, the Symfony realtime transport |
+| | `rabbitmq` | RabbitMQ broker with management UI |
+| | `soketi` | Pusher-compatible WebSocket server |
+| | `valkey` | Valkey, the Redis-compatible cache (coexists with Redis) |
**Testing & tooling**
-| Service | What it is |
-|---------|------------|
-| `selenium` | Headless Chromium with a noVNC viewer for browser tests |
-| `stripe-mock` | Stripe API mock server |
-| `gotenberg` | Gotenberg API for PDF and document conversion |
-| `localstack` | LocalStack, the AWS APIs run locally (SQS, SES, DynamoDB, Secrets Manager) |
-| `spamassassin` | SpamAssassin scoring backend for the Mailpit catcher |
+| | Service | What it is |
+|:-:|---|---|
+| | `gotenberg` | Gotenberg API for PDF and document conversion |
+| | `localstack` | LocalStack, the AWS APIs run locally (SQS, SES, DynamoDB, Secrets Manager) |
+| | `selenium` | Headless Chromium with a noVNC viewer for browser tests |
+| | `spamassassin` | SpamAssassin scoring backend for the Mailpit catcher |
+| | `stripe-mock` | Stripe API mock server |
Don't see what you need? [Add it](#contributing) — that's what this repo is for.
@@ -102,7 +127,7 @@ New services and version bumps are welcome — this store is only as good as the
1. Fork this repo
2. Add or update `services/.yaml`
-3. Optionally add the service's own mark as `services/.svg` (see below)
+3. Optionally add the service's own mark as `services/.svg` (see below), then run `python3 .github/scripts/readme_logos.py` to draw its tile for this README
4. Add or update the matching entry in `services/index.json` (name, description, and optionally family, dashboard, depends_on)
5. Open a pull request
@@ -208,6 +233,8 @@ A family is the other way round. Every member of one is that engine, so they all
carry its mark and its colour: pgvector and TimescaleDB are Postgres, and both
draw the Postgres elephant in Postgres blue rather than a mark of their own.
+The coloured tiles in this README are not the marks themselves. `.github/scripts/readme_logos.py` paints each mark in its declared colour on a white tile, so GitHub shows it in either theme, and a service without one gets the lerd glyph its `icon` names in its category's tint, from copies kept in `.github/glyphs/`. It writes the result to `.github/logos/`; run it again whenever a service, its mark or its colour changes.
+
See the [service presets documentation](https://lerd.sh/usage/service-presets) for the full schema reference and every available field.
## License