|
|
5 days ago | |
|---|---|---|
| assets | 6 days ago | |
| bin | 1 year ago | |
| config | 5 days ago | |
| docker/cron | 5 days ago | |
| docs | 9 months ago | |
| frankenphp | 5 days ago | |
| migrations | 9 months ago | |
| public | 11 months ago | |
| publication/Newsroom | 9 months ago | |
| scripts | 5 days ago | |
| src | 5 days ago | |
| templates | 6 days ago | |
| tests | 11 months ago | |
| translations | 9 months ago | |
| .dockerignore | 6 days ago | |
| .editorconfig | 1 year ago | |
| .env.dist | 5 days ago | |
| .env.test | 1 year ago | |
| .gitattributes | 1 year ago | |
| .gitignore | 12 months ago | |
| Dockerfile | 6 days ago | |
| LICENSE | 1 year ago | |
| Makefile | 5 days ago | |
| README.md | 5 days ago | |
| compose.hub.yaml | 6 days ago | |
| compose.override.yaml | 5 days ago | |
| compose.prod.yaml | 9 months ago | |
| compose.yaml | 5 days ago | |
| composer.json | 5 days ago | |
| composer.lock | 5 days ago | |
| importmap.php | 6 days ago | |
| package.json | 1 year ago | |
| phpunit.xml.dist | 1 year ago | |
| symfony.lock | 9 months ago | |
README.md
Unfold: Imwald
A Symfony + FrankenPHP site that reads Nostr long-form articles (kind 30023) and related data from relays, stores articles in MySQL, and serves pages with Twig. Comments and profile metadata are cache-backed (not the full source of truth in the DB).
Requirements
| Requirement | Version / notes |
|---|---|
| PHP | ≥ 8.3.13 (see composer.json) |
| Docker | Optional; recommended for local dev and production images |
| Database | MySQL 8.0 (configurable) |
Local development (Docker)
-
Env: copy
.env.distto.envand adjust if needed (especiallyAPP_SECREToutside dev). -
Start stack
docker compose up -d -
App URL (default): http://127.0.0.1:9080
Port comes fromHTTP_PORTin.envandcompose.override.yaml(loopback only). -
First-time DB: migrations run on php container start when
migrations/contains PHP files (seefrankenphp/docker-entrypoint.sh).
| Service | Role |
|---|---|
php |
FrankenPHP + Caddy, Symfony app, console |
database |
MySQL; dev exposes 127.0.0.1:3307 → 3306 for local clients |
cron |
Runs full app:prewarm every 10 minutes; repo bind-mounted at /var/www/html (see docker/cron/) |
Backfill articles + warm caches (recommended)
To migrate, import articles from Nostr for a time window, then prewarm magazine indices, author metadata, and comment caches:
make prewarm
| Step (script order) | Command / effect |
|---|---|
| 1 | docker compose up -d --wait — starts php, database, and cron (the cron image runs a full app:prewarm on a 10 min schedule) |
| 2 | doctrine:migrations:migrate |
| 3 | articles:get -- '-2 month' 'now' — sync long-form into MySQL for that window |
| 4 | app:prewarm — magazine 30040, kind-0 profiles, comment cache (default --comments-max=20, newest by createdAt) |
make prewarm brings the stack (including cron) up so scheduled prewarm is active. Optional extra arguments for the cron-scheduled app:prewarm go in .env as PREWARM_FLAGS (same as you might pass to php bin/console app:prewarm …); Compose passes them into the cron container. Example: PREWARM_FLAGS="--metadata-limit=50 --no-magazine". Restart the cron service after changing PREWARM_FLAGS so the container reloads the env. Hub / compose.hub.yaml has no cron service; use a host timer or exec if you need the same there.
Console commands (overview)
| Command | Purpose |
|---|---|
articles:get <from> <to> |
Pull long-form articles from Nostr for the time range, persist to DB |
app:prewarm |
Magazine relay refresh + metadata cache + comment cache warm |
doctrine:migrations:migrate |
Apply SQL migrations |
user:elevate |
(If used) user elevation helper |
php bin/console list and … -h for full options.
app:prewarm (notable options)
| Option | Default | Meaning |
|---|---|---|
--no-magazine |
off | Skip magazine 30040 index |
--no-metadata |
off | Skip Nostr kind-0 / profile cache |
--no-comments |
off | Skip comment thread cache |
--metadata-limit |
0 (all authors) |
Cap distinct author pubkeys |
--metadata-batch |
50 |
Pubkeys per batched Nostr REQ |
--comments-max |
20 |
Newest N articles (by createdAt DESC); 0 = all (still bounded by budget) |
--comments-budget |
120 |
Max wall seconds for the comments phase |
--magazine-budget |
30 |
Max wall seconds for magazine refresh |
Prewarm clears the PHP CLI execution time limit for that run; relay work can be slow.
PREWARM_ON_START (optional)
| Variable | Set where | Effect |
|---|---|---|
PREWARM_ON_START=1 |
Compose environment on the php service (not only Symfony .env inside the container) |
After DB is up and migrations run, executes app:prewarm once on start. Does not run articles:get. |
For a full Nostr backfill + one-shot prewarm, use make prewarm (or a host cron / systemd timer) instead of relying on PREWARM_ON_START alone.
Configuration
| What | File |
|---|---|
Site title, npub, d_tag, relays (default_relay, article_relays, profile_relays), theme |
config/unfold.yaml (imported as Symfony parameters) |
DATABASE_URL, APP_SECRET, HTTP_PORT, MYSQL_*, optional PREWARM_FLAGS (for the Docker cron service) |
.env / .env.local (see .env.dist) |
Service wiring (e.g. cache, NostrClient args) |
config/services.yaml |
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).
Production / Hub image
| Topic | Notes |
|---|---|
compose.hub.yaml |
Runs a pulled image (default silberengel/unfold:latest), no local PHP app build. Override with UNFOLD_DOCKER_IMAGE. |
| HTTP publish | HTTP_PUBLISH in .env (default 9080 → container 80). Set TRUSTED_PROXIES behind a reverse proxy. |
| Secrets | Set APP_SECRET and DB credentials in real env; do not commit production secrets. |
File header in compose.hub.yaml lists pull, migrate, and optional build/push one-liners.
License
MIT — see LICENSE.
Project links (example)
Configurable under parameters.external_links in config/unfold.yaml (e.g. Unfold on GitHub, Decent Newsroom). Adjust for your deployment.