| Magazine index (30040), kind-0 **profiles**, NIP-65 **relay lists** (10002) | **MySQL**`event` table with stable `core_row_key`(filled by `app:prewarm` and on-demand fetches) |
| Published articles (30023/24) | **MySQL**`article` table (global rows) + `article_magazine` (which magazine tenant ingested/references each row) |
| Magazine index (30040), kind-0 **profiles**, NIP-65 **relay lists** (10002) | **MySQL**`event` table with stable `core_row_key`— magazine indices are prefixed with `magazine_slug`; profiles/relay lists are shared |
| Comment / reply / thread **UI** (fetched thread HTML, etc.) | **Filesystem cache** pool `cache.replies` (not the DB) |
| Generic Symfony `cache.app` | Other app caches; **not** used for long-term profile or magazine index storage |
@ -115,7 +115,7 @@ For a full **Nostr backfill** + one-shot prewarm, use **`make prewarm`** (or a h
@@ -115,7 +115,7 @@ For a full **Nostr backfill** + one-shot prewarm, use **`make prewarm`** (or a h
| What | File |
|------|------|
| Site title, `npub`, `d_tag`, **relays** (`default_relay`, `article_relays`, `profile_relays`), theme | `config/unfold.yaml` (imported as Symfony parameters) |
| Site title, `npub`, `d_tag`, **`magazine_slug`** (tenant id for shared MySQL), **relays** (`default_relay`, `article_relays`, `profile_relays`), theme | `config/unfold.yaml` (imported as Symfony parameters) |
| `MAGAZINE_PREWARM_PREFER_SLUGS` | `.env` / `.env.local` — optional comma-separated category slugs to prioritize in `app:prewarm` magazine phase (after the root). Use when the relay time budget would otherwise skip your updated category. |
| `DATABASE_URL`, `APP_SECRET`, `HTTP_PORT`, `MYSQL_*`, optional **`PREWARM_FLAGS`** (for the Docker `cron` service) | `.env` / `.env.local` (see `.env.dist`) |
| Cache pool definitions (`cache.replies`, `cache.drafts`, `cache.app`) | `config/packages/cache.yaml` |
@ -123,6 +123,17 @@ For a full **Nostr backfill** + one-shot prewarm, use **`make prewarm`** (or a h
@@ -123,6 +123,17 @@ For a full **Nostr backfill** + one-shot prewarm, use **`make prewarm`** (or a h
**Relays (short):** `default_relay` and `article_relays` drive article sync and many queries; `profile_relays` are used **first** for kind-0 / profile fetches, then the merged default + article set (see `NostrClient`).
### Shared MySQL (multiple magazines on one host)
Two deployments (e.g. Imwald + GitCitadel) can use **one MySQL** instead of separate `database_data` volumes:
1. Set a unique **`magazine_slug`** in each image’s `config/unfold.yaml` (e.g. `imwald`, `gitcitadel`).
2. Run **one** MySQL container (or external server). Point both stacks at the same **`DATABASE_URL`** (host port or Docker network alias).
3. **Nuke old volumes** and run migrations once: `docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction`.
4. Backfill each site separately (`articles:get`, `app:prewarm`) — each container tags rows with its own `magazine_slug`.
Articles and kind-0 profiles are stored once and shared; magazine indices, featured authors, admin users, and list/search/sitemap views are scoped per `magazine_slug`.
# Per-relay WebSocket I/O (seconds) in NostrClient; also default_socket_timeout during app:prewarm.
nostr_relay_request_timeout_sec:12
# Stable tenant id for shared MySQL (magazine indices, article visibility, featured authors, admin users).
# Lowercase alnum and hyphens only; must be unique per deployment on the same database.
magazine_slug:'imwald'
name:'Nostr, Curated Thoughtfully'
short_name:'Imwald Blog'
description:'A selection of my own Nostr long-form articles and articles from other authors, selected for the quality of their writing and the depth of their analysis.'
* Multi-tenant shared MySQL: magazine_slug scopes site data; article rows stay global with article_magazine links.
*/
final class Version20260528140000 extends AbstractMigration
{
public function getDescription(): string
{
return 'Shared DB multi-tenancy: article_magazine, magazine_slug on featured_author and app_user';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE article_magazine (magazine_slug VARCHAR(64) NOT NULL, article_id INT NOT NULL, INDEX IDX_ARTICLE_MAGAZINE_ARTICLE (article_id), PRIMARY KEY (magazine_slug, article_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');