Asset CDN Management
Status: COMPLETE — all binary assets served from Cloudflare R2 CDN. Binary assets are gitignored. Only CSS, HTML slides, and Python files tracked in git. GTM symlink removed — all apps use CDN URLs directly.How to manage assets across Syntropy apps using Cloudflare R2 CDN.
Architecture
All Syntropy apps share a single R2 bucket with namespaced prefixes:https://pub-fb02de4e493b4e6a8c61de37de19575b.r2.dev
Controlled by env var ASSETS_CDN_BASE_URL. When empty, all paths serve
locally (no CDN). When set, asset_url() rewrites paths transparently.
Credentials
Already configured at the GitHub org level:| Secret | Where | Value |
|---|---|---|
CLOUDFLARE_API_TOKEN | GH Secrets (repo) | cfat_TlOn... |
CLOUDFLARE_ACCOUNT_ID | GH Secrets (repo) | 803cc0d1... |
ASSETS_CDN_BASE_URL | GH Variable + envs/ | https://pub-fb02...r2.dev |
Migration Steps (for a new app)
1. Add asset_url() resolver
Copy syntropy_journals/app/utils/asset_url.py into your app. The function:
- Returns paths unchanged when
ASSETS_CDN_BASE_URLis empty - Passes through external URLs (
http://,https://) - Maps brand files to
shared/brand/ - Maps icon paths to
shared/icons/ - Maps everything else to
{app_name}/prefix
gtm/ instead of journals/).
2. Add sync function to scripts/sync-assets.sh
The sync script already supports multiple apps. To add a new app:
case statement at the bottom to include your app.
3. Wrap asset paths
In every file that uses static asset paths:4. Add CI guardrail
Copyscripts/ci/check_asset_paths.py and adjust _SCAN_DIRS for your app’s
source directories. This validates all asset paths resolve to real files.
5. Set env var
AddASSETS_CDN_BASE_URL to your environment files and Railway/deployment config.
The CDN is opt-in: leave it empty for local dev, set it for test/prod.
6. Upload assets
CI Integration
The deploy workflow syncs assets before Railway deploy:Shared Assets
Theshared/ prefix contains assets used by multiple apps:
- Brand:
syntropy.svg,syntropy.png, favicons - Icons: sidebar, chat avatars, device icons, partner logos
sync_shared() in the sync script and sourced from
the Journals repo’s assets/ directory (canonical source).