WP Composer has two primary runtime concerns:
- Build and serve a static Composer repository.
- Provide web/admin interfaces for package browsing and operations.
- Single binary (
wpcomposer) provides CLI commands and HTTP server. - SQLite (WAL mode) as the sole runtime data store.
- R2/CDN serves Composer metadata artifacts (
packages.json,p/,p2/,manifest.json). - Caddy reverse proxies app routes to the Go HTTP server.
- systemd manages the
serveprocess and periodic timers.
wpcomposer discover— discovers package slugs (config list or SVN).wpcomposer update— fetches and stores package metadata from WordPress.org.wpcomposer build— generates static Composer JSON artifacts.wpcomposer deploy— promotes a completed build, supports rollback/cleanup.wpcomposer pipeline— orchestrates discover → update → build → deploy.
- Immutable build directories under
storage/repository/builds/<build-id>/. - Atomic
currentsymlink points to the promoted build.
- Public package browser/detail pages via server-rendered Go templates + Tailwind.
- Admin panel at
/adminwith in-app auth (email/password + session).
cmd/wpcomposer/ CLI entrypoint (Cobra)
internal/
├── config/ env-first loading + optional YAML config
├── db/ SQLite connection, pragmas, Goose migrations
├── wporg/ WordPress.org API + SVN clients
├── packages/ package normalization/storage logic
├── repository/ artifact generation, hashing, integrity validation
├── deploy/ local promote/rollback/cleanup + R2 sync
├── telemetry/ event ingestion, dedupe, rollups
└── http/ stdlib router, handlers, templates, static assets
- Discovery creates/updates shell package records (
type,name,last_committed). - Update fetches full package payloads, normalizes versions, stores to
packages.versions_json. - Build generates:
packages.json(with absolutenotify-batchURL to app domain)- Provider files under
p/ - Composer v2 metadata files under
p2/ manifest.jsonwith build metrics and snapshot metadata
- Deploy promotes a complete build by switching the
currentsymlink and optionally syncing to R2. - R2/CDN serves static JSON directly; Caddy proxies dynamic routes to the Go app.
wpcomposer updatestamps updated rows withlast_sync_run_id.wpcomposer buildsnapshotsmax(last_sync_run_id)and only includes rows at or below that value.- This prevents mixed-state builds when updates are running concurrently.
storage/repository/
├── current -> builds/20260313-140000
└── builds/
├── 20260313-140000/
│ ├── packages.json
│ ├── manifest.json
│ ├── p/
│ └── p2/
└── 20260313-130000/
GET /— package browser with search/filter/sort/paginationGET /packages/{type}/{name}— package detailPOST /downloads— Composer notify-batch endpoint (install telemetry)GET /health— status + package totals + last build metadata
GET /admin— dashboardGET /admin/packages— package managementGET /admin/builds— build history/status- Admin-triggered sync/build/deploy actions
- Access: in-app auth/authorization required
Two deployment targets:
- R2/CDN (production) —
wpcomposer deploy --to-r2syncs the build to Cloudflare R2 with appropriateCache-Controlheaders. R2 custom domain + Cloudflare CDN serves the static files. Seedocs/r2-deployment.md. - Local (development) —
wpcomposer deployupdates thecurrentsymlink only.
Periodic tasks run via systemd timers or cron (no in-process scheduler required):
wpcomposer pipeline— every 5 minuteswpcomposer aggregate-installs— hourlywpcomposer cleanup-sessions— daily
Optional: wpcomposer serve --with-scheduler for in-process scheduling.